queues: service level counts callers who weren't answered
ci / check (push) Successful in 1m4s
ci / images (push) Successful in 19s

withinSlaPercent was "answered within sla_seconds / answered", so a
queue where a caller timed out still showed 100%. It is now the usual
service level: answered within sla_seconds out of every caller the
queue is done with, leaving out those who hung up before sla_seconds
(nobody could have answered them in time). Timed out and turned away
count as missed; callers still waiting aren't counted. Written into
04; the page label says "Callers answered within N s".

Real calls: m8-t1 now shows 50% (1 answered in time, 1 timed out, the
early hang-up left out) and the milestone check 67% (2 of 3), and
asserts it. The check moves to queue 552 (*45552) so it doesn't clash
with m8-t1's 550 on a shared database.

Task: M8-T1
This commit is contained in:
iamdoubz committed 2026-10-19 12:13:39 -05:00
1 parent 088372bff8
commit 4742ed52ea
8 files changed
+28 -17

No files matched your search

+5 -1
View File
@@ -122,6 +122,10 @@ export async function queueStats(
number | null
>`round(avg(${queueCalls.waitSeconds}) filter (where ${answered}))::int`,
withinSla: sql<number>`count(*) filter (where ${answered} and ${queueCalls.waitSeconds} <= ${q.slaSeconds})::int`,
// Service level: answered within the target out of every caller the queue is done with,
// leaving out callers still waiting and those who hung up before the target (nobody could
// have answered them in time). Timed out and turned away count as missed.
slaBase: sql<number>`count(*) filter (where ${queueCalls.outcome} is not null and not (${queueCalls.outcome} = 'abandoned' and ${queueCalls.waitSeconds} <= ${q.slaSeconds}))::int`,
})
.from(queueCalls)
.where(
@@ -137,6 +141,6 @@ export async function queueStats(
abandoned: r!.abandoned,
timedOut: r!.timedOut,
avgWaitSeconds: r!.avgWait,
withinSlaPercent: r!.answered ? Math.round((100 * r!.withinSla) / r!.answered) : null,
withinSlaPercent: r!.slaBase ? Math.round((100 * r!.withinSla) / r!.slaBase) : null,
};
}
+3 -1
View File
@@ -240,7 +240,9 @@ describe.skipIf(!hasTestDb)('/api/queues', () => {
abandoned: 1,
timedOut: 2,
avgWaitSeconds: 24,
withinSlaPercent: 50, // 3 s yes, 45 s no (sla 20 s)
// sla 20 s: 3 s yes; 45 s, the timeout and the turned away no; the 4 s hang-up and the
// caller still waiting don't count
withinSlaPercent: 25,
});
const [taken] = await t.db.select().from(queueCalls).where(eq(queueCalls.uniqueid, '1.1'));
expect(taken).toMatchObject({
+1 -1
View File
@@ -241,7 +241,7 @@ function QueueDetail({ id }: { id: string }) {
['Timed out', s?.timedOut],
['Average wait', s && (s.avgWaitSeconds === null ? '—' : `${s.avgWaitSeconds} s`)],
[
`Answered within ${q.slaSeconds} s`,
`Callers answered within ${q.slaSeconds} s`,
s && (s.withinSlaPercent === null ? '—' : `${s.withinSlaPercent}%`),
],
] as const