import { sql, eq, and, ne } from "drizzle-orm"; import { db } from "@/lib/db/client"; import { sessions, sessionDevices, sessionEvents, devices } from "@/lib/db/schema"; export async function getSessionsSummary() { const [totals] = await db .select({ count: sql`count(*)`, totalDurationMs: sql`coalesce(sum(${sessions.durationMs}), 0)`, avgDurationMs: sql`coalesce(avg(${sessions.durationMs}), 0)`, // How many times sessions have been replayed - replaying itself creates no // session of its own, it just bumps the source session's playCount. totalReplays: sql`coalesce(sum(${sessions.playCount}), 0)`, }) .from(sessions) .where(eq(sessions.status, "completed")); const durationPerDevice = await db .select({ deviceId: devices.id, displayName: devices.displayName, bleName: devices.bleName, totalActiveMs: sql`coalesce(sum(coalesce(${sessionDevices.disconnectedAt}, ${sessionDevices.connectedAt}) - ${sessionDevices.connectedAt}), 0)`, }) .from(sessionDevices) .innerJoin(devices, eq(sessionDevices.deviceId, devices.id)) .groupBy(devices.id); return { ...totals, durationPerDevice }; } export async function getSessionTimeline(sessionId: number, bucketMs = 1000) { // Reuse the same expression object (not a `sql`bucket`` alias reference) // in groupBy/orderBy - drizzle doesn't emit a literal `AS bucket` that // SQLite's GROUP BY/ORDER BY could resolve a bare "bucket" identifier // against, so referencing it by name causes "no such column: bucket". const bucket = sql`(${sessionEvents.tsMs} / ${bucketMs}) * ${bucketMs}`; return db .select({ bucket, sessionDeviceId: sessionEvents.sessionDeviceId, slotLabel: sessionDevices.slotLabel, avgValue: sql`avg(${sessionEvents.value})`, maxValue: sql`max(${sessionEvents.value})`, }) .from(sessionEvents) .innerJoin(sessionDevices, eq(sessionEvents.sessionDeviceId, sessionDevices.id)) .where(and(eq(sessionEvents.sessionId, sessionId), ne(sessionEvents.commandType, "stop"))) .groupBy(bucket, sessionEvents.sessionDeviceId) .orderBy(bucket); } export async function getDeviceUsageStats() { return db .select({ deviceId: devices.id, displayName: devices.displayName, bleName: devices.bleName, sessionCount: sql`count(distinct ${sessionDevices.sessionId})`, totalActiveMs: sql`coalesce(sum(coalesce(${sessionDevices.disconnectedAt}, ${sessionDevices.connectedAt}) - ${sessionDevices.connectedAt}), 0)`, lastUsedAt: sql`max(${sessionDevices.connectedAt})`, }) .from(devices) .leftJoin(sessionDevices, eq(sessionDevices.deviceId, devices.id)) .groupBy(devices.id); } export async function getDeviceCommandCounts() { return db .select({ deviceId: devices.id, commandCount: sql`count(${sessionEvents.id})`, }) .from(devices) .leftJoin(sessionDevices, eq(sessionDevices.deviceId, devices.id)) .leftJoin(sessionEvents, eq(sessionEvents.sessionDeviceId, sessionDevices.id)) .groupBy(devices.id); }