adding event tracker

This commit is contained in:
Emily
2024-06-11 18:41:45 +02:00
parent da6984add9
commit 7460ad5ed4
7 changed files with 146 additions and 12 deletions

68
dashboard/pages/test.vue Normal file
View File

@@ -0,0 +1,68 @@
<script lang="ts" setup>
definePageMeta({ layout: 'dashboard' });
const activeProject = useActiveProject();
const eventNames = ref<string[]>([]);
const selectedEventName = ref<string>();
const metadataFields = ref<string[]>([]);
const selectedMetadataField = ref<string>();
const metadataFieldGrouped = ref<any[]>([]);
onMounted(async () => {
eventNames.value = await $fetch<string[]>(`/api/metrics/${activeProject.value?._id.toString()}/events/names`, signHeaders());
});
watch(selectedEventName, () => {
getMetadataFields();
});
async function getMetadataFields() {
metadataFields.value = await $fetch<string[]>(`/api/metrics/${activeProject.value?._id.toString()}/events/metadata_fields?name=${selectedEventName.value}`, signHeaders());
selectedMetadataField.value = undefined;
}
async function getMetadataFieldGrouped() {
metadataFieldGrouped.value = await $fetch<string[]>(`/api/metrics/${activeProject.value?._id.toString()}/events/metadata_field_group?name=${selectedEventName.value}&field=${selectedMetadataField.value}`, signHeaders());
}
</script>
<template>
<div class="w-full h-full p-8 flex">
<CardTitled title="Event tracker" sub="Track users from you marketing links to inner events" class="w-full">
<div class="p-8">
<div class="flex flex-col gap-2">
<USelectMenu searchable searchable-placeholder="Search an event..." class="w-full"
placeholder="Select an event" :options="eventNames" v-model="selectedEventName">
</USelectMenu>
<USelectMenu v-if="metadataFields.length > 0" searchable searchable-placeholder="Search a field..."
class="w-full" placeholder="Select a field" :options="metadataFields"
v-model="selectedMetadataField">
</USelectMenu>
</div>
<div @click="getMetadataFieldGrouped()"
class="bg-black/70 p-2 px-8 w-fit rounded-lg mt-4 hover:bg-black/40 cursor-pointer">
Find
</div>
<div>
{{ metadataFieldGrouped }}
</div>
</div>
</CardTitled>
</div>
</template>

View File

@@ -0,0 +1,31 @@
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
import { EventModel } from "@schema/metrics/EventSchema";
import { EVENT_METADATA_FIELDS_EXPIRE_TIME, Redis } from "~/server/services/CacheService";
export default defineEventHandler(async event => {
const project_id = getRequestProjectId(event);
if (!project_id) return;
const user = getRequestUser(event);
const project = await getUserProjectFromId(project_id, user);
if (!project) return;
const { name: eventName, field } = getQuery(event);
if (!eventName || !field) return [];
const aggregation = [
{ $match: { project_id: project._id, name: eventName } },
{ $group: { _id: `$metadata.${field}`, count: { $sum: 1 } } }
]
const metadataGrouped = await EventModel.aggregate(aggregation);
console.log(metadataGrouped);
return metadataGrouped;
});

View File

@@ -0,0 +1,33 @@
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
import { EventModel } from "@schema/metrics/EventSchema";
import { EVENT_METADATA_FIELDS_EXPIRE_TIME, Redis } from "~/server/services/CacheService";
export default defineEventHandler(async event => {
const project_id = getRequestProjectId(event);
if (!project_id) return;
const user = getRequestUser(event);
const project = await getUserProjectFromId(project_id, user);
if (!project) return;
const { name: eventName } = getQuery(event);
if (!eventName) return [];
const fields: string[] = await Redis.useCache({ key: `metadata_fields:${project_id}:${eventName}`, exp: EVENT_METADATA_FIELDS_EXPIRE_TIME }, async () => {
const eventsWithName = await EventModel.find({ project_id, name: eventName }, { metadata: 1 }, { limit: 10, sort: { created_at: -1 } });
const allMetadata = eventsWithName.map(e => e.metadata);
const allFields = new Set<string>();
for (const metadata of allMetadata) {
const keys = Object.keys(metadata || {});
keys.forEach(key => allFields.add(key));
}
return Array.from(allFields.values());
});
return fields;
});

View File

@@ -14,18 +14,9 @@ export default defineEventHandler(async event => {
const project = await getUserProjectFromId(project_id, user);
if (!project) return;
const names: string[] = await Redis.useCache({
key: `counts:${project_id}:event_names`,
exp: EVENT_NAMES_EXPIRE_TIME
}, async () => {
const namesAggregation = await EventModel.aggregate([
{ $match: { project_id: project._id } },
{ $group: { _id: "$name" } }
]);
const names: string[] = await Redis.useCache({ key: `event_names:${project_id}`, exp: EVENT_NAMES_EXPIRE_TIME }, async () => {
const namesAggregation = await EventModel.aggregate([{ $match: { project_id: project._id } }, { $group: { _id: "$name" } }]);
return namesAggregation.map(e => e._id);
});
return names;

View File

@@ -0,0 +1,9 @@
import { UserSettingsModel } from "@schema/UserSettings";
import { AuthContext } from "~/server/middleware/01-authorization";
export default defineEventHandler(async event => {
const userData: AuthContext = getRequestUser(event) as any;
if (!userData.logged) return;
const userSettings = await UserSettingsModel.findOne({ user_id: userData.id }, { max_projects: 1 });
return userSettings?.max_projects || 3;
});

View File

@@ -12,6 +12,8 @@ export const COUNTS_SESSIONS_EXPIRE_TIME = 60 * 3;
export const EVENT_NAMES_EXPIRE_TIME = 60;
export const EVENT_METADATA_FIELDS_EXPIRE_TIME = 120;
export class Redis {

View File

@@ -11,7 +11,7 @@ export type TEvent = {
const EventSchema = new Schema<TEvent>({
project_id: { type: Types.ObjectId, index: 1 },
name: { type: String, required: true },
name: { type: String, required: true, index: 1 },
metadata: Schema.Types.Mixed,
session: { type: String },
flowHash: { type: String },