SPB Git

spb/cancerindex

Public
4.8 KB · 104 lines typescript
1import 'server-only';2import { run, sql, safe } from '@/lib/db';34export interface DrugRow {5  id: string;6  slug: string;7  name: string;8  kind: string | null;9  ncit_code: string | null;10  chembl_id: string | null;11  civic_therapy_id: number | null;12  drugbank_id: string | null;13  pubchem_cid: string | null;14  unii: string | null;15  mechanism: string | null;16  target_gene_ids: string[];17  development_status: string | null;18  description: string | null;19  updated_at: Date;20  approval_count?: number;21  evidence_count?: number;22  trial_count?: number;23  aliases?: string[] | null;24}2526export async function getDrugBySlug(slug: string): Promise<DrugRow | null> {27  const rows = await safe(28    () =>29      run<DrugRow>(sql`30        SELECT d.*, (SELECT count(*) FROM drug_approvals a WHERE a.drug_id = d.id)::int AS approval_count,31               (SELECT count(*) FROM civic_evidence_items e WHERE d.id = ANY(e.therapy_ids))::int AS evidence_count,32               (SELECT count(DISTINCT ti.trial_id) FROM trial_interventions ti WHERE ti.drug_id = d.id)::int AS trial_count,33               (SELECT array_agg(DISTINCT a.alias ORDER BY a.alias) FROM drug_aliases a WHERE a.drug_id = d.id) AS aliases34        FROM drugs d WHERE d.slug = ${slug} LIMIT 1`),35    [] as DrugRow[],36  );37  return rows[0] ?? null;38}3940export async function listDrugs(opts: { q: string; kind: string; page: number; pageSize: number }): Promise<{ rows: DrugRow[]; total: number; kinds: Array<{ kind: string; n: number }> }> {41  const where = sql`${opts.kind ? sql`d.kind = ${opts.kind}` : sql`true`} AND ${opts.q ? sql`(d.name ILIKE ${'%' + opts.q + '%'} OR EXISTS (SELECT 1 FROM drug_aliases a WHERE a.drug_id = d.id AND a.alias ILIKE ${'%' + opts.q + '%'}))` : sql`true`}`;42  const total = await safe(() => run<{ n: string }>(sql`SELECT count(*) AS n FROM drugs d WHERE ${where}`), [{ n: '0' }]);43  const rows = await safe(44    () =>45      run<DrugRow>(sql`46        SELECT d.*, (SELECT count(*) FROM drug_approvals a WHERE a.drug_id = d.id)::int AS approval_count,47               (SELECT count(*) FROM civic_evidence_items e WHERE d.id = ANY(e.therapy_ids))::int AS evidence_count,48               (SELECT count(DISTINCT ti.trial_id) FROM trial_interventions ti WHERE ti.drug_id = d.id)::int AS trial_count49        FROM drugs d WHERE ${where} ORDER BY approval_count DESC, evidence_count DESC, d.name LIMIT ${opts.pageSize} OFFSET ${(opts.page - 1) * opts.pageSize}`),50    [] as DrugRow[],51  );52  const kinds = await safe(() => run<{ kind: string; n: string }>(sql`SELECT kind, count(*) AS n FROM drugs WHERE kind IS NOT NULL GROUP BY kind ORDER BY n DESC`), [] as Array<{ kind: string; n: string }>);53  return { rows, total: Number(total[0]?.n ?? 0), kinds: kinds.map((k) => ({ kind: k.kind, n: Number(k.n) })) };54}5556export interface ApprovalRow {57  id: number;58  drug_id: string;59  drug_slug: string;60  drug_name: string;61  cancer_id: string | null;62  cancer_slug: string | null;63  cancer_name: string | null;64  biomarker_ids: string[];65  tumor_agnostic: boolean;66  jurisdiction: string;67  authority: string;68  indication: string;69  line_of_therapy: string | null;70  disease_stage: string | null;71  approval_type: string | null;72  accelerated: boolean | null;73  conditional: boolean | null;74  approval_date: string | null;75  withdrawal_date: string | null;76  status: string;77  application_number: string | null;78  source_slug: string;79  source_name: string;80  provenance_id: number;81  updated_at: Date;82}8384const APPROVAL_SELECT = sql`85  SELECT a.*, d.slug AS drug_slug, d.name AS drug_name, c.slug AS cancer_slug, c.canonical_name AS cancer_name, s.slug AS source_slug, s.name AS source_name86  FROM drug_approvals a JOIN drugs d ON d.id = a.drug_id LEFT JOIN cancers c ON c.id = a.cancer_id JOIN sources s ON s.id = a.source_id`;8788export async function approvalsForDrug(drugId: string): Promise<ApprovalRow[]> {89  return safe(() => run<ApprovalRow>(sql`${APPROVAL_SELECT} WHERE a.drug_id = ${drugId} ORDER BY a.jurisdiction, a.approval_date DESC NULLS LAST`), [] as ApprovalRow[]);90}9192export async function approvalsForCancer(cancerIds: string[]): Promise<ApprovalRow[]> {93  if (cancerIds.length === 0) return [];94  return safe(() => run<ApprovalRow>(sql`${APPROVAL_SELECT} WHERE a.cancer_id IN (${sql.join(cancerIds.map((i) => sql`${i}`), sql`, `)}) OR a.tumor_agnostic ORDER BY d.name, a.jurisdiction, a.approval_date DESC NULLS LAST LIMIT 500`), [] as ApprovalRow[]);95}9697export async function drugSlugsForSitemap(offset: number, limit: number): Promise<Array<{ slug: string; updated_at: Date }>> {98  return safe(() => run<{ slug: string; updated_at: Date }>(sql`SELECT slug, updated_at FROM drugs ORDER BY id LIMIT ${limit} OFFSET ${offset}`), []);99}100export async function countDrugs(): Promise<number> {101  const r = await safe(() => run<{ n: string }>(sql`SELECT count(*) AS n FROM drugs`), [{ n: '0' }]);102  return Number(r[0]?.n ?? 0);103}104
Files · 33aae9564b414c377acdc2de5ccf643cd1f18285
Menu

Theme