feat: getVerifyResultStats helper + 5 Vitest-tests (lib/insights/verify-stats.ts) (#38)

Aggregeert verify_result counts (ALIGNED/PARTIAL/EMPTY/DIVERGENT) en top-5 EMPTY/DIVERGENT
jobs over de laatste N dagen voor de ingelogde gebruiker.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-01 16:41:22 +02:00 committed by GitHub
parent 2539361784
commit ddd9b8b39b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 204 additions and 0 deletions

View file

@ -0,0 +1,92 @@
import { prisma } from '@/lib/prisma'
export type VerifyResultKey = 'ALIGNED' | 'PARTIAL' | 'EMPTY' | 'DIVERGENT'
export interface TopJob {
jobId: string
taskId: string
taskTitle: string
productId: string
productName: string
finishedAt: Date
}
export interface VerifyResultStats {
counts: { result: VerifyResultKey; count: number }[]
topEmpty: TopJob[]
topDivergent: TopJob[]
}
const RESULT_ORDER: VerifyResultKey[] = ['ALIGNED', 'PARTIAL', 'EMPTY', 'DIVERGENT']
export async function getVerifyResultStats(
userId: string,
daysBack = 30,
): Promise<VerifyResultStats> {
const cutoff = new Date()
cutoff.setDate(cutoff.getDate() - daysBack)
const baseWhere = {
user_id: userId,
status: 'DONE' as const,
verify_result: { not: null as null },
finished_at: { gt: cutoff },
}
const [grouped, rawEmpty, rawDivergent] = await Promise.all([
prisma.claudeJob.groupBy({
by: ['verify_result'],
where: baseWhere,
_count: { _all: true },
}),
prisma.claudeJob.findMany({
where: { ...baseWhere, verify_result: 'EMPTY' },
orderBy: { finished_at: 'desc' },
take: 5,
select: {
id: true,
finished_at: true,
task: { select: { id: true, title: true } },
product: { select: { id: true, name: true } },
},
}),
prisma.claudeJob.findMany({
where: { ...baseWhere, verify_result: 'DIVERGENT' },
orderBy: { finished_at: 'desc' },
take: 5,
select: {
id: true,
finished_at: true,
task: { select: { id: true, title: true } },
product: { select: { id: true, name: true } },
},
}),
])
const countMap = new Map(
grouped
.filter(g => g.verify_result !== null)
.map(g => [g.verify_result as VerifyResultKey, g._count._all]),
)
const counts = RESULT_ORDER
.filter(r => countMap.has(r))
.map(r => ({ result: r, count: countMap.get(r)! }))
function toTopJob(j: { id: string; finished_at: Date | null; task: { id: string; title: string }; product: { id: string; name: string } }): TopJob {
return {
jobId: j.id,
taskId: j.task.id,
taskTitle: j.task.title,
productId: j.product.id,
productName: j.product.name,
finishedAt: j.finished_at!,
}
}
return {
counts,
topEmpty: rawEmpty.map(toTopJob),
topDivergent: rawDivergent.map(toTopJob),
}
}