diff --git a/README.md b/README.md index e935392..dbc91a6 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ We define a couple of different event types that are unified across Bits of Good "ObjectId": "id of the object (i.e. a specific button, tab, etc.) that was clicked", "UserId": "id of the user who clicked said event" } + "Created At": "Date the event was created at" } ``` - Visit Events: These are events that occur when a user visits a specific page in an application (i.e. a user visits `/app` or `/home`) @@ -31,8 +32,8 @@ We define a couple of different event types that are unified across Bits of Good "Event Properties": { "PageUrl": "URL of the page that was visited in the app", "UserId": "user who visited that page", - "Date": "date when they visited a page" - } + }, + "Created At": "Date the event was created at" } ``` - Input Events: These are events where a user inputs a specific piece of information into a input box @@ -44,7 +45,8 @@ We define a couple of different event types that are unified across Bits of Good "ObjectId": "id of the object (i.e. a specific text field) where the user inputted data", "UserId": "id of the user who inputted the text", "TextValue": "value of the text that was submitted into the text field" - } + }, + "Created At": "Date the event was created at" } ``` diff --git a/api/Dockerfile.dev b/api/Dockerfile.dev index 0780dad..c6bf7bc 100644 --- a/api/Dockerfile.dev +++ b/api/Dockerfile.dev @@ -12,4 +12,4 @@ RUN yarn install RUN chmod -R 777 node_modules/ -ENTRYPOINT ["yarn", "dev"] \ No newline at end of file +ENTRYPOINT ["sh", "-c", "rm -rf /tmp/tsx-0 && yarn dev"] \ No newline at end of file diff --git a/api/src/actions/click-event.ts b/api/src/actions/click-event.ts index ad70d2a..7e99bd9 100644 --- a/api/src/actions/click-event.ts +++ b/api/src/actions/click-event.ts @@ -12,7 +12,7 @@ export const createClickEvent = async (event: Partial) => { export const getClickEvents = async (date?: Date) => { await dbConnect(); let fromDate = date ?? new Date(Date.now() - 60 * 60 * 24 * 30 * 1000) - const events = await ClickEventModel.find({ date: { $gte: fromDate } }) + const events = await ClickEventModel.find({ createdAt: { $gte: fromDate } }) return events } @@ -22,7 +22,7 @@ export const paginatedGetClickEvents = async (afterDate: Date, afterID: String, if (project && project._id) { const events = await ClickEventModel.find( { - date: { $gte: afterDate }, + createdAt: { $gte: afterDate }, ...(afterID && { _id: { $gte: afterID } }), projectId: project._id }) diff --git a/api/src/actions/custom-event-type.ts b/api/src/actions/custom-event-type.ts new file mode 100644 index 0000000..7cba763 --- /dev/null +++ b/api/src/actions/custom-event-type.ts @@ -0,0 +1,45 @@ +import { CustomEventType } from "@/src/utils/types"; +import { dbConnect } from "@/src/utils/db-connect"; +import CustomEventTypeModel from "@/src/models/custom-event-type"; +import CustomEvent from "@/src/models/custom-event"; +import CustomGraphType from "@/src/models/custom-graph-type"; +import { Types } from "mongoose"; + + +export const findEventForProject = async (projectId: string | Types.ObjectId, category: string, subcategory: string) => { + await dbConnect(); + return await CustomEventTypeModel.findOne({ projectId, category, subcategory }) + +} +export const createCustomEventType = async (eventType: Partial) => { + await dbConnect(); + const createdEventType = await CustomEventTypeModel.create(eventType); + return createdEventType; +} + +export const getCustomEventTypesForProject = async (projectId: string) => { + await dbConnect(); + const eventTypes = await CustomEventTypeModel.find({ projectId }) + return eventTypes; +} +export const getCustomEventType = async (projectId: string, category: string, subcategory: string) => { + await dbConnect(); + const eventType = await CustomEventTypeModel.find({ projectId, category, subcategory }) + return eventType; +} +export const getCustomEventTypeID = async (projectId: string, category: string, subcategory: string) => { + await dbConnect(); + const eventType = await CustomEventTypeModel.findOne({ projectId, category, subcategory }) + return eventType?._id; +} +export const deleteCustomEventType = async (projectId: string, category: string, subcategory: string) => { + const deletedEventType = await CustomEventTypeModel.findOne({ projectId, category, subcategory }); + if (!deletedEventType) { + return; + } + let eventTypeId = deletedEventType._id + + await CustomEvent.deleteMany({ eventTypeId }) + await CustomGraphType.deleteMany({ eventTypeId }) + await CustomEventTypeModel.deleteOne({ projectId, category, subcategory }) +} \ No newline at end of file diff --git a/api/src/actions/custom-event.ts b/api/src/actions/custom-event.ts new file mode 100644 index 0000000..6ea7266 --- /dev/null +++ b/api/src/actions/custom-event.ts @@ -0,0 +1,34 @@ +import { CustomEventType } from "@/src/utils/types"; +import { dbConnect } from "@/src/utils/db-connect"; +import CustomEventTypeModel from "@/src/models/custom-event-type"; +import CustomEvent from "@/src/models/custom-event"; +import CustomGraphType from "@/src/models/custom-graph-type"; + + +export const createCustomEvent = async (projectId: string, eventTypeId: string, properties: object) => { + await dbConnect(); + let eventType = await CustomEventTypeModel.findOne({ _id: eventTypeId, projectId }) + + if (!eventType) { + return null; + } + let typeProperties = eventType.properties; + if (Object.keys(typeProperties).length === Object.keys(properties).length + && Object.keys(typeProperties).every(k => properties.hasOwnProperty(k))) { + return null; + } + const createdEvent = await CustomEvent.create({ projectId, eventTypeId, properties }); + return createdEvent; +} +//one function to get eventTypeId, then this paginated method +export const paginatedGetCustomEvents = async (eventTypeId: string, afterDate: Date, afterID: string, limit: number) => { + await dbConnect(); + const events = await CustomEvent.find( + { + createdAt: { $gte: afterDate }, + ...(afterID && { _id: { $gte: afterID } }), + eventTypeId + }) + .limit(limit); + return events +} \ No newline at end of file diff --git a/api/src/actions/custom-graph-type.ts b/api/src/actions/custom-graph-type.ts new file mode 100644 index 0000000..03d7ec0 --- /dev/null +++ b/api/src/actions/custom-graph-type.ts @@ -0,0 +1,35 @@ +import { dbConnect } from "@/src/utils/db-connect"; +import CustomGraphTypeModel from "@/src/models/custom-event-type"; +import { CustomGraphType, GraphTypes } from "@/src/utils/types"; +import CustomEventTypeModel from "@/src/models/custom-event-type"; + + +export const createCustomGraphType = async (newGraph: Partial) => { + await dbConnect(); + let eventTypeId = newGraph.eventTypeId + let eventType = await CustomEventTypeModel.findOne({ _id: eventTypeId }) + if (!eventType) { + return null; + } + let typeProperties = eventType.properties; + if (!typeProperties.includes(newGraph.xProperty as string) || !typeProperties.includes(newGraph.yProperty as string)) { + return null; + } + + if (!Object.values(GraphTypes).includes(newGraph.graphType as GraphTypes)) { + return null; + } + const createdGraphType = await CustomGraphTypeModel.create(newGraph); + return createdGraphType; +} + +export const getCustomGraphTypes = async (eventTypeId: string, projectId: string) => { + await dbConnect(); + const graphTypes = await CustomGraphTypeModel.find({ eventTypeId, projectId }) + return graphTypes +} +export const deleteCustomGraphType = async (_id: string) => { + await dbConnect(); + const deletedGraphType = await CustomGraphTypeModel.deleteOne({ _id }) + return deletedGraphType +} \ No newline at end of file diff --git a/api/src/actions/input-event.ts b/api/src/actions/input-event.ts index d2727c3..8a2afc6 100644 --- a/api/src/actions/input-event.ts +++ b/api/src/actions/input-event.ts @@ -12,7 +12,7 @@ export const createInputEvent = async (event: Partial) => { export const getInputEvents = async (date?: Date) => { await dbConnect(); const fromDate = date ?? new Date(Date.now() - 60 * 60 * 24 * 30 * 1000) - const events = await InputEventModel.find({ date: { $gte: fromDate } }) + const events = await InputEventModel.find({ createdAt: { $gte: fromDate } }) return events } @@ -22,7 +22,7 @@ export const paginatedGetInputEvents = async (afterDate: Date, afterID: String, if (project && project._id) { const events = await InputEventModel.find( { - date: { $gte: afterDate }, + createdAt: { $gte: afterDate }, ...(afterID && { _id: { $gte: afterID } }), projectId: project._id }) diff --git a/api/src/actions/project.ts b/api/src/actions/project.ts index 7857bdb..f548437 100644 --- a/api/src/actions/project.ts +++ b/api/src/actions/project.ts @@ -8,6 +8,11 @@ export const createProject = async (project: Partial) => { return createdProject } +export const getProjectIDByName = async (projectName: string) => { + await dbConnect(); + const project = await ProjectModel.findOne({ projectName }); + return project?._id +} export const getProjectByClientKey = async (clientApiKey: string): Promise => { await dbConnect(); return await ProjectModel.findOne({ clientApiKey }); diff --git a/api/src/actions/visit-event.ts b/api/src/actions/visit-event.ts index 9f232b9..8c70e6d 100644 --- a/api/src/actions/visit-event.ts +++ b/api/src/actions/visit-event.ts @@ -12,7 +12,7 @@ export const createVisitEvent = async (event: Partial) => { export const getVisitEvents = async (date?: Date) => { await dbConnect(); const fromDate = date ?? new Date(Date.now() - 60 * 60 * 24 * 30 * 1000) - const events = await VisitEventModel.find({ date: { $gte: fromDate } }) + const events = await VisitEventModel.find({ createdAt: { $gte: fromDate } }) return events } export const paginatedGetVisitEvents = async (afterDate: Date, afterID: string, limit: number, projectName: String) => { @@ -22,7 +22,7 @@ export const paginatedGetVisitEvents = async (afterDate: Date, afterID: string, const events = await VisitEventModel.find( { - date: { $gte: afterDate }, + createdAt: { $gte: afterDate }, ...(afterID && { _id: { $gte: afterID } }), projectId: project._id }) diff --git a/api/src/controllers/events/custom-event-type.ts b/api/src/controllers/events/custom-event-type.ts new file mode 100644 index 0000000..5ac6025 --- /dev/null +++ b/api/src/controllers/events/custom-event-type.ts @@ -0,0 +1,82 @@ +import { createCustomEventType, getCustomEventTypesForProject, deleteCustomEventType, findEventForProject } from "@/src/actions/custom-event-type"; +import { getProjectIDByName } from "@/src/actions/project"; +import { getProjectByServerKey } from "@/src/actions/project"; +import { relogRequestHandler } from "@/src/middleware/request-middleware"; +import APIWrapper from "@/src/utils/api-wrapper"; +import { CustomEventType } from "@/src/utils/types"; +import { Request } from "express"; + +const customEventTypeRoute = APIWrapper({ + POST: { + config: { + requireClientToken: false, + requireServerToken: true + }, + handler: async (req: Request) => { + const { category, subcategory, properties } = req.body; + if (!category || !subcategory) { + throw new Error("You must specify a category and subcategory to create a custom event type!") + } + const project = await getProjectByServerKey(req.headers.servertoken as string); + + if (!project) { + throw new Error("Project does not exist for client token") + } + const customEventType: Partial = { + category: category, + subcategory: subcategory, + properties: properties, + projectId: project._id, + } + + const preexistingEventType = await findEventForProject(project._id, category, subcategory) + if (preexistingEventType != null) { + throw new Error("A custom event type with the same category and subcategory already exists") + } + + const createdType = await createCustomEventType(customEventType); + return createdType; + }, + }, + GET: { + config: { + requireClientToken: false, + requireServerToken: false + }, + handler: async (req: Request) => { + const projectName = req.query.projectName + if (!projectName) { + throw new Error("You must specify a project to get custom event types!") + } + const id = await getProjectIDByName(projectName as string); + + if (!id) { + throw new Error("Project does not exist") + } + const types = await getCustomEventTypesForProject(id.toString()); + return types; + }, + }, + DELETE: { + config: { + requireClientToken: false, + requireServerToken: true + }, + handler: async (req: Request) => { + const { category, subcategory } = req.body + if (!category || !subcategory) { + throw new Error("You must specify a category and subcategory to delete custom event types!") + } + const project = await getProjectByServerKey(req.headers.servertoken as string); + + if (!project) { + throw new Error("Project does not exist") + } + await deleteCustomEventType(project._id.toString(), category, subcategory) + }, + }, + +}); + + +export const customEventType = relogRequestHandler(customEventTypeRoute); \ No newline at end of file diff --git a/api/src/controllers/events/custom-event.ts b/api/src/controllers/events/custom-event.ts new file mode 100644 index 0000000..ea1d32d --- /dev/null +++ b/api/src/controllers/events/custom-event.ts @@ -0,0 +1,69 @@ +import { createCustomEvent, paginatedGetCustomEvents } from "@/src/actions/custom-event"; +import { getCustomEventTypeID } from "@/src/actions/custom-event-type"; +import { getProjectIDByName } from "@/src/actions/project"; +import { getProjectByClientKey } from "@/src/actions/project"; +import { relogRequestHandler } from "@/src/middleware/request-middleware"; +import APIWrapper from "@/src/utils/api-wrapper"; +import { CustomEvent } from "@/src/utils/types"; +import { Request } from "express"; + +const customEventRoute = APIWrapper({ + POST: { + config: { + requireClientToken: true, + requireServerToken: false + }, + handler: async (req: Request) => { + const { eventTypeId, properties } = req.body; + if (!eventTypeId || !properties) { + throw new Error("You must specify a category and subcategory to create a custom event!") + } + const project = await getProjectByClientKey(req.headers.clienttoken as string); + + if (!project) { + throw new Error("Project does not exist for client token") + } + + const createdEvent = await createCustomEvent(project._id.toString(), eventTypeId, properties); + + if (!createdEvent) { + throw new Error("Failed to create custom event"); + } + return createdEvent; + }, + }, + GET: { + config: { + requireClientToken: false, + requireServerToken: false + }, + handler: async (req: Request) => { + const { projectName, category, subcategory } = req.query + if (!projectName) { + throw new Error("You must specify a project to get custom event types!") + } + const id = await getProjectIDByName(projectName as string); + + if (!id) { + throw new Error("Project does not exist") + } + const eventType = await getCustomEventTypeID(id.toString(), category as string, subcategory as string); + if (!eventType) { + throw new Error("Event type does not exist"); + } + + const { afterId } = req.query; + const limit = req.query.limit ?? 10 + const afterTime = req.query.afterTime ? new Date(req.query.afterTime as string) : new Date(Date.now() - 60 * 60 * 24 * 30 * 1000) + if (!projectName) { + throw new Error("You must specify a project name to create a project!") + } + let events = await paginatedGetCustomEvents(eventType.toString(), afterTime, afterId as string, parseInt(limit as string)); + return events; + }, + }, + +}); + + +export const customEvent = relogRequestHandler(customEventRoute); \ No newline at end of file diff --git a/api/src/controllers/events/index.ts b/api/src/controllers/events/index.ts index dfb33f4..dd27ca8 100644 --- a/api/src/controllers/events/index.ts +++ b/api/src/controllers/events/index.ts @@ -1,3 +1,5 @@ export { clickEvent } from '@/src/controllers/events/click-event'; export { inputEvent } from '@/src/controllers/events/input-event'; -export { visitEvent } from '@/src/controllers/events/visit-event'; \ No newline at end of file +export { visitEvent } from '@/src/controllers/events/visit-event'; +export { customEventType } from '@/src/controllers/events/custom-event-type'; +export { customEvent} from '@/src/controllers/events/custom-event'; \ No newline at end of file diff --git a/api/src/controllers/events/visit-event.ts b/api/src/controllers/events/visit-event.ts index 79eabc6..604f632 100644 --- a/api/src/controllers/events/visit-event.ts +++ b/api/src/controllers/events/visit-event.ts @@ -28,7 +28,6 @@ const visitEventRoute = APIWrapper({ eventProperties: { pageUrl, userId, - date } } diff --git a/api/src/controllers/graphs/custom-graph-type.ts b/api/src/controllers/graphs/custom-graph-type.ts new file mode 100644 index 0000000..dbb7330 --- /dev/null +++ b/api/src/controllers/graphs/custom-graph-type.ts @@ -0,0 +1,74 @@ +import { createCustomGraphType, getCustomGraphTypes, deleteCustomGraphType } from "@/src/actions/custom-graph-type"; +import { getProjectByClientKey } from "@/src/actions/project"; +import { relogRequestHandler } from "@/src/middleware/request-middleware"; +import APIWrapper from "@/src/utils/api-wrapper"; +import { CustomGraphType } from "@/src/utils/types"; +import { Request } from "express"; + +const customGraphTypeRoute = APIWrapper({ + POST: { + config: { + requireClientToken: false, + requireServerToken: true + }, + handler: async (req: Request) => { + const { eventTypeId, xProperty, yProperty, graphType } = req.body; + if (!eventTypeId || !xProperty || !yProperty || !graphType) { + throw new Error("You must specify an event type, x property, y property, and graph type to create a custom graph!") + } + const project = await getProjectByClientKey(req.headers.servertoken as string); + + if (!project) { + throw new Error("Project does not exist for client token") + } + const customGraphType: Partial = { + projectId: project._id, + eventTypeId, + xProperty, + yProperty, + graphType, + ...(req.body.caption !== undefined && { graphType: { ...graphType, caption: req.body.caption } }) + }; + + const createdGraphType = await createCustomGraphType(customGraphType); + + if (!createdGraphType) { + throw new Error("Failed to create a custom graph"); + } + return createdGraphType; + }, + }, + DELETE: { + config: { + requireClientToken: false, + requireServerToken: true + }, + handler: async (req: Request) => { + const { graphId } = req.body + if (!graphId) { + throw new Error("You must specify a graph to delete!") + } + + let deletedGraph = await deleteCustomGraphType(graphId); + return deletedGraph; + }, + }, + GET: { + config: { + requireClientToken: false, + requireServerToken: false + }, + handler: async (req: Request) => { + const { projectId, eventTypeId } = req.body + if (!projectId || !eventTypeId) { + throw new Error("You must specify a project and event type to get custom event types!") + } + let graphTypes = await getCustomGraphTypes(eventTypeId, projectId); + return graphTypes; + }, + }, + +}); + + +export const customGraphType = relogRequestHandler(customGraphTypeRoute); \ No newline at end of file diff --git a/api/src/controllers/graphs/index.ts b/api/src/controllers/graphs/index.ts new file mode 100644 index 0000000..28a6c46 --- /dev/null +++ b/api/src/controllers/graphs/index.ts @@ -0,0 +1 @@ +export { customGraphType } from '@/src/controllers/graphs/custom-graph-type'; \ No newline at end of file diff --git a/api/src/models/base-event.ts b/api/src/models/base-event.ts index 5e736f7..257cc08 100644 --- a/api/src/models/base-event.ts +++ b/api/src/models/base-event.ts @@ -16,10 +16,10 @@ export const BaseEventSchema = new mongoose.Schema({ required: true, ref: ProjectModel.modelName } -}); +}, { timestamps: true }); const BaseEventModel = (mongoose.models.BaseEvent as mongoose.Model) || mongoose.model("BaseEvent", BaseEventSchema); -export default BaseEventModel; \ No newline at end of file +export default BaseEventModel; diff --git a/api/src/models/custom-event-type.ts b/api/src/models/custom-event-type.ts new file mode 100644 index 0000000..2b947ce --- /dev/null +++ b/api/src/models/custom-event-type.ts @@ -0,0 +1,29 @@ +import mongoose, { Schema } from "mongoose"; +import { CustomEventType } from "@/src/utils/types"; +import ProjectModel from "@/src/models/project"; + +export const CustomEventTypeSchema = new mongoose.Schema({ + category: { + type: String, + required: true, + }, + subcategory: { + type: String, + required: true, + }, + properties: { + type: [String], + required: true + }, + projectId: { + type: Schema.Types.ObjectId, + required: true, + ref: ProjectModel.modelName + } +}); + +const CustomEventTypeModel = + (mongoose.models.CustomEventType as mongoose.Model) || + mongoose.model("CustomEventType", CustomEventTypeSchema); + +export default CustomEventTypeModel; \ No newline at end of file diff --git a/api/src/models/custom-event.ts b/api/src/models/custom-event.ts new file mode 100644 index 0000000..f9e1d1e --- /dev/null +++ b/api/src/models/custom-event.ts @@ -0,0 +1,31 @@ +import mongoose, { Schema } from "mongoose"; +import { CustomEvent } from "@/src/utils/types"; +import ProjectModel from "@/src/models/project"; +import CustomEventTypeModel from "@/src/models/custom-event-type"; + +export const CustomEventSchema = new mongoose.Schema({ + subcategory: { + type: String, + required: true, + }, + properties: { + type: Schema.Types.Mixed, + required: true + }, + projectId: { + type: Schema.Types.ObjectId, + required: true, + ref: ProjectModel.modelName + }, + eventTypeId: { + type: Schema.Types.ObjectId, + required: true, + ref: CustomEventTypeModel.modelName + } +}, { timestamps: true }); + +const CustomEventModel = + (mongoose.models.CustomEvent as mongoose.Model) || + mongoose.model("CustomEvent", CustomEventSchema); + +export default CustomEventModel; \ No newline at end of file diff --git a/api/src/models/custom-graph-type.ts b/api/src/models/custom-graph-type.ts new file mode 100644 index 0000000..7bd4408 --- /dev/null +++ b/api/src/models/custom-graph-type.ts @@ -0,0 +1,43 @@ +import mongoose, { Schema } from "mongoose"; +import { CustomGraphType } from "@/src/utils/types"; +import ProjectModel from "@/src/models/project"; +import CustomEventTypeModel from "@/src/models/custom-event-type"; + +export const CustomGraphTypeSchema = new mongoose.Schema({ + projectId: { + type: Schema.Types.ObjectId, + required: true, + ref: ProjectModel.modelName + }, + eventTypeId: { + type: Schema.Types.ObjectId, + required: true, + ref: CustomEventTypeModel.modelName + }, + graphTitle: { + type: String, + required: true + }, + xProperty: { + type: String, + required: true + }, + yProperty: { + type: String, + required: true + }, + graphType: { + type: String, + required: true + }, + caption: { + type: String, + required: false + }, +}); + +const CustomGraphTypeModel = + (mongoose.models.CustomGraphType as mongoose.Model) || + mongoose.model("CustomGraphType", CustomGraphTypeSchema); + +export default CustomGraphTypeModel; \ No newline at end of file diff --git a/api/src/models/visit-event.ts b/api/src/models/visit-event.ts index 2f5155e..2f78ed0 100644 --- a/api/src/models/visit-event.ts +++ b/api/src/models/visit-event.ts @@ -14,10 +14,6 @@ const VisitEventSchema = new Schema; +} + +export interface CustomGraphType { + _id: string | Types.ObjectId; + eventTypeId: string | Types.ObjectId; + projectId: string | Types.ObjectId; + graphTitle: string; + xProperty: string + yProperty: string + graphType: string + caption?: string +} export enum HttpMethod { GET = "GET", @@ -76,4 +104,10 @@ export enum EventSubcategories { CLICK = "Click", VISIT = "Visit", INPUT = "Input", +} + +export enum GraphTypes { + BAR = "bar", + SCATTER = "scatter", + LINE = "line" } \ No newline at end of file diff --git a/dashboard/scripts/data.py b/dashboard/scripts/data.py index 14fef5b..1ddd62d 100644 --- a/dashboard/scripts/data.py +++ b/dashboard/scripts/data.py @@ -1,19 +1,24 @@ import datetime import random +possible_dates = [ + (datetime.datetime.now() - datetime.timedelta(days=i)).isoformat() + for i in range(30) +] + class BaseEvent: def __init__(self, event_properties, category, subcategory) -> None: self.category = category self.subcategory = subcategory self.event_properties = event_properties + self.createdAt = random.choice(possible_dates) class VisitEventProperties: - def __init__(self, pageUrl, user_id, date) -> None: + def __init__(self, pageUrl, user_id) -> None: self.pageUrl = pageUrl self.user_id = user_id - self.date = date class VisitEvent(BaseEvent): @@ -74,10 +79,6 @@ def __init__(self, category, subcategory, xProperty, yProperty, graphType) -> No possible_inputs = [f"input_{i}" for i in range(0, 5)] possible_users = [f"user_{i}" for i in range(0, 100)] possible_text_values = [f"text_{i}" for i in range(0, 5)] -possible_dates = [ - (datetime.datetime.now() - datetime.timedelta(days=i)).isoformat() - for i in range(30) -] possible_custom_event_categories = [f"custom{i}" for i in range(0, 2)] possible_custom_event_subcategories = [f"custom{i}" for i in range(0, 2)] @@ -93,7 +94,6 @@ def __init__(self, category, subcategory, xProperty, yProperty, graphType) -> No visit_properties = VisitEventProperties( pageUrl=random.choice(possible_urls), user_id=random.choice(possible_users), - date=random.choice(possible_dates), ) visit_events.append(VisitEvent(visit_properties)) diff --git a/dashboard/widgets/visit_event_widgets.py b/dashboard/widgets/visit_event_widgets.py index 5806dc1..6449d23 100644 --- a/dashboard/widgets/visit_event_widgets.py +++ b/dashboard/widgets/visit_event_widgets.py @@ -14,12 +14,12 @@ def init_recent_events_table(st, visit_events): st.markdown(hide_table_row_index, unsafe_allow_html=True) - visit_sorted = sorted(visit_events, key=lambda event: event.event_properties.date) + visit_sorted = sorted(visit_events, key=lambda event: event.createdAt) data = [ { "Page URL": event.event_properties.pageUrl, "User ID": event.event_properties.user_id, - "Date": event.event_properties.date, + "Date": event.createdAt, } for event in visit_sorted ] @@ -90,14 +90,13 @@ def init_page_active_users_graph(st, visit_events): .properties(width=600, height=400) ) - st.altair_chart(chart, use_container_width=True) def init_visitors_over_time_graph(st, visit_events): st.write("**Visitors Over Time Graph**") data = { - "Date": [event.event_properties.date for event in visit_events], + "Date": [event.createdAt for event in visit_events], "pageUrl": [event.event_properties.pageUrl for event in visit_events], "Number of Visitors": [1 for _ in visit_events], } diff --git a/package/src/actions/visit-event.ts b/package/src/actions/visit-event.ts index 39f6813..2c70d7c 100644 --- a/package/src/actions/visit-event.ts +++ b/package/src/actions/visit-event.ts @@ -3,7 +3,7 @@ import { VisitEvent, HttpMethod } from "@/utils/types"; import { urls } from "@/utils/urls" const visitEventUrl = urls.apiBaseUrl + urls.events.visitEvent; -export const createVisitEvent = async (apiKey: string, pageUrl: string, userId: string, date: Date): Promise => { +export const createVisitEvent = async (apiKey: string, pageUrl: string, userId: string): Promise => { return externalRequest({ url: visitEventUrl, method: HttpMethod.POST, @@ -11,7 +11,6 @@ export const createVisitEvent = async (apiKey: string, pageUrl: string, userId: body: { pageUrl, userId, - date } }) } \ No newline at end of file diff --git a/package/src/lib/analytics-logger.ts b/package/src/lib/analytics-logger.ts index ba58559..63275ef 100644 --- a/package/src/lib/analytics-logger.ts +++ b/package/src/lib/analytics-logger.ts @@ -35,14 +35,14 @@ export default class AnalyticsLogger { } } - public async logVisitEvent(pageUrl: string, userId: string, date: Date): Promise { + public async logVisitEvent(pageUrl: string, userId: string): Promise { try { if (!this.clientApiKey) { throw new Error('Please authenticate first with your client api key using the authenticate method'); } // Client side API Key - const event = await createVisitEvent(this.clientApiKey, pageUrl, userId, date); + const event = await createVisitEvent(this.clientApiKey, pageUrl, userId); return event; } catch { @@ -51,7 +51,7 @@ export default class AnalyticsLogger { { clientApiKey: this.clientApiKey as string, pageUrl, - userId, date: date.toString() + userId } )) return null