+
{printerUtilization.map((data) => (
))}
@@ -81,11 +95,24 @@ export default async function AdminPage() {
+
+
+
+
+ ({
+ day: index,
+ usageMinutes,
+ }))}
+ />
+
diff --git a/packages/reservation-platform/src/utils/analytics/errors.ts b/packages/reservation-platform/src/utils/analytics/errors.ts
new file mode 100644
index 0000000..31e369a
--- /dev/null
+++ b/packages/reservation-platform/src/utils/analytics/errors.ts
@@ -0,0 +1,39 @@
+import type { InferResultType } from "@/utils/drizzle";
+
+export interface AbortReasonCount {
+ abortReason: string;
+ count: number;
+}
+
+/**
+ * Calculates the count of each unique abort reason for print jobs.
+ *
+ * @param pJobs - Array of print job objects.
+ * @returns An array of AbortReasonCount objects, each containing the abort reason and its count.
+ */
+export function calculateAbortReasonsCount(pJobs: InferResultType<"printJobs">[]): AbortReasonCount[] {
+ if (pJobs.length === 0) {
+ return []; // No jobs, no data.
+ }
+
+ // Filter aborted jobs and count each abort reason
+ const abortReasonsCount = pJobs
+ .filter((job) => job.aborted && job.abortReason) // Consider only aborted jobs with a reason
+ .reduce(
+ (acc, job) => {
+ const reason = job.abortReason || "Unbekannter Grund";
+ if (!acc[reason]) {
+ acc[reason] = 0;
+ }
+ acc[reason]++;
+ return acc;
+ },
+ {} as Record
,
+ );
+
+ // Convert the result to an array of AbortReasonCount objects
+ return Object.entries(abortReasonsCount).map(([abortReason, count]) => ({
+ abortReason,
+ count,
+ }));
+}
diff --git a/packages/reservation-platform/src/utils/analytics/forecast.ts b/packages/reservation-platform/src/utils/analytics/forecast.ts
new file mode 100644
index 0000000..0aa5268
--- /dev/null
+++ b/packages/reservation-platform/src/utils/analytics/forecast.ts
@@ -0,0 +1,59 @@
+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[] {
+ const usagePerDayMap = new Map();
+ const usagePerDayDatapointsMap = new Map();
+
+ for (const job of jobs) {
+ let remainingDuration = job.durationInMinutes;
+ const currentStart = new Date(job.startAt);
+
+ 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();
+ const minutesToAdd = Math.min(remainingDuration, minutesRemainingInDay);
+
+ // Update the usage for the current day
+ const usageMinutes = usagePerDayMap.get(day) || 0;
+ usagePerDayMap.set(day, usageMinutes + minutesToAdd);
+
+ // Update remaining duration and move to the next day
+ remainingDuration -= minutesToAdd;
+ currentStart.setDate(currentStart.getDate() + 1);
+ currentStart.setHours(0, 0, 0, 0); // Start at the beginning of the next day
+ }
+ }
+
+ const usageData: UsagePerDay[] = Array.from({ length: 7 }, (_, day) => ({
+ day,
+ usageMinutes: usagePerDayMap.get(day) || 0,
+ dataPoints: usagePerDayDatapointsMap.get(day) || 0,
+ }));
+
+ return usageData;
+}
+
+export function forecastPrinterUsage(jobs: InferResultType<"printJobs">[]): number[] {
+ const usageData = aggregateUsageByDay(jobs);
+ console.log(usageData);
+ const forecasts: number[] = [];
+ for (const data of usageData) {
+ let usagePrediction = data.usageMinutes / data.dataPoints;
+ if (Number.isNaN(usagePrediction)) {
+ usagePrediction = 0;
+ }
+ forecasts.push(Math.round(usagePrediction));
+ }
+
+ return forecasts;
+}
diff --git a/packages/reservation-platform/src/utils/analytics/idle-time.ts b/packages/reservation-platform/src/utils/analytics/idle-time.ts
deleted file mode 100644
index 27193e8..0000000
--- a/packages/reservation-platform/src/utils/analytics/idle-time.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import type { printJobs, printers } from "@/server/db/schema";
-import { endOfMonth, startOfMonth } from "date-fns";
-import type { InferSelectModel } from "drizzle-orm";
-
-interface PrinterIdleTime {
- printerId: string;
- printerName: string;
- averageIdleTime: number; // in minutes
-}
-
-/**
- * Calculates the average idle time for each printer within the current month.
- *
- * @param printJobs - Array of print job objects.
- * @param printers - Array of printer objects.
- * @returns An array of PrinterIdleTime objects with average idle times.
- */
-export function calculatePrinterIdleTime(
- pJobs: InferSelectModel[],
- p: InferSelectModel[],
-): PrinterIdleTime[] {
- const now = new Date();
- const startOfThisMonth = startOfMonth(now);
- const endOfThisMonth = endOfMonth(now);
- const totalMinutesInMonth = 60 * 70 * 4; // 60min * 70h (35*2) * 4 Weeks
-
- const usedTimePerPrinter: Record = pJobs.reduce(
- (acc, job) => {
- const jobStart = new Date(job.startAt);
- if (jobStart >= startOfThisMonth && jobStart <= endOfThisMonth) {
- acc[job.printerId] = (acc[job.printerId] || 0) + job.durationInMinutes;
- }
- return acc;
- },
- {} as Record,
- );
-
- return p.map((printer) => {
- const usedTime = usedTimePerPrinter[printer.id] || 0;
- const idleTime = totalMinutesInMonth - usedTime;
- const averageIdleTime = idleTime < 0 ? 0 : idleTime; // Ensure no negative idle time
-
- return {
- printerId: printer.id,
- printerName: printer.name,
- averageIdleTime,
- };
- });
-}
diff --git a/packages/reservation-platform/src/utils/analytics/utilization.ts b/packages/reservation-platform/src/utils/analytics/utilization.ts
index f053fce..dfc93b9 100644
--- a/packages/reservation-platform/src/utils/analytics/utilization.ts
+++ b/packages/reservation-platform/src/utils/analytics/utilization.ts
@@ -15,10 +15,12 @@ export function calculatePrinterUtilization(jobs: InferResultType<"printJobs", {
{} as Record,
);
- const totalTimeInMinutes = 60 * 70 * 4; // 60 Minutes * 70h * 4 Weeks
+ const totalTimeInMinutes = 60 * 35 * 3; // 60 Minutes * 35h * 3 Weeks
+ // 35h Woche, 3 mal in der Woche in TBA
const printerUtilizationPercentage = Object.keys(usedTimePerPrinter).map((printerId) => {
const usedTime = usedTimePerPrinter[printerId];
+
return {
printerId,
name: printers[printerId],