From 7b6eef32b463e3551b00954437783fe51150f92c Mon Sep 17 00:00:00 2001 From: Julen Mendieta Date: Thu, 3 Sep 2026 12:02:54 +0200 Subject: [PATCH 1/6] Remove status column from QC table --- software/per-cell-metrics/src/qc_rows.py | 11 +++--- .../test/test_emit_verdicts.py | 31 ++++++++++------- ui/src/pages/QcSummaryPage.vue | 34 ------------------- workflow/src/column-specs.lib.tengo | 13 ------- 4 files changed, 24 insertions(+), 65 deletions(-) diff --git a/software/per-cell-metrics/src/qc_rows.py b/software/per-cell-metrics/src/qc_rows.py index 76618b9..3de37e9 100644 --- a/software/per-cell-metrics/src/qc_rows.py +++ b/software/per-cell-metrics/src/qc_rows.py @@ -460,18 +460,20 @@ def sample_summary_rows( Pivots `sample_report` -- the same dict `main` writes to `result_qc_by_sample.json` -- rather than walking `MEASUREMENTS` a second time, so this table and a sample's own report cannot disagree. - `status` is `sample_report`'s own rollup, from `roll_up`, never recomputed here. + + NO rollup column. `roll_up`'s result travels in `result_qc_by_sample.json` and reaches a reader as + the Main grid's Quality tag and the heading of a sample's own Quality Checks tab. A third copy here + said nothing those two had not already said, and it needed its own cell renderer to say it. Every id in `samples` gets a row, a sample absent from `sample_report` included: its measurement - columns and its status come back null, which reads as nothing having rolled up rather than as a - passing sample. + columns come back null, which reads as nothing having been computed rather than as a passing sample. """ built = [] for sample in samples: report = sample_report.get(sample, {}) entries = {e["id"]: e["value"] for e in report.get("measurements", [])} qc = read_qc.get(sample, {}) - row = {"sampleId": sample, "status": report.get("status")} + row = {"sampleId": sample} for col in _MITOOL_ONLY_COLUMNS: row[col] = _number(qc, col) for m in _SAMPLE_MEASUREMENTS: @@ -479,7 +481,6 @@ def sample_summary_rows( built.append(row) schema = { "sampleId": pl.String, - "status": pl.String, **{col: pl.Float64 for col in _MITOOL_ONLY_COLUMNS}, **{m.id: pl.Float64 for m in _SAMPLE_MEASUREMENTS}, } diff --git a/software/per-cell-metrics/test/test_emit_verdicts.py b/software/per-cell-metrics/test/test_emit_verdicts.py index 7180388..04d757c 100644 --- a/software/per-cell-metrics/test/test_emit_verdicts.py +++ b/software/per-cell-metrics/test/test_emit_verdicts.py @@ -3544,10 +3544,9 @@ def test_the_wide_summary_carries_every_sample_in_the_roster_including_one_with_ s2 = summary.filter(pl.col("sampleId") == "S2") assert s2.height == 1 - # Nothing computed a value for S2, so its cells read null rather than 0 or "OK" -- a blank and a zero - # are opposite findings. + # Nothing computed a value for S2, so its cells read null rather than 0 -- a blank and a zero are + # opposite findings. assert s2["readsTotal"].item() is None - assert s2["status"].item() is None def test_the_wide_summary_carries_every_sample_level_measurement_as_a_column(bed): @@ -3562,19 +3561,25 @@ def test_the_wide_summary_carries_every_sample_level_measurement_as_a_column(bed assert "cellBarcodeValidFraction" in summary.columns -def test_the_wide_summary_status_is_the_sample_rollup_and_is_not_recomputed(bed): +def test_the_wide_summary_carries_no_rollup_column(bed): + """The rollup lives in one place, and this table is not it. + + `roll_up`'s result travels in `result_qc_by_sample.json` and reaches a reader as the Main grid's + Quality tag and as the heading of a sample's own Quality Checks tab. It used to be copied here as + well, and the test that stood here pinned the two copies against each other -- because two copies of + a status can disagree, and the one a reader happens to be looking at decides what they believe. + + One copy cannot disagree with itself. What needs pinning now is that a second one does not come + back: this table is measurements, and a status column here would need its own cell renderer and its + own agreement test all over again. + """ _run(bed, *BASE) summary = pl.read_csv(bed / "result_qc_summary.csv") + assert "status" not in summary.columns + # The rollup is still computed and still reported -- just not from here. by_sample = json.loads((bed / "result_qc_by_sample.json").read_text()) - - for sample_id, report in by_sample.items(): - row = summary.filter(pl.col("sampleId") == sample_id) - assert row.height == 1 - got = row["status"].item() - assert got == report["status"], ( - f"wide table status {got!r} for {sample_id!r} disagrees with the sample's own report " - f"{report['status']!r}; the two must read off one rollup" - ) + assert by_sample, "the bed produced no per-sample report" + assert all("status" in report for report in by_sample.values()) def test_a_missing_read_qc_row_names_the_row_not_the_denominator(bed): diff --git a/ui/src/pages/QcSummaryPage.vue b/ui/src/pages/QcSummaryPage.vue index a73bf9b..7155c86 100644 --- a/ui/src/pages/QcSummaryPage.vue +++ b/ui/src/pages/QcSummaryPage.vue @@ -1,50 +1,17 @@