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%

Split research-gap routes, trial-sites stub, unit labels for new metrics

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed 13 days ago (Sep 11, 2026) parent 7a72991

8 changed files +56 −18

modified apps/api/src/app.ts +2 −0
@@ -30,6 +30,7 @@ import { graphRoutes } from './routes/graph.js';
30 30 import { epidemiologyRoutes } from './routes/epidemiology.js';
31 31 import { approvalRoutes } from './routes/approvals.js';
32 32 import { intelligenceRoutes } from './routes/intelligence.js';
33 +import { researchGapRoutes } from './routes/research-gap.js';
33 34
34 35 export interface BuildOptions {
35 36 db?: Database;
@@ -164,6 +165,7 @@ export async function buildApp(opts: BuildOptions = {}): Promise<FastifyInstance
164 165 await v1.register(approvalRoutes);
165 166 await v1.register(graphRoutes);
166 167 await v1.register(intelligenceRoutes);
168 + await v1.register(researchGapRoutes);
167 169 await v1.register(adminRoutes, { prefix: '/admin' });
168 170 },
169 171 { prefix: '/v1' },
modified apps/api/src/routes/intelligence.ts +5 −13
@@ -1,18 +1,10 @@
1 1 import type { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
2 2
3 3 /**
4 − * Intelligence routes (SPEC §10-11, §34): `GET /trials/intelligence` (per-cancer trial metrics),
5 − * `GET /trials/sites` (country/city aggregates for the map), `GET /research-gap` (components and
6 − * ratios per scope). Filled by the Trial Intelligence and Research Gap work packages
7 − * (each adds its own routes to this file; keep route groups in separate functions).
4 + * Trial-intelligence routes (SPEC §10-11): `GET /trials/intelligence` (per-cancer trial metrics
5 + * with formula version and inputs), `GET /trials/sites` (country / city aggregates for the map).
6 + * Filled by the Trial Intelligence and Trial Map work packages.
8 7 */
9 −export const intelligenceRoutes: FastifyPluginAsyncZod = async (app) => {
10 − await app.register(trialIntelligenceRoutes);
11 − await app.register(researchGapRoutes);
8 +export const intelligenceRoutes: FastifyPluginAsyncZod = async (_app) => {
9 + /* routes added by the trial-intelligence / trial-map work packages */
12 10 };
13 −
14 −/** Filled by the Trial Intelligence work package. */
15 −export const trialIntelligenceRoutes: FastifyPluginAsyncZod = async (_app) => {};
16 −
17 −/** Filled by the Research Gap work package. */
18 −export const researchGapRoutes: FastifyPluginAsyncZod = async (_app) => {};
added apps/api/src/routes/research-gap.ts +10 −0
@@ -0,0 +1,10 @@
1 +import type { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
2 +
3 +/**
4 + * Research Gap routes (SPEC §34, §113): `GET /research-gap` → components (deaths, trials,
5 + * publications, shares, log-ratios) per cancer for one burden scope; `GET /research-gap/scopes`.
6 + * Filled by the Research Gap work package.
7 + */
8 +export const researchGapRoutes: FastifyPluginAsyncZod = async (_app) => {
9 + /* routes added by the research-gap work package */
10 +};
modified apps/web/src/lib/format.ts +12 −0
@@ -40,6 +40,12 @@ export function fmtValue(value: number | string | null | undefined, unit: string
40 40 return fmtPct(v, 1);
41 41 case 'percentile_points':
42 42 return `${v > 0 ? '+' : ''}${fmtNum(v, 1)}`;
43 + case 'log2_ratio':
44 + return `${v > 0 ? '+' : ''}${fmtNum(v, 2)}`;
45 + case 'per_1000_deaths':
46 + return fmtNum(v, 1);
47 + case 'index':
48 + return fmtNum(v, 3);
43 49 default:
44 50 return Math.abs(v) >= 1000 ? fmtInt(v) : fmtNum(v, 2);
45 51 }
@@ -57,6 +63,12 @@ export function unitLabel(unit: string | null | undefined): string {
57 63 return '%';
58 64 case 'percentile_points':
59 65 return 'percentile points';
66 + case 'per_1000_deaths':
67 + return 'per 1,000 deaths';
68 + case 'log2_ratio':
69 + return 'log₂ ratio';
70 + case 'index':
71 + return 'index (0–1)';
60 72 default:
61 73 return unit ?? '';
62 74 }
modified packages/ranking/src/index.ts +1 −0
@@ -6,3 +6,4 @@ export * from './intelligence.js';
6 6 export * from './trial-intelligence.js';
7 7 export * from './drug-pipeline.js';
8 8 export * from './research-gap.js';
9 +export * from './trial-sites.js';
modified packages/ranking/src/intelligence.ts +8 −1
@@ -1,5 +1,6 @@
1 1 import type { Database } from '@cancerindex/database';
2 2 import { computeTrialIntelligence } from './trial-intelligence.js';
3 +import { computeTrialSiteCounts } from './trial-sites.js';
3 4 import { computeDrugPipeline } from './drug-pipeline.js';
4 5 import { computeResearchGap } from './research-gap.js';
5 6
@@ -23,11 +24,17 @@ export async function computeIntelligence(db: Database, log: (msg: string, extra
23 24 try {
24 25 const r = await computeTrialIntelligence(db);
25 26 out.trialIntelligenceRows = r.rows;
26 − out.trialSiteCountryRows = r.siteCountryRows;
27 27 log('trial intelligence computed', { ...r });
28 28 } catch (e) {
29 29 errors.push(`trial-intelligence: ${(e as Error).message}`);
30 30 }
31 + try {
32 + const r = await computeTrialSiteCounts(db);
33 + out.trialSiteCountryRows = r.rows;
34 + log('trial site country counts computed', { ...r });
35 + } catch (e) {
36 + errors.push(`trial-sites: ${(e as Error).message}`);
37 + }
31 38 try {
32 39 const r = await computeDrugPipeline(db);
33 40 out.drugPipelineRows = r.rows;
modified packages/ranking/src/trial-intelligence.ts +2 −4
@@ -1,17 +1,15 @@
1 1 import type { Database } from '@cancerindex/database';
2 2
3 3 export const TRIAL_INTELLIGENCE_FORMULA_VERSION = 'ci-trial-intel-v1';
4 −export const TRIAL_SITE_COUNTRY_FORMULA_VERSION = 'ci-trial-sites-v1';
5 4
6 5 export interface TrialIntelligenceResult {
7 6 rows: number;
8 − siteCountryRows: number;
9 7 }
10 8
11 9 /**
12 10 * STUB — implemented by the Clinical Trial Intelligence work package.
13 − * Recomputes `trial_intelligence` (per cancer × entity level) and `trial_site_country_counts`.
11 + * Recomputes `trial_intelligence` (per cancer × entity level).
14 12 */
15 13 export async function computeTrialIntelligence(_db: Database): Promise<TrialIntelligenceResult> {
16 − return { rows: 0, siteCountryRows: 0 };
14 + return { rows: 0 };
17 15 }
added packages/ranking/src/trial-sites.ts +16 −0
@@ -0,0 +1,16 @@
1 +import type { Database } from '@cancerindex/database';
2 +
3 +export const TRIAL_SITE_COUNTRY_FORMULA_VERSION = 'ci-trial-sites-v1';
4 +
5 +export interface TrialSiteCountsResult {
6 + rows: number;
7 +}
8 +
9 +/**
10 + * STUB — implemented by the Trial Map work package.
11 + * Recomputes `trial_site_country_counts` (country aggregates of trial sites for all oncology trials
12 + * and for each top-level cancer, any phase / per phase, all statuses / recruiting only).
13 + */
14 +export async function computeTrialSiteCounts(_db: Database): Promise<TrialSiteCountsResult> {
15 + return { rows: 0 };
16 +}
17