Apply the brand across the repo: reports, README, guide
* simurg/core/brand.py: single source of the mark geometry, colours, favicon and the report header band (scripts/brand now load it from there). * HTML reports (QC report, compare dashboard, methylation QC): offline SVG favicon, indigo header band with the mark, version and a sha256 of the data shown; Noto Sans font stack; methylation plots in Okabe-Ito colours. * README banner, guide logo, brand/blog links; branding brief updated to the final identity; Noto (OFL) and Okabe-Ito entries in THIRD_PARTY_NOTICES; CHANGELOG (Unreleased). Unit tests + P5 QC oracle: 285 passed; ruff and mypy clean. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01814GKVzZVL3FuF3DeiZXhv
This commit is contained in:
1 parent
dfe477787f
commit
9149ef328a
12 files changed
+222
-106
No files matched your search
@@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
|
||||
import datetime as _dt
|
||||
import gzip
|
||||
import hashlib
|
||||
import html
|
||||
import json
|
||||
import math
|
||||
@@ -390,22 +391,29 @@ def _write_html(
|
||||
th_rows = "".join(
|
||||
f"<tr><td>{m.column}</td><td>{m.key}</td><td>{_cell(th.get(m.key).value)}</td></tr>" for m in metrics
|
||||
)
|
||||
from simurg import __version__
|
||||
from simurg.core.brand import BAR_CSS, CHART, FONT_STACK, favicon_link, report_bar
|
||||
|
||||
generated = _dt.datetime.now(_dt.UTC).strftime("%Y-%m-%d %H:%M UTC")
|
||||
checksum = hashlib.sha256(json.dumps(rows, sort_keys=True, default=str).encode("utf-8")).hexdigest()
|
||||
bar = report_bar("Methylation control QC", __version__, "samples sha256", checksum)
|
||||
page = f"""<!doctype html><html lang="en"><head><meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"><title>Methylation QC</title>
|
||||
<style>body{{font:14px system-ui,sans-serif;margin:16px;color:#222;background:#fff}}table{{border-collapse:collapse}}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"><title>Methylation QC · SimurgArray</title>
|
||||
{favicon_link()}
|
||||
<style>body{{font:14px {FONT_STACK};margin:0;color:#222;background:#fff}}.sa-main{{padding:16px}}
|
||||
table{{border-collapse:collapse}}
|
||||
td,th{{border:1px solid #ccc;padding:3px 6px;text-align:left}}.plots{{display:flex;flex-wrap:wrap;gap:16px}}
|
||||
.plot{{width:min(560px,100%);height:380px}}.note{{color:#666}}</style>
|
||||
.plot{{width:min(560px,100%);height:380px}}.note{{color:#666}}{BAR_CSS}</style>
|
||||
<script>{_plotly()}</script></head><body>
|
||||
<h1>Methylation control QC</h1>
|
||||
{bar}<div class="sa-main">
|
||||
<p class="note">{len(rows)} samples · manifest {html.escape(Path(man.path).name)}
|
||||
· profile {html.escape(th.profile or "none")} · generated {generated} · SimurgArray</p>
|
||||
· profile {html.escape(th.profile or "none")} · generated {generated}</p>
|
||||
<div class="plots"><div id="hist" class="plot"></div><div id="pca" class="plot"></div></div>
|
||||
<h2>Samples</h2><table><tr>{"".join(f"<th>{c}</th>" for c in head)}</tr>{trs}</table>
|
||||
<h2>Thresholds</h2><table><tr><th>metric</th><th>key</th><th>threshold</th></tr>{th_rows}</table>
|
||||
<script>const D={json.dumps(data, allow_nan=False, default=_json_num)};
|
||||
Plotly.newPlot("hist",[{{x:D.hist.pass,type:"histogram",name:"pass",marker:{{color:"#4c78a8"}}}},
|
||||
{{x:D.hist.fail,type:"histogram",name:"fail",marker:{{color:"#e45756"}}}}],
|
||||
Plotly.newPlot("hist",[{{x:D.hist.pass,type:"histogram",name:"pass",marker:{{color:"{CHART["BB"]}"}}}},
|
||||
{{x:D.hist.fail,type:"histogram",name:"fail",marker:{{color:"{CHART["AA"]}"}}}}],
|
||||
{{title:"Probes passing detection",barmode:"overlay",xaxis:{{title:"proportion"}},yaxis:{{title:"samples"}}}});
|
||||
if(D.pcs.length){{const g=[...new Set(D.pcs.map(p=>p.sample_group))];
|
||||
Plotly.newPlot("pca",g.map(k=>({{x:D.pcs.filter(p=>p.sample_group===k).map(p=>p.pc1),
|
||||
@@ -413,7 +421,7 @@ y:D.pcs.filter(p=>p.sample_group===k).map(p=>p.pc2),text:D.pcs.filter(p=>p.sampl
|
||||
mode:"markers",type:"scatter",name:k}})),{{title:"PCA of beta ("+D.n_pca_probes+" candidate probes)",
|
||||
xaxis:{{title:"PC1"}},yaxis:{{title:"PC2"}}}});}}
|
||||
else{{document.getElementById("pca").textContent="No features, PCA skipped";}}
|
||||
</script></body></html>
|
||||
</script></div></body></html>
|
||||
"""
|
||||
tmp = path.with_suffix(".html.part")
|
||||
tmp.write_text(page, encoding="utf-8")
|
||||
|
||||
Reference in new issue
Block a user