diff --git a/packages/reservation-platform/src/utils/analytics/forecast.ts b/packages/reservation-platform/src/utils/analytics/forecast.ts index e5ebb98..05e5576 100644 --- a/packages/reservation-platform/src/utils/analytics/forecast.ts +++ b/packages/reservation-platform/src/utils/analytics/forecast.ts @@ -3,21 +3,34 @@ import type { InferResultType } from "@/utils/drizzle"; type UsagePerDay = { day: number; // 0 (Sunday) to 6 (Saturday) usageMinutes: number; - dataPoints: number; }; -function aggregateUsageByDay(jobs: InferResultType<"printJobs">[]): UsagePerDay[] { +function aggregateUsageByDay(jobs: InferResultType<"printJobs">[]): { + usageData: UsagePerDay[]; + earliestDate: Date; + latestDate: Date; +} { const usagePerDayMap = new Map(); - const usagePerDayDatapointsMap = new Map(); + + let earliestDate: Date | null = null; + let latestDate: Date | null = null; for (const job of jobs) { let remainingDuration = job.durationInMinutes; const currentStart = new Date(job.startAt); + // Update earliest and latest dates + if (!earliestDate || currentStart < earliestDate) { + earliestDate = new Date(currentStart); + } + const jobEnd = new Date(currentStart); + jobEnd.setMinutes(jobEnd.getMinutes() + job.durationInMinutes); + if (!latestDate || jobEnd > latestDate) { + latestDate = new Date(jobEnd); + } + while (remainingDuration > 0) { const day = currentStart.getDay(); - const dataPoints = usagePerDayDatapointsMap.get(day) || 0; - usagePerDayDatapointsMap.set(day, dataPoints + 1); // Calculate minutes remaining in the current day const minutesRemainingInDay = (24 - currentStart.getHours()) * 60 - currentStart.getMinutes(); @@ -37,17 +50,43 @@ function aggregateUsageByDay(jobs: InferResultType<"printJobs">[]): UsagePerDay[ const usageData: UsagePerDay[] = Array.from({ length: 7 }, (_, day) => ({ day, usageMinutes: usagePerDayMap.get(day) || 0, - dataPoints: usagePerDayDatapointsMap.get(day) || 0, })); - return usageData; + if (earliestDate === null) { + earliestDate = new Date(); + } + + if (latestDate === null) { + latestDate = new Date(); + } + + return { usageData, earliestDate: earliestDate, latestDate: latestDate }; +} + +function countWeekdays(startDate: Date, endDate: Date): number[] { + const countPerDay = Array(7).fill(0); + const currentDate = new Date(startDate); + currentDate.setHours(0, 0, 0, 0); // Ensure starting at midnight + endDate.setHours(0, 0, 0, 0); // Ensure ending at midnight + + while (currentDate <= endDate) { + const day = currentDate.getDay(); + countPerDay[day]++; + currentDate.setDate(currentDate.getDate() + 1); + } + return countPerDay; } export function forecastPrinterUsage(jobs: InferResultType<"printJobs">[]): number[] { - const usageData = aggregateUsageByDay(jobs); + const { usageData, earliestDate, latestDate } = aggregateUsageByDay(jobs); + + // Count the number of times each weekday occurs in the data period + const weekdaysCount = countWeekdays(earliestDate, latestDate); + const forecasts: number[] = []; for (const data of usageData) { - let usagePrediction = data.usageMinutes / data.dataPoints; + const dayCount = weekdaysCount[data.day]; + let usagePrediction = data.usageMinutes / dayCount; if (Number.isNaN(usagePrediction)) { usagePrediction = 0; } diff --git a/packages/reservation-platform/src/utils/analytics/volume.ts b/packages/reservation-platform/src/utils/analytics/volume.ts index 05c3eaf..a42c4e3 100644 --- a/packages/reservation-platform/src/utils/analytics/volume.ts +++ b/packages/reservation-platform/src/utils/analytics/volume.ts @@ -17,10 +17,13 @@ interface PrintVolumes { export function calculatePrintVolumes(pJobs: InferSelectModel[]): PrintVolumes { const now = new Date(); - // Define time ranges + // Define time ranges with week starting on Monday const timeRanges = { today: { start: startOfDay(now), end: endOfDay(now) }, - thisWeek: { start: startOfWeek(now), end: endOfWeek(now) }, + thisWeek: { + start: startOfWeek(now, { weekStartsOn: 1 }), + end: endOfWeek(now, { weekStartsOn: 1 }), + }, thisMonth: { start: startOfMonth(now), end: endOfMonth(now) }, };