Apply the brand across the repo: reports, README, guide
ci / test (3.12) (push) Failing after 9s
ci / test (3.13) (push) Failing after 11s

* 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:
kemalandClaude Opus 5.5 committed 2026-09-30 08:01:30 +01:00
1 parent dfe477787f
commit 9149ef328a
12 files changed
+222 -106

No files matched your search

+16 -8
View File
@@ -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")