mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 07:48:37 +01:00
[NOT READY] start change aggregation timeline
This commit is contained in:
@@ -139,7 +139,6 @@ const { snapshotDuration } = useSnapshot();
|
|||||||
const selectLabels: { label: string, value: Slice }[] = [
|
const selectLabels: { label: string, value: Slice }[] = [
|
||||||
{ label: 'Hour', value: 'hour' },
|
{ label: 'Hour', value: 'hour' },
|
||||||
{ label: 'Day', value: 'day' },
|
{ label: 'Day', value: 'day' },
|
||||||
// { label: 'Week', value: 'week' },
|
|
||||||
{ label: 'Month', value: 'month' },
|
{ label: 'Month', value: 'month' },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -159,7 +158,11 @@ const allDatesFull = ref<string[]>([]);
|
|||||||
|
|
||||||
function transformResponse(input: { _id: string, count: number }[]) {
|
function transformResponse(input: { _id: string, count: number }[]) {
|
||||||
const data = input.map(e => e.count);
|
const data = input.map(e => e.count);
|
||||||
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, selectedSlice.value));
|
|
||||||
|
console.log('RESPONSE', input);
|
||||||
|
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, new Date().getTimezoneOffset(), selectedSlice.value));
|
||||||
|
console.log('LABELS', input);
|
||||||
|
|
||||||
if (input.length > 0) allDatesFull.value = input.map(e => e._id.toString());
|
if (input.length > 0) allDatesFull.value = input.map(e => e._id.toString());
|
||||||
return { data, labels }
|
return { data, labels }
|
||||||
}
|
}
|
||||||
@@ -223,9 +226,6 @@ function onDataReady() {
|
|||||||
const maxChartY = Math.max(...visitsData.data.value.data, ...sessionsData.data.value.data);
|
const maxChartY = Math.max(...visitsData.data.value.data, ...sessionsData.data.value.data);
|
||||||
const maxEventSize = Math.max(...eventsData.data.value.data)
|
const maxEventSize = Math.max(...eventsData.data.value.data)
|
||||||
|
|
||||||
|
|
||||||
const currentDateTime = Date.now();
|
|
||||||
|
|
||||||
chartData.value.datasets[0].data = visitsData.data.value.data;
|
chartData.value.datasets[0].data = visitsData.data.value.data;
|
||||||
chartData.value.datasets[1].data = sessionsData.data.value.data;
|
chartData.value.datasets[1].data = sessionsData.data.value.data;
|
||||||
chartData.value.datasets[2].data = eventsData.data.value.data.map(e => {
|
chartData.value.datasets[2].data = eventsData.data.value.data.map(e => {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const { safeSnapshotDates } = useSnapshot()
|
|||||||
|
|
||||||
function transformResponse(input: { _id: string, count: number }[]) {
|
function transformResponse(input: { _id: string, count: number }[]) {
|
||||||
const data = input.map(e => e.count);
|
const data = input.map(e => e.count);
|
||||||
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, props.slice));
|
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, new Date().getTimezoneOffset(), props.slice));
|
||||||
return { data, labels }
|
return { data, labels }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function transformResponse(input: { _id: string, count: number }[]) {
|
|||||||
|
|
||||||
const data = input.map(e => e.count || 0);
|
const data = input.map(e => e.count || 0);
|
||||||
|
|
||||||
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, chartSlice.value));
|
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, new Date().getTimezoneOffset(), chartSlice.value));
|
||||||
|
|
||||||
const pool = [...input.map(e => e.count || 0)];
|
const pool = [...input.map(e => e.count || 0)];
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const { safeSnapshotDates } = useSnapshot()
|
|||||||
|
|
||||||
function transformResponse(input: { _id: string, count: number }[]) {
|
function transformResponse(input: { _id: string, count: number }[]) {
|
||||||
const data = input.map(e => e.count);
|
const data = input.map(e => e.count);
|
||||||
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, props.slice));
|
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, new Date().getTimezoneOffset(), props.slice));
|
||||||
return { data, labels }
|
return { data, labels }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,16 +44,16 @@ const selfhosted = useSelfhosted();
|
|||||||
<BannerLimitsInfo v-if="!selfhosted" :key="refreshKey"></BannerLimitsInfo>
|
<BannerLimitsInfo v-if="!selfhosted" :key="refreshKey"></BannerLimitsInfo>
|
||||||
<BannerOffer v-if="!selfhosted" :key="refreshKey"></BannerOffer>
|
<BannerOffer v-if="!selfhosted" :key="refreshKey"></BannerOffer>
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
<div>
|
<div>
|
||||||
<DashboardTopSection :key="refreshKey"></DashboardTopSection>
|
<DashboardTopSection :key="refreshKey"></DashboardTopSection>
|
||||||
<DashboardTopCards :key="refreshKey"></DashboardTopCards>
|
<DashboardTopCards :key="refreshKey"></DashboardTopCards>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full">
|
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full">
|
||||||
<DashboardActionableChart :key="refreshKey"></DashboardActionableChart>
|
<DashboardActionableChart :key="refreshKey"></DashboardActionableChart>
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
<div class="flex w-full justify-center mt-6 px-6">
|
<div class="flex w-full justify-center mt-6 px-6">
|
||||||
<div class="flex w-full gap-6 flex-col xl:flex-row">
|
<div class="flex w-full gap-6 flex-col xl:flex-row">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
@@ -85,7 +85,7 @@ const selfhosted = useSelfhosted();
|
|||||||
<BarCardOperatingSystems :key="refreshKey"></BarCardOperatingSystems>
|
<BarCardOperatingSystems :key="refreshKey"></BarCardOperatingSystems>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -13,17 +13,12 @@ export default defineEventHandler(async event => {
|
|||||||
const cacheExp = 60;
|
const cacheExp = 60;
|
||||||
|
|
||||||
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
||||||
|
|
||||||
const timelineData = await executeTimelineAggregation({
|
const timelineData = await executeTimelineAggregation({
|
||||||
projectId: project_id,
|
projectId: project_id,
|
||||||
model: EventModel,
|
model: EventModel,
|
||||||
from, to, slice,
|
from, to, slice,
|
||||||
});
|
});
|
||||||
|
return timelineData;
|
||||||
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
|
||||||
|
|
||||||
return timelineFilledMerged;
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ export default defineEventHandler(async event => {
|
|||||||
model: SessionModel,
|
model: SessionModel,
|
||||||
from, to, slice,
|
from, to, slice,
|
||||||
});
|
});
|
||||||
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
return timelineData;
|
||||||
return timelineFilledMerged;
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,12 @@ export default defineEventHandler(async event => {
|
|||||||
const cacheExp = 60;
|
const cacheExp = 60;
|
||||||
|
|
||||||
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
||||||
|
|
||||||
const timelineData = await executeTimelineAggregation({
|
const timelineData = await executeTimelineAggregation({
|
||||||
projectId: project_id,
|
projectId: project_id,
|
||||||
model: VisitModel,
|
model: VisitModel,
|
||||||
from, to, slice,
|
from, to, slice
|
||||||
});
|
});
|
||||||
|
return timelineData;
|
||||||
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
|
||||||
return timelineFilledMerged;
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export type TimelineAggregationOptions = {
|
|||||||
from: string | number,
|
from: string | number,
|
||||||
to: string | number,
|
to: string | number,
|
||||||
slice: Slice,
|
slice: Slice,
|
||||||
|
dateOffset?: number,
|
||||||
debug?: boolean
|
debug?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +28,9 @@ export async function executeAdvancedTimelineAggregation<T = {}>(options: Advanc
|
|||||||
options.customProjection = options.customProjection || {};
|
options.customProjection = options.customProjection || {};
|
||||||
options.customIdGroup = options.customIdGroup || {};
|
options.customIdGroup = options.customIdGroup || {};
|
||||||
|
|
||||||
const { group, sort } = DateService.getQueryDateRange(options.slice);
|
const { dateFromParts, granularity } = DateService.getGranularityData(options.slice, '$tmpDate');
|
||||||
|
|
||||||
if (!sort) throw Error('Slice is probably not correct');
|
if (!dateFromParts) throw Error('Slice is probably not correct');
|
||||||
|
|
||||||
|
|
||||||
const [sliceValid, errorOrDays] = checkSliceValidity(options.from, options.to, options.slice);
|
const [sliceValid, errorOrDays] = checkSliceValidity(options.from, options.to, options.slice);
|
||||||
@@ -40,31 +41,60 @@ export async function executeAdvancedTimelineAggregation<T = {}>(options: Advanc
|
|||||||
{
|
{
|
||||||
$match: {
|
$match: {
|
||||||
project_id: options.projectId,
|
project_id: options.projectId,
|
||||||
created_at: { $gte: new Date(options.from), $lte: new Date(options.to) },
|
created_at: {
|
||||||
|
$gte: new Date(options.from),
|
||||||
|
$lte: new Date(options.to)
|
||||||
|
},
|
||||||
...options.customMatch
|
...options.customMatch
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
$addFields: {
|
||||||
|
tmpDate: {
|
||||||
|
$dateSubtract: {
|
||||||
|
startDate: "$created_at",
|
||||||
|
unit: "minute",
|
||||||
|
amount: options.dateOffset || -60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$addFields: { isoDate: { $dateFromParts: dateFromParts } }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
$group: {
|
$group: {
|
||||||
_id: { ...group, ...options.customIdGroup },
|
_id: { isoDate: "$isoDate", ...options.customIdGroup },
|
||||||
count: { $sum: 1 },
|
count: { $sum: 1 },
|
||||||
firstDate: { $first: '$created_at' },
|
|
||||||
...options.customGroup
|
...options.customGroup
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ $sort: { firstDate: 1 } },
|
{
|
||||||
|
$sort: { "_id.isoDate": 1 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$densify: {
|
||||||
|
field: "_id.isoDate",
|
||||||
|
range: {
|
||||||
|
step: 1,
|
||||||
|
unit: granularity,
|
||||||
|
bounds: "full"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$addFields: { count: { $ifNull: ["$count", 0] }, }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
$project: {
|
$project: {
|
||||||
_id: "$firstDate",
|
_id: '$_id.isoDate',
|
||||||
count: "$count",
|
count: '$count',
|
||||||
...options.customProjection
|
...options.customProjection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
] as any;
|
] as any;
|
||||||
|
|
||||||
if (options.debug === true) {
|
if (options.debug === true) {
|
||||||
console.log('---------- SORT ----------')
|
|
||||||
console.log(JSON.stringify(sort, null, 2));
|
|
||||||
console.log('---------- AGGREAGATION ----------')
|
console.log('---------- AGGREAGATION ----------')
|
||||||
console.log(JSON.stringify(aggregation, null, 2));
|
console.log(JSON.stringify(aggregation, null, 2));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,13 @@ class DateService {
|
|||||||
|
|
||||||
public slicesData = slicesData;
|
public slicesData = slicesData;
|
||||||
|
|
||||||
getChartLabelFromISO(iso: string, locale: string, slice: Slice) {
|
getChartLabelFromISO(iso: string, offset: number, slice: Slice) {
|
||||||
if (slice === 'hour') return fns.format(iso, 'HH:mm');
|
const date = new Date(new Date(iso).getTime() - offset * 1000 * 60);
|
||||||
if (slice === 'day') return fns.format(iso, 'dd/MM');
|
if (slice === 'hour') return fns.format(date, 'HH:mm');
|
||||||
if (slice === 'week') return fns.format(iso, 'dd/MM');
|
if (slice === 'day') return fns.format(date, 'dd/MM');
|
||||||
if (slice === 'month') return fns.format(iso, 'MM MMMM');
|
if (slice === 'week') return fns.format(date, 'dd/MM');
|
||||||
if (slice === 'year') return fns.format(iso, 'YYYY');
|
if (slice === 'month') return fns.format(date, 'MM MMMM');
|
||||||
|
if (slice === 'year') return fns.format(date, 'YYYY');
|
||||||
return iso;
|
return iso;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,47 +72,27 @@ class DateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getQueryDateRange(slice: Slice) {
|
getGranularityData(slice: Slice, dateField: string) {
|
||||||
|
|
||||||
const group: Record<string, any> = {}
|
const dateFromParts: Record<string, any> = {};
|
||||||
const sort: Record<string, any> = {}
|
let granularity = '';
|
||||||
|
|
||||||
switch (slice) {
|
switch (slice) {
|
||||||
case 'hour':
|
case 'hour':
|
||||||
group.hour = { $hour: '$created_at' }
|
dateFromParts.hour = { $hour: { date: dateField } }
|
||||||
|
granularity = 'hour';
|
||||||
case 'day':
|
case 'day':
|
||||||
group.day = { $dayOfMonth: '$created_at' }
|
dateFromParts.day = { $dayOfMonth: { date: dateField } }
|
||||||
case 'week':
|
granularity = 'day';
|
||||||
group.week = { $isoWeek: '$created_at' }
|
|
||||||
case 'month':
|
case 'month':
|
||||||
group.month = { $month: '$created_at' }
|
dateFromParts.month = { $month: { date: dateField } }
|
||||||
|
granularity = 'month';
|
||||||
case 'year':
|
case 'year':
|
||||||
group.year = { $year: '$created_at' }
|
dateFromParts.year = { $year: { date: dateField } }
|
||||||
|
granularity = 'year';
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (slice) {
|
return { dateFromParts, granularity }
|
||||||
case 'year':
|
|
||||||
sort['_id.year'] = 1;
|
|
||||||
break;
|
|
||||||
case 'month':
|
|
||||||
sort['_id.year'] = 1;
|
|
||||||
sort['_id.month'] = 1;
|
|
||||||
break;
|
|
||||||
case 'week':
|
|
||||||
case 'day':
|
|
||||||
sort['_id.year'] = 1;
|
|
||||||
sort['_id.month'] = 1;
|
|
||||||
sort['_id.day'] = 1;
|
|
||||||
break;
|
|
||||||
case 'hour':
|
|
||||||
sort['_id.year'] = 1;
|
|
||||||
sort['_id.month'] = 1;
|
|
||||||
sort['_id.day'] = 1;
|
|
||||||
sort['_id.hour'] = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { group, sort }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user