@@ -9005,6 +9005,20 @@ def _compute_and_write_zscores(
90059005 heatmap_data[species][sample_name] = []
90069006 heatmap_data[species][sample_name].append(ref) # Store full ref data for filtering
90079007
9008+ # Controls are background only: never show a Z-score on control sample hits.
9009+ if _controls_used:
9010+ for sname in list(db_samples_data.keys()):
9011+ if sname not in _controls_used:
9012+ continue
9013+ per_db = db_samples_data.get(sname) or {}
9014+ for _db, refs in per_db.items():
9015+ if not isinstance(refs, list):
9016+ continue
9017+ for ref in refs:
9018+ if isinstance(ref, dict):
9019+ ref.pop("zscore", None)
9020+ ref.pop("zscore_controls", None)
9021+
90089022 # Prepare data for JavaScript (only this database)
90099023 # Include ALL samples, even if they have no viruses detected
90109024 db_samples_json = {}
@@ -11097,10 +11111,19 @@ def _compute_and_write_zscores(
1109711111 if sname in db_samples_data and database_name in db_samples_data[sname]:
1109811112 refs = db_samples_data[sname][database_name]
1109911113 if not refs:
11100- writer.writerow([sname] + [""] * 11)
11114+ # Empty control samples: still mark Z-score as dash (no numeric score).
11115+ z_empty = "-" if sname in (_controls_used or set()) else ""
11116+ writer.writerow([sname] + [""] * 10 + [z_empty])
1110111117 continue
1110211118 for ref in sorted(refs, key=lambda r: r.get("coverage_breadth", 0), reverse=True):
1110311119 nogr = f"{int(ref.get('nogr_regions', ref.get('non_overlapping_reads', 0)) or 0)}|{int(ref.get('nogr_bases', ref.get('non_overlapping_bases', 0)) or 0)}"
11120+ # Controls are used only to compute Z-scores for non-control samples.
11121+ if sname in (_controls_used or set()):
11122+ z_cell = "-"
11123+ elif ref.get("zscore", None) is None:
11124+ z_cell = "-"
11125+ else:
11126+ z_cell = f"{float(ref.get('zscore') or 0.0):.2f}"
1110411127 writer.writerow([
1110511128 sname,
1110611129 ref.get("accession", ""),
@@ -11113,7 +11136,7 @@ def _compute_and_write_zscores(
1111311136 f"{ref.get('coverage_depth', 0):.2f}",
1111411137 f"{ref.get('coverage_breadth', 0) * 100:.1f}",
1111511138 nogr,
11116- "" if ref.get("zscore", None) is None else f"{float(ref.get('zscore') or 0.0):.2f}" ,
11139+ z_cell ,
1111711140 ])
1111811141 _atomic_write_text(csv_file, csv_buf.getvalue())
1111911142 logger.info(f"Generated CSV summary for {database_name}: {csv_file}")
0 commit comments