import { Injectable } from '@nestjs/common'; import { Prisma } from '../../../generated/prisma'; import { PrismaService } from '../../prisma/prisma.service'; export interface VoiceCaptureStats { byStatus: Record; byError: Record; tokens: { input: number; output: number }; byModel: Record; } interface CountRow { key: string; count: number; } interface TokenRow { input: bigint | number; output: bigint | number; } function toRecord(rows: readonly CountRow[]): Record { return Object.fromEntries(rows.map((row) => [row.key, Number(row.count)])); } /** * Counters of the last seven days for the system area: per status, per error * code, per model and the token sums. Aggregates only - no user, no capture * id, no content. The window runs on the database clock. */ @Injectable() export class VoiceCapturesStatsService { constructor(private readonly prisma: PrismaService) {} async stats(): Promise { const since = Prisma.sql`"createdAt" > (now() AT TIME ZONE 'UTC') - interval '7 days'`; const [byStatus, byError, byModel, tokens] = await Promise.all([ this.prisma.$queryRaw(Prisma.sql` SELECT "status"::text AS "key", count(*)::int AS "count" FROM "VoiceCapture" WHERE ${since} GROUP BY "status"`), this.prisma.$queryRaw(Prisma.sql` SELECT "lastErrorCode" AS "key", count(*)::int AS "count" FROM "VoiceCapture" WHERE ${since} AND "lastErrorCode" IS NOT NULL GROUP BY "lastErrorCode"`), this.prisma.$queryRaw(Prisma.sql` SELECT "model" AS "key", count(*)::int AS "count" FROM "VoiceCapture" WHERE ${since} AND "model" IS NOT NULL GROUP BY "model"`), this.prisma.$queryRaw(Prisma.sql` SELECT COALESCE(sum("inputTokens"), 0) AS "input", COALESCE(sum("outputTokens"), 0) AS "output" FROM "VoiceCapture" WHERE ${since}`), ]); return { byStatus: toRecord(byStatus), byError: toRecord(byError), tokens: { input: Number(tokens[0]?.input ?? 0), output: Number(tokens[0]?.output ?? 0), }, byModel: toRecord(byModel), }; } }