SPB Git forge

spb/cancerindex

Public
37commits 1branches 0releases
2.9 MBsize
maindefault branch
10 days agolast push
TypeScript 97.2% SQL 1.5% CSS 0.6% JavaScript 0.5%
2.6 KB · 50 lines typescript
Raw Blame History
1import { sql } from 'drizzle-orm';2import type { Database } from '@cancerindex/database';3import type { SourceRef } from './envelope.js';45/**6 * Load source references for the envelope. Accepts CI-SOURCE ids and/or connector slugs (ranking7 * snapshots store slugs for count metrics and ids for epidemiology scopes).8 */9export async function loadSources(db: Database, refs: Iterable<string | null | undefined>): Promise<SourceRef[]> {10  const ids = new Set<string>();11  const slugs = new Set<string>();12  for (const r of refs) {13    if (!r) continue;14    if (r.startsWith('CI-SOURCE-')) ids.add(r);15    else slugs.add(r);16  }17  if (ids.size === 0 && slugs.size === 0) return [];18  const rows = await db.execute<{ id: string; slug: string; name: string; license: string | null; attribution: string | null; homepage: string | null }>(sql`19    SELECT id, slug, name, license, attribution, homepage FROM sources20    WHERE id = ANY(${sql.param([...ids])}::text[]) OR slug = ANY(${sql.param([...slugs])}::text[])21    ORDER BY slug`);22  return rows.map((r) => ({ id: r.id, slug: r.slug, name: r.name, license: r.license, attribution: r.attribution, url: r.homepage }));23}2425/** Collect a column from rows (null-safe). */26export function pluck<T extends Record<string, unknown>>(rows: readonly T[], ...keys: Array<keyof T>): string[] {27  const out: string[] = [];28  for (const r of rows) for (const k of keys) if (typeof r[k] === 'string') out.push(r[k] as string);29  return out;30}3132/** Sources that contributed codes, aliases or hierarchy edges to a set of cancers. */33export async function cancerStructuralSourceIds(db: Database, cancerIds: string[]): Promise<string[]> {34  if (cancerIds.length === 0) return [];35  const rows = await db.execute<{ source_id: string }>(sql`36    SELECT DISTINCT source_id FROM (37      SELECT source_id FROM cancer_codes WHERE cancer_id = ANY(${sql.param(cancerIds)}::text[]) AND source_id IS NOT NULL38      UNION SELECT source_id FROM cancer_aliases WHERE cancer_id = ANY(${sql.param(cancerIds)}::text[]) AND source_id IS NOT NULL39      UNION SELECT source_id FROM cancer_hierarchy WHERE (child_id = ANY(${sql.param(cancerIds)}::text[]) OR parent_id = ANY(${sql.param(cancerIds)}::text[])) AND source_id IS NOT NULL40    ) s`);41  return rows.map((r) => r.source_id);42}4344/** Most recent successful ingest finish — used for the dataRelease label. Cached by the caller. */45export async function latestDataAsOf(db: Database): Promise<Date | undefined> {46  const rows = await db.execute<{ t: string | null }>(sql`SELECT max(finished_at)::text AS t FROM ingest_runs WHERE status IN ('succeeded','partial')`);47  const t = rows[0]?.t;48  return t ? new Date(t) : undefined;49}50