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.
68 lines
2.4 KiB
JavaScript
68 lines
2.4 KiB
JavaScript
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();
|