Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7affb8a
perf(live): parse each trace file once until it changes, not on every…
GeiserX Nov 8, 2026
d678c91
docs(live): describe the person page, the family page and the card ba…
GeiserX Nov 8, 2026
79a285a
feat(report): mark AnnotSV class 4 and 5 SVs and MoChA calls on the g…
GeiserX Nov 8, 2026
2f186fd
feat(live): draw the repeat gauge, the Lp(a) interval, CHIP VAF, copy…
GeiserX Nov 8, 2026
1b5a6b8
feat(live): draw the family page in depth on an invented trio
GeiserX Nov 8, 2026
028ee51
docs(live): describe the card widgets, the family page and the demo trio
GeiserX Nov 8, 2026
d94b8a8
test(live): keep the widget fixture's invented repeat counts off the …
GeiserX Nov 8, 2026
d16f300
fix(live): draw AnnotSV calls without a chr prefix, flag only real re…
GeiserX Nov 8, 2026
f62bb44
fix(report): draw MoChA calls on chromosomes named without the chr pr…
GeiserX Nov 8, 2026
312897e
docs(report): say the Chromosomes chart draws AnnotSV class 4/5 spans…
GeiserX Nov 8, 2026
9a0c884
feat(live): give each finding a level, a lead paragraph and a page of…
GeiserX Nov 8, 2026
b522041
feat(live): lay out home, person, finding, family and run pages as th…
GeiserX Nov 8, 2026
6f6b6a7
docs(live): describe the Journal pages, the finding route and the new…
GeiserX Nov 8, 2026
154989f
Merge remote-tracking branch 'origin/main' into pgp/live-journal
GeiserX Nov 8, 2026
bddbebd
test(live): wait for a widget's card by a string, not the element, so…
GeiserX Nov 8, 2026
377970b
fix(live): keep a child's result closed everywhere until opened, and …
GeiserX Nov 8, 2026
28058a9
test(live): build the invented repeat count at runtime so the persona…
GeiserX Nov 8, 2026
403ddda
fix(live): close a child's result card with its finding, so the gene …
GeiserX Nov 8, 2026
1140776
fix(live): say the clinical screens have no result yet instead of cal…
GeiserX Nov 8, 2026
461e9c8
test(live): plant a CPSR result on the son's page and prove its card …
GeiserX Nov 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ This pipeline takes raw sequencing data (FASTQ/BAM/VCF) from any vendor and runs
- One Nextflow DSL2 pipeline from FASTQ, BAM or VCF to the report, and every step also as a bash script you can run on its own.
- Optional family analysis of two or more processed relatives (step 38, a pedigree file): a GLnexus joint call, a somalier check that the samples are the people the pedigree says, which allele a child got from each parent, de novo candidates with both parents, and compound heterozygotes in trans or in cis ([multi-sample](https://geiserx.github.io/OwnGenome/multi-sample/#family-analysis-step-38-opt-in)).
- Optional couple carrier screen for two partners of the pedigree (step 39): the recessive genes where both carry a change (ClinVar hits, deletions, SMN1 copy loss, FMR1 and FXN repeats) with the chance for a child, the X-linked findings, and what the screen does not see ([step 39](https://geiserx.github.io/OwnGenome/39-couple/)).
- On the live site, a page per family: the pedigree drawn from the family file, each child's chromosome map, the relationship check, the shared segments and the couple screen ([family page](https://geiserx.github.io/OwnGenome/multi-sample/#on-the-live-site)).
- On the live site, a page per family: a pedigree where each sequenced person opens their own page, the couple screen, who is related how as a grid, each child's stretches painted by parent, the de novo and compound-het candidates, and the members' medicines genes and haplogroups side by side ([family page](https://geiserx.github.io/OwnGenome/multi-sample/#on-the-live-site)).
- Ends in an HTML report and a MultiQC summary. Alternative callers (GATK, FreeBayes, Strelka2, Octopus, BWA-MEM2, TIDDIT, GRIDSS) are there for benchmarking.

## Quick start
Expand Down Expand Up @@ -66,7 +66,7 @@ The full documentation is at **https://geiserx.github.io/OwnGenome/**, one page
- [Vendor compatibility guide](https://geiserx.github.io/OwnGenome/vendor-guide/): what each provider delivers and how to get it
- [Pipeline overview](https://geiserx.github.io/OwnGenome/pipeline-overview/): every step with its tool and image, which ones a default run includes, and the page for each step
- [Nextflow](https://geiserx.github.io/OwnGenome/nextflow/): the workflow runner, parallel steps and resume
- [Watch a run live](https://geiserx.github.io/OwnGenome/live-site/): a step map in the browser while the run goes, behind a password. The [demo](https://geiserx.github.io/OwnGenome/demo/) replays an invented run
- [Watch a run live](https://geiserx.github.io/OwnGenome/live-site/): each genome as a report that leads with what to take to a doctor, a page per finding, a report per family, and a timeline of the steps while the run goes, behind a password. The [demo](https://geiserx.github.io/OwnGenome/demo/) replays an invented run
- [Interpreting your results](https://geiserx.github.io/OwnGenome/interpreting-results/): what each report means and what to do with it
- [Multi-sample comparison](https://geiserx.github.io/OwnGenome/multi-sample/): partners, siblings, parents
- [Long-read guide](https://geiserx.github.io/OwnGenome/long-read-guide/): Nanopore and PacBio HiFi
Expand Down
92 changes: 89 additions & 3 deletions assets/live/core.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// core.js: what the live site's pages share (home.html, index.html, family.html). Element builders,
// core.js: what the live site's pages share (home.html, index.html, finding.html, family.html). Element builders,
// a fetch that sends the session and goes to the login on a 401, the pedigree symbol, dates, the
// caveat block, the facts list and the table. Text only through textContent and createElementNS:
// caveat block, the facts list, the table, and the Journal's level marks and chromosome map. Text only through textContent and createElementNS:
// no innerHTML, no inline script, no eval. Everything hangs on window.PGPUI.
(function () {
'use strict';
Expand Down Expand Up @@ -124,6 +124,92 @@
});
}

// --- the Journal's shared marks: the level glyph and word, and the chromosome map of findings ---
var LEVEL_WORDS = { act: 'Take to a doctor', medicine: 'Tell your prescriber', review: 'Worth a look',
carrier: 'For family planning', trust: 'Data quality', info: 'For information' };
var LEVEL_SHORT = { act: 'doctor', medicine: 'prescriber', review: 'look', carrier: 'family', trust: 'quality', info: 'info' };
var LEVEL_ORDER = ['act', 'medicine', 'review', 'carrier', 'trust', 'info'];
// A level is always its glyph and a word, never a colour alone.
function level(lv, words, extra) {
var s = el('span', { 'class': 'lv ' + lv + (extra ? ' ' + extra : ''), 'data-level': lv });
add(s, el('span', { 'class': 'g', 'aria-hidden': 'true' }), words === undefined ? LEVEL_WORDS[lv] || lv : words);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
return s;
}
// A finding's level: a data-quality finding is always a problem with the data, so its mark is red.
function findingLevel(lv, words) { return level(lv, words, lv === 'trust' ? 'problem' : undefined); }
// GRCh38: [name, length Mb, centromere Mb].
var CHR = [['1', 248.96, 123.4], ['2', 242.19, 93.9], ['3', 198.3, 90.9], ['4', 190.21, 50], ['5', 181.54, 48.8],
['6', 170.81, 59.8], ['7', 159.35, 60.1], ['8', 145.14, 45.2], ['9', 138.39, 43], ['10', 133.8, 39.8],
['11', 135.09, 53.4], ['12', 133.28, 35.5], ['13', 114.36, 17.7], ['14', 107.04, 17.2], ['15', 101.99, 19],
['16', 90.34, 36.8], ['17', 83.26, 25.1], ['18', 80.37, 18.5], ['19', 58.62, 26.2], ['20', 64.44, 28.1],
['21', 46.71, 12], ['22', 50.82, 15], ['X', 156.04, 60.6], ['Y', 57.23, 10.4]];
var LV_COLOUR = { act: '#d03b3b', medicine: '#ec835a', review: '#fab219', carrier: '#9085e9', trust: '#c3c2b7', info: '#c3c2b7' };
function glyph(g, lv, x, y) {
var c = LV_COLOUR[lv] || LV_COLOUR.info;
if (lv === 'act') { g.appendChild(svg('rect', { x: x - 4, y: y - 4, width: 8, height: 8, fill: c, transform: 'rotate(45 ' + x + ' ' + y + ')' })); }
else if (lv === 'medicine') { g.appendChild(svg('rect', { x: x - 5.5, y: y - 3, width: 11, height: 6, rx: 3, fill: c })); }
else if (lv === 'review') { g.appendChild(svg('path', { d: 'M' + x + ' ' + (y - 5) + 'L' + (x + 5) + ' ' + (y + 4) + 'L' + (x - 5) + ' ' + (y + 4) + 'Z', fill: c })); }
else if (lv === 'carrier') {
g.appendChild(svg('circle', { cx: x - 2.6, cy: y, r: 3.4, fill: 'none', stroke: c, 'stroke-width': 1.6 }));
g.appendChild(svg('circle', { cx: x + 2.6, cy: y, r: 3.4, fill: 'none', stroke: c, 'stroke-width': 1.6 }));
} else { g.appendChild(svg('circle', { cx: x, cy: y, r: 4, fill: 'none', stroke: c, 'stroke-width': 1.5 })); }
}
// "chr16:2087914" or "chr16:2084000-2096000" -> {chr: '16', mb: 2.08}; null for anything else.
function locus(v) {
var m = /^(?:chr)?([0-9]{1,2}|X|Y):([0-9]+)/.exec(String(v || ''));
return m ? { chr: m[1], mb: Number(m[2]) / 1e6 } : null;
}
// The chromosome map: the 24 chromosomes in a row, each finding a glyph on its chromosome, with its
// name beside it and the same words in a title (and in the list under the figure).
function karyogram(marks, opts) {
opts = opts || {};
var W = opts.width || 680, H = opts.height || 190, top = 22, bottom = 22;
var slot = (W - 56) / CHR.length, scale = (H - top - bottom) / 249;
var s = svg('svg', { viewBox: '0 0 ' + W + ' ' + H, role: 'img', 'aria-label': opts.aria || 'Where the findings sit, by chromosome' });
CHR.forEach(function (c, i) {
var x = i * slot + slot / 2 - 3.5;
s.appendChild(svg('rect', { 'class': 'chr', x: x, y: top, width: 7, height: Math.max(1, c[2] * scale - 1.2), rx: 3.5 }));
s.appendChild(svg('rect', { 'class': 'chr', x: x, y: top + c[2] * scale + 1.2, width: 7, height: Math.max(1, (c[1] - c[2]) * scale - 1.2), rx: 3.5 }));
var t = svg('text', { 'class': 'lab', x: x + 3.5, y: H - 6, 'text-anchor': 'middle' });
t.textContent = c[0];
s.appendChild(t);
});
(marks || []).forEach(function (m) {
var i = -1;
CHR.forEach(function (c, j) { if (c[0] === m.chr) { i = j; } });
if (i < 0) { return; }
var x = i * slot + slot / 2 + 7, y = top + Math.min(m.mb, CHR[i][1]) * scale;
var g = svg('g', { 'class': 'mk', tabindex: '0' });
var tt = svg('title', {});
tt.textContent = (LEVEL_WORDS[m.lv] || '') + ': ' + (m.tip || m.label);
g.appendChild(tt);
g.appendChild(svg('line', { x1: x - 10.5, x2: x - 1, y1: y, y2: y, stroke: LV_COLOUR[m.lv] || LV_COLOUR.info, 'stroke-width': 2 }));
glyph(g, m.lv, x + 4, y);
if (m.label) {
var t = svg('text', { x: x + 11, y: y + 3.5, stroke: '#0e0f10', 'stroke-width': 4, 'paint-order': 'stroke', 'stroke-linejoin': 'round' });
t.textContent = m.label;
g.appendChild(t);
}
if (m.href) {
g.setAttribute('role', 'link');
g.addEventListener('click', function () { window.location.href = m.href; });
g.addEventListener('keydown', function (e) { if (e.key === 'Enter') { window.location.href = m.href; } });
}
s.appendChild(g);
});
return s;
}
// A plain time: "14:22" in UTC, "4 Nov" for a day.
var MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
function hm(utc) { var d = new Date(utc); return isFinite(d) ? ('0' + d.getUTCHours()).slice(-2) + ':' + ('0' + d.getUTCMinutes()).slice(-2) : ''; }
function dm(utc) { var d = new Date(utc); return isFinite(d) ? d.getUTCDate() + ' ' + MONTHS[d.getUTCMonth()] : ''; }
function hours(a, b) {
var x = Date.parse(a), y = Date.parse(b);
return isFinite(x) && isFinite(y) && y > x ? (y - x) / 3600e3 : null;
}

window.PGPUI = { el: el, svg: svg, add: add, getJSON: getJSON, avatar: avatar, day: day, caveat: caveat,
facts: facts, table: table, tablist: tablist, firstSentence: firstSentence };
facts: facts, table: table, tablist: tablist, firstSentence: firstSentence, level: level, findingLevel: findingLevel, glyph: glyph,
karyogram: karyogram, locus: locus, LEVEL_WORDS: LEVEL_WORDS, LEVEL_SHORT: LEVEL_SHORT, LEVEL_ORDER: LEVEL_ORDER,
hm: hm, dm: dm, hours: hours };
}());
Loading
Loading