Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ And your report will look like:
| `levels` | An ordered list; the first level whose `match` is found in a sample name wins. Required. |
| `levels[].match` | A regular expression searched in the sample name. Named captures are available to `label`. |
| `levels[].label` | A format string built from the captures of `match`. Columns of matching rows are renamed with it and folded onto the group row; the row itself stays beneath. Omit it, and omit `table`, to fold the row's columns onto the group row unchanged. |
| `levels[].table` | The name of a table that receives matching rows instead of General Statistics. Rows keep their grouping, so paired reads stay nested under their library. |
| `levels[].table` | The name of a table that receives matching rows instead of General Statistics. Rows keep their grouping, so paired reads stay nested under their library. Tables sit directly under General Statistics in the order their levels are listed. |
| `column_title` | How a pivoted column is titled. `{label}` is the label as written, `{Label}` has its first letter upper-cased, `{title}` is the module's title. Default `{Label} {title}`. |
| `label_order` | Labels in the order their column blocks should appear. Labels not listed follow in order of first appearance. |
| `tables` | Presentation of the tables named by `levels[].table`, currently a `description` each. |
Expand Down
10 changes: 5 additions & 5 deletions multiqc_pivot/pivot.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,14 @@ def pivot(rows: Rows, headers: Headers, settings: SamplePivotConfig) -> PivotRes
Rows that match a labelled level have their declared columns renamed after the label and copied
onto the group's row; the original rows stay beneath it so the group can still be expanded.
Rows that match an unlabelled level are folded onto the group's row as they are. Rows that match
a level with a table are moved into that table with their grouping intact. Rows that match no
a level with a table are moved into that table with their grouping intact; the tables come back
in the order their levels are listed, without the ones that received no rows. Rows that match no
level, and columns a module did not declare a header for, are left alone.
"""
placement = Placement(settings.label_order)
out_rows: Rows = {}
out_headers: Headers = {}
tables: dict[str, Table] = {}
tables = {level.table: Table() for level in settings.levels if level.table is not None}
for section, rows_by_group in rows.items():
section_headers = headers.get(section, {})
pivoted = SectionPivot(section_headers, settings, placement)
Expand All @@ -227,15 +228,14 @@ def pivot(rows: Rows, headers: Headers, settings: SamplePivotConfig) -> PivotRes
if route is None:
pivoted.keep(group, row)
elif isinstance(route, Moved):
table = tables.setdefault(route.table, Table())
table.add(section, group, row, section_headers)
tables[route.table].add(section, group, row, section_headers)
elif route.label is None:
pivoted.fold(route.group, row)
else:
pivoted.fold_labelled(route.group, route.label, row)
out_rows[section] = pivoted.finish()
out_headers[section] = pivoted.new_headers
return PivotResult(out_rows, out_headers, tables)
return PivotResult(out_rows, out_headers, {n: t for n, t in tables.items() if t.rows})


def _fold(target: RowData, key: ColumnKeyT, value: ExtValueT | None, group: str) -> None:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "multiqc-pivot"
version = "0.1.0"
version = "0.1.1"
description = "A MultiQC plugin that folds related samples into one General Statistics row per group with labelled metric columns."
readme = "README.md"
authors = [{ name = "Clint Valentine", email = "valentine.clint@gmail.com" }]
Expand Down
28 changes: 28 additions & 0 deletions tests/test_pivot.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,34 @@ def test_table_levels_move_rows_with_their_grouping() -> None:
assert table.headers == headers


def test_tables_follow_level_order_not_module_order() -> None:
settings = SETTINGS.model_copy(
update={
"levels": [
Level(match=r"\.library\.", table="Library statistics"),
Level(match=r"^cohort\.", table="Cohort annotation"),
Level(match=r"\.nothing\.", table="Never filled"),
]
}
)
snpeff, fastqc = SectionKey("snpeff"), SectionKey("fastqc")
rows = {
snpeff: section(row("cohort.somatic", variants=1234)),
fastqc: section(row("101.tissueA.library.L1", dups=70.0)),
}
headers = {
snpeff: {ColumnKey("variants"): header("Variants")},
fastqc: {ColumnKey("dups"): header("Dups")},
}

result = pivot(rows, headers, settings)

assert list(result.tables) == ["Library statistics", "Cohort annotation"]
assert result.tables["Cohort annotation"].rows == {
snpeff: section(row("cohort.somatic", variants=1234))
}


def test_unmatched_rows_are_untouched() -> None:
other = SectionKey("other")
rows = {other: section(row("control", median=1))}
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.