mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
123 lines
4 KiB
TypeScript
123 lines
4 KiB
TypeScript
import 'server-only'
|
|
|
|
import { sql } from '@payloadcms/db-postgres'
|
|
import type { Payload, PayloadRequest } from 'payload'
|
|
|
|
import { drizzleResultRows } from '@/utilities/drizzleBulk'
|
|
import {
|
|
getPostgresTransactionDatabase,
|
|
type PostgresTransactionDatabase,
|
|
} from '@/utilities/postgresTransactionLocks'
|
|
|
|
/**
|
|
* C155 backfill coverage: what the catalog actually HAS, per legislature.
|
|
* The run report says what one execution did; this says what is stored, so a
|
|
* resumed/partial run cannot be mistaken for the final state. Read-only, one
|
|
* aggregate query (`--coverage` in the import CLI).
|
|
*/
|
|
|
|
export type SpeechLegislatureCoverage = {
|
|
legislature: string | null
|
|
total: number
|
|
withExcerpt: number
|
|
withVideo: number
|
|
withSegments: number
|
|
withoutExcerpt: number
|
|
withoutSegments: number
|
|
fallbackYoutube: number
|
|
}
|
|
|
|
export type SpeechCoverage = {
|
|
byLegislature: SpeechLegislatureCoverage[]
|
|
totals: Omit<SpeechLegislatureCoverage, 'legislature'>
|
|
}
|
|
|
|
type CoverageRequest = { transactionID?: PayloadRequest['transactionID'] }
|
|
|
|
const COVERAGE_QUERY = sql`
|
|
SELECT
|
|
s."legislature" AS "legislature",
|
|
COUNT(*) AS "total",
|
|
COUNT(*) FILTER (WHERE s."audio_id" IS NOT NULL) AS "with_excerpt",
|
|
COUNT(*) FILTER (WHERE s."vod_playback_url" IS NOT NULL) AS "with_video",
|
|
COUNT(*) FILTER (WHERE EXISTS (
|
|
SELECT 1 FROM "speech_segment" seg WHERE seg."speech_id" = s."id"
|
|
)) AS "with_segments",
|
|
COUNT(*) FILTER (WHERE s."audio_id" IS NULL AND s."youtube_url" IS NOT NULL) AS "fallback_youtube"
|
|
FROM "speech" s
|
|
GROUP BY s."legislature"
|
|
ORDER BY s."legislature" NULLS LAST
|
|
`
|
|
|
|
const toCount = (value: unknown): number => {
|
|
const count = Number(value ?? 0)
|
|
return Number.isFinite(count) ? count : 0
|
|
}
|
|
|
|
const rowToCoverage = (row: Record<string, unknown>): SpeechLegislatureCoverage => {
|
|
const total = toCount(row.total)
|
|
const withExcerpt = toCount(row.with_excerpt)
|
|
const withSegments = toCount(row.with_segments)
|
|
return {
|
|
legislature:
|
|
row.legislature === null || row.legislature === undefined ? null : String(row.legislature),
|
|
total,
|
|
withExcerpt,
|
|
withVideo: toCount(row.with_video),
|
|
withSegments,
|
|
withoutExcerpt: total - withExcerpt,
|
|
withoutSegments: total - withSegments,
|
|
fallbackYoutube: toCount(row.fallback_youtube),
|
|
}
|
|
}
|
|
|
|
const sumCoverage = (
|
|
rows: SpeechLegislatureCoverage[],
|
|
): Omit<SpeechLegislatureCoverage, 'legislature'> =>
|
|
rows.reduce(
|
|
(totals, row) => ({
|
|
total: totals.total + row.total,
|
|
withExcerpt: totals.withExcerpt + row.withExcerpt,
|
|
withVideo: totals.withVideo + row.withVideo,
|
|
withSegments: totals.withSegments + row.withSegments,
|
|
withoutExcerpt: totals.withoutExcerpt + row.withoutExcerpt,
|
|
withoutSegments: totals.withoutSegments + row.withoutSegments,
|
|
fallbackYoutube: totals.fallbackYoutube + row.fallbackYoutube,
|
|
}),
|
|
{
|
|
total: 0,
|
|
withExcerpt: 0,
|
|
withVideo: 0,
|
|
withSegments: 0,
|
|
withoutExcerpt: 0,
|
|
withoutSegments: 0,
|
|
fallbackYoutube: 0,
|
|
},
|
|
)
|
|
|
|
/**
|
|
* Per-legislature coverage of the speech catalog. Pass a `req` with an active
|
|
* transaction (int tests) to run the aggregate on that transaction's session;
|
|
* the CLI calls it without one and reads the pooled connection.
|
|
*/
|
|
export const getSpeechCoverage = async (
|
|
payload: Payload,
|
|
req?: CoverageRequest,
|
|
): Promise<SpeechCoverage> => {
|
|
if (payload.db.name !== 'postgres') {
|
|
throw new Error('A cobertura do acervo exige o adaptador PostgreSQL.')
|
|
}
|
|
|
|
let database: PostgresTransactionDatabase | undefined
|
|
if (req?.transactionID) {
|
|
database = await getPostgresTransactionDatabase(payload, req)
|
|
} else {
|
|
database = (payload.db as unknown as { drizzle?: PostgresTransactionDatabase }).drizzle
|
|
}
|
|
if (!database || typeof database.execute !== 'function') {
|
|
throw new Error('A sessão PostgreSQL da cobertura do acervo não está disponível.')
|
|
}
|
|
|
|
const rows = drizzleResultRows(await database.execute(COVERAGE_QUERY)).map(rowToCoverage)
|
|
return { byLegislature: rows, totals: sumCoverage(rows) }
|
|
}
|