Skip to content

Commit 0006024

Browse files
committed
.
1 parent 69e7a9b commit 0006024

2 files changed

Lines changed: 131 additions & 7 deletions

File tree

nf-metatropics/modules/local/virasign/build_html.nf

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ process VIRASIGN_SUMMARY {
9898
<head>
9999
<meta charset="utf-8"/>
100100
<meta name="viewport" content="width=device-width, initial-scale=1"/>
101-
<title>Virasign summary</title>
101+
<title>Metatropics Results Summary</title>
102102
</head>
103103
<body>
104-
<h1>Virasign summary</h1>
104+
<h1>Metatropics Results Summary</h1>
105105
<p>No confident hits were found (no <code>*_final_selected_references.json</code> files present).</p>
106106
</body>
107107
</html>
@@ -624,6 +624,13 @@ if summary_html.exists():
624624
625625
text = summary_html.read_text(encoding="utf-8")
626626
627+
# Branding: Metatropics report header (keep database suffix, e.g. " - RVDB").
628+
text = text.replace("Virasign Results Summary", "Metatropics Results Summary")
629+
text = text.replace(
630+
"Viral Read ASSIGNment from nanopore sequencing",
631+
"Human viral pathogen identification from nanopore metagenomic sequencing",
632+
)
633+
627634
# If the pipeline is re-run, remove any previously injected consensus panel.
628635
# Use plain string operations (avoid regex backslash parsing issues).
629636
section_start = text.find('<section id="consensus-breadth"')
@@ -680,13 +687,72 @@ if summary_html.exists():
680687
bg_map = {}
681688
bg_map_json = json.dumps(bg_map, separators=(",", ":"))
682689
690+
# Resolve which water/background samples were actually used for Z-score.
691+
# Prefer names embedded by Virasign in samplesData.zscore_controls (truth of the run).
692+
zscore_controls_used = []
693+
try:
694+
m_sd = re.search(r"const samplesData\\s*=\\s*", text)
695+
if m_sd:
696+
samples_obj, _ = json.JSONDecoder().raw_decode(text, m_sd.end())
697+
seen = []
698+
for _sname, dbs in (samples_obj or {}).items():
699+
for _db, payload in (dbs or {}).items():
700+
for ref in (payload or {}).get("references") or []:
701+
for c in ref.get("zscore_controls") or []:
702+
c = str(c).strip()
703+
if c and c not in seen:
704+
seen.append(c)
705+
zscore_controls_used = seen
706+
except Exception:
707+
zscore_controls_used = []
708+
709+
if not zscore_controls_used and controls_norm:
710+
zscore_controls_used = sorted(controls_norm)
711+
if not zscore_controls_used:
712+
zscore_controls_used = sorted(s for s, v in bg_map.items() if str(v).strip().lower() == "yes")
713+
714+
if not zscore_enabled:
715+
zscore_mode = "disabled"
716+
zscore_mode_label = "disabled"
717+
zscore_water_msg = f"Z-score disabled (--zscore={zscore_enabled_raw})."
718+
elif zscore_controls_text:
719+
zscore_mode = "manual"
720+
zscore_mode_label = "manually selected"
721+
names = ", ".join(zscore_controls_used) if zscore_controls_used else zscore_controls_text
722+
if zscore_has_values:
723+
zscore_water_msg = f"Water controls (manually selected): {names}"
724+
else:
725+
zscore_water_msg = (
726+
f"Water controls provided, but none were usable/available for Z-score computation. "
727+
f"Provided: {names}"
728+
)
729+
elif zscore_controls_used and zscore_has_values:
730+
zscore_mode = "auto"
731+
zscore_mode_label = "auto-detected"
732+
zscore_water_msg = f"Water controls (auto-detected): {', '.join(zscore_controls_used)}"
733+
else:
734+
zscore_mode = "none"
735+
zscore_mode_label = "none available"
736+
zscore_water_msg = (
737+
"Water controls: none usable/available (Z-score not computed)."
738+
)
739+
740+
zscore_controls_json = json.dumps(zscore_controls_used, separators=(",", ":"))
741+
zscore_mode_json = json.dumps(zscore_mode, separators=(",", ":"))
742+
zscore_mode_label_json = json.dumps(zscore_mode_label, separators=(",", ":"))
743+
zscore_water_msg_json = json.dumps(zscore_water_msg, separators=(",", ":"))
744+
683745
inj_script = '''
684746
<script>
685747
(function(){
686748
const consensusMap = __CONS_MAP_JSON__;
687749
const qcReadsMap = __QC_READS_MAP_JSON__;
688750
const readLenMap = __READLEN_MAP_JSON__;
689751
const bgMap = __BG_MAP_JSON__;
752+
const zscoreControls = __ZSCORE_CONTROLS_JSON__;
753+
const zscoreMode = __ZSCORE_MODE_JSON__;
754+
const zscoreModeLabel = __ZSCORE_MODE_LABEL_JSON__;
755+
const zscoreWaterMsg = __ZSCORE_WATER_MSG_JSON__;
690756
691757
function getVal(sampleName, accession){
692758
if (!consensusMap || !consensusMap[sampleName]) return null;
@@ -945,6 +1011,62 @@ if summary_html.exists():
9451011
setConsensusColumnVisibility(sampleName, false);
9461012
}
9471013
1014+
// Inject Z-score water-control info into the Summary banner and Pipeline Configuration.
1015+
function zscoreControlsDisplay(){
1016+
if (Array.isArray(zscoreControls) && zscoreControls.length) {
1017+
return zscoreControls.join(', ');
1018+
}
1019+
return zscoreWaterMsg || 'none';
1020+
}
1021+
1022+
function injectZscoreControlsBanner(){
1023+
const summary = document.querySelector('.summary');
1024+
if (!summary || document.getElementById('zscore-controls-banner')) return;
1025+
const div = document.createElement('div');
1026+
div.id = 'zscore-controls-banner';
1027+
div.style.margin = '1rem 0 0';
1028+
div.style.padding = '0.85rem 1rem';
1029+
div.style.background = '#f0f7ff';
1030+
div.style.border = '1px solid #c5daf5';
1031+
div.style.borderRadius = '8px';
1032+
div.style.color = '#1a365d';
1033+
div.style.fontSize = '0.95em';
1034+
const modeTxt = zscoreModeLabel ? (' (' + zscoreModeLabel + ')') : '';
1035+
div.innerHTML =
1036+
'<strong>Z-score water controls</strong>' +
1037+
'<span style="opacity:0.75">' + modeTxt + '</span>: ' +
1038+
'<span>' + zscoreControlsDisplay() + '</span>';
1039+
summary.appendChild(div);
1040+
}
1041+
1042+
function appendZscoreMetadata(sampleName){
1043+
const container = document.getElementById('metadata-' + sampleName);
1044+
if (!container || container.querySelector('[data-role="zscore-controls"]')) return;
1045+
const item = document.createElement('div');
1046+
item.className = 'metadata-item';
1047+
item.setAttribute('data-role', 'zscore-controls');
1048+
const label = document.createElement('div');
1049+
label.className = 'label';
1050+
label.textContent = 'Z-score water controls' + (zscoreModeLabel ? ' (' + zscoreModeLabel + ')' : '');
1051+
const value = document.createElement('div');
1052+
value.className = 'value';
1053+
value.textContent = zscoreControlsDisplay();
1054+
value.style.wordBreak = 'break-word';
1055+
item.appendChild(label);
1056+
item.appendChild(value);
1057+
container.appendChild(item);
1058+
}
1059+
1060+
injectZscoreControlsBanner();
1061+
1062+
const origPopulateMetadata = window.populateMetadata;
1063+
if (typeof origPopulateMetadata === 'function'){
1064+
window.populateMetadata = function(sampleName){
1065+
origPopulateMetadata(sampleName);
1066+
appendZscoreMetadata(sampleName);
1067+
};
1068+
}
1069+
9481070
const origPopulateTable = window.populateTable;
9491071
if (typeof origPopulateTable === 'function'){
9501072
window.populateTable = function(sampleName){
@@ -953,10 +1075,7 @@ if summary_html.exists():
9531075
};
9541076
}
9551077
956-
// (No Z-score background metadata injection: keep HTML clean.)
957-
958-
// Override downloadTableAsCSV so the downloaded CSV includes QC reads + median read length + consensus breadth.
959-
const origDownloadTableAsCSV = window.downloadTableAsCSV;
1078+
// Override downloadTableAsCSV so the downloaded CSV includes QC reads + median read length + consensus breadth. const origDownloadTableAsCSV = window.downloadTableAsCSV;
9601079
window.downloadTableAsCSV = function(sampleName) {
9611080
const tbody = document.getElementById('tbody-' + sampleName);
9621081
if (!tbody) return;
@@ -1096,6 +1215,7 @@ if summary_html.exists():
10961215
if (active && active.id && active.id.startsWith('sample-')){
10971216
const sn = active.id.slice('sample-'.length);
10981217
injectForSample(sn);
1218+
appendZscoreMetadata(sn);
10991219
}
11001220
} catch (e) {}
11011221
})();
@@ -1106,6 +1226,10 @@ if summary_html.exists():
11061226
inj_script = inj_script.replace("__QC_READS_MAP_JSON__", qc_map_json)
11071227
inj_script = inj_script.replace("__READLEN_MAP_JSON__", readlen_map_json)
11081228
inj_script = inj_script.replace("__BG_MAP_JSON__", bg_map_json)
1229+
inj_script = inj_script.replace("__ZSCORE_CONTROLS_JSON__", zscore_controls_json)
1230+
inj_script = inj_script.replace("__ZSCORE_MODE_JSON__", zscore_mode_json)
1231+
inj_script = inj_script.replace("__ZSCORE_MODE_LABEL_JSON__", zscore_mode_label_json)
1232+
inj_script = inj_script.replace("__ZSCORE_WATER_MSG_JSON__", zscore_water_msg_json)
11091233
11101234
if 'data-col="consensus_breadth_pct"' not in text:
11111235
if "</body>" in text:

params_fastq.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ Host: human
1313

1414
# Z-score controls: samplesheet sample names (works for FASTQ / fastq_pass / POD5).
1515
# When set, replaces auto-detect
16-
#virasign_zscore_controls: H20_1,H20_2,BG_1,BG_2
16+
virasign_zscore_controls: H20_1,H20_2,BG_1,BG_2

0 commit comments

Comments
 (0)