docs: a technical introduction to the platform, as a LaTeX article

docs/blog/rarelens.tex builds a 10-page write-up covering the Nextflow DSL2
pipeline, the three execution backends behind one API call, the Argo and
ArgoCD track, Terraform, the external systems integrated, the ranking, the
benchmark and the model.

It is deliberately as much an account of what was wrong as of what works: the
components that scored evidence nobody had looked up, the missense AUROC that
fell from 0.872 to 0.500 once allele frequency was removed, the ontology walk
that silently dropped 399 terms, and the propagation change that measured
slightly worse than what it replaced and was kept anyway with the numbers
published.

Figures come from one headless-browser script. Screenshots are of the running
application; the six diagrams are hand-written HTML styled from the same
palette, rendered by the same script. Keeping both in one place is what stops
the article drifting from the system, and every number in a diagram has to be
changed deliberately.
This commit is contained in:
Kemal Yaylali
2026-09-12 13:10:47 +01:00
parent c25fb53666
commit 1e46fff2ff
25 changed files with 729 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
import { chromium } from 'playwright';
import { mkdirSync } from 'node:fs';
import { resolve } from 'node:path';
const BASE = process.env.BASE ?? 'http://localhost:5173';
const CASE = process.env.CASE_ID ?? '91d85d3d-3351-4a7d-8d29-164cdea76518';
const OUT = resolve('out');
mkdirSync(OUT, { recursive: true });
const shot = async (page, name, opts = {}) => {
await page.screenshot({ path: `${OUT}/${name}.png`, ...opts });
console.log('wrote', name);
};
const EXE = process.env.CHROME_PATH;
const browser = await chromium.launch(EXE ? { executablePath: EXE } : {});
const page = await browser.newPage({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 2
});
// 1. case list
await page.goto(`${BASE}/`, { waitUntil: 'networkidle' });
await page.waitForSelector('.caselist li', { timeout: 20000 });
await shot(page, '01-cases', { fullPage: true });
// 2. the published case: funnel, filters, ranked candidates
await page.goto(`${BASE}/cases/${CASE}`, { waitUntil: 'networkidle' });
await page.waitForSelector('.candidate', { timeout: 30000 });
await page.waitForTimeout(600);
await shot(page, '02-case', { fullPage: true });
// 3. the evidence panel for the top candidate: the components table, abstentions and all
await page.locator('.candidate').first().click();
await page.waitForSelector('.panel', { timeout: 20000 });
await page.waitForTimeout(600);
await shot(page, '03-variant-panel', { fullPage: true });
// just the panel, cropped, for a tighter figure
const panel = page.locator('.panel').first();
await panel.screenshot({ path: `${OUT}/04-panel-only.png` });
console.log('wrote 04-panel-only');
// just the funnel
const funnel = page.locator('.funnel, [class*="funnel"]').first();
if (await funnel.count()) {
await funnel.screenshot({ path: `${OUT}/05-funnel.png` });
console.log('wrote 05-funnel');
}
// 4. the case report
await page.goto(`${BASE}/cases/${CASE}/report`, { waitUntil: 'networkidle' });
await page.waitForTimeout(1200);
await shot(page, '06-report', { fullPage: true });
// 5. local diagram pages rendered to PNG
for (const name of process.env.DIAGRAMS?.split(',').filter(Boolean) ?? []) {
await page.goto(`file://${resolve(`${name}.html`)}`, {
waitUntil: 'networkidle'
});
await page.waitForTimeout(300);
const box = page.locator('#frame');
await box.screenshot({ path: `${OUT}/${name}.png` });
console.log('wrote', name);
}
await browser.close();