multiqc-pivot 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multiqc_pivot-0.1.0/.github/CODEOWNERS +1 -0
- multiqc_pivot-0.1.0/.github/workflows/publish_multiqc_pivot.yml +133 -0
- multiqc_pivot-0.1.0/.github/workflows/tests.yml +37 -0
- multiqc_pivot-0.1.0/.github/workflows/wheels.yml +37 -0
- multiqc_pivot-0.1.0/.gitignore +161 -0
- multiqc_pivot-0.1.0/CONTRIBUTING.md +125 -0
- multiqc_pivot-0.1.0/LICENSE +21 -0
- multiqc_pivot-0.1.0/PKG-INFO +142 -0
- multiqc_pivot-0.1.0/README.md +112 -0
- multiqc_pivot-0.1.0/docs/pivot.png +0 -0
- multiqc_pivot-0.1.0/multiqc_pivot/__init__.py +0 -0
- multiqc_pivot-0.1.0/multiqc_pivot/config.py +107 -0
- multiqc_pivot-0.1.0/multiqc_pivot/pivot.py +245 -0
- multiqc_pivot-0.1.0/multiqc_pivot/plugin.py +89 -0
- multiqc_pivot-0.1.0/multiqc_pivot/py.typed +0 -0
- multiqc_pivot-0.1.0/pyproject.toml +211 -0
- multiqc_pivot-0.1.0/tests/__init__.py +0 -0
- multiqc_pivot-0.1.0/tests/data/multiqc_config.yml +17 -0
- multiqc_pivot-0.1.0/tests/data/report/alignment_mqc.yaml +16 -0
- multiqc_pivot-0.1.0/tests/data/report/concordance_mqc.yaml +12 -0
- multiqc_pivot-0.1.0/tests/data/report/coverage_mqc.yaml +19 -0
- multiqc_pivot-0.1.0/tests/data/report/libraries_mqc.yaml +22 -0
- multiqc_pivot-0.1.0/tests/test_config.py +73 -0
- multiqc_pivot-0.1.0/tests/test_pivot.py +210 -0
- multiqc_pivot-0.1.0/tests/test_plugin.py +77 -0
- multiqc_pivot-0.1.0/uv.lock +2293 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @clintval
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
name: publish multiqc-pivot
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: '[0-9]+.[0-9]+.[0-9]+'
|
|
6
|
+
|
|
7
|
+
permissions: {}
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
UV_VERSION: 0.11.16
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
on-main-branch-check:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
outputs:
|
|
18
|
+
on_main: ${{ steps.contains_tag.outputs.retval }}
|
|
19
|
+
steps:
|
|
20
|
+
# action-contains-tag lists remote branches to find the tag; current git follows the remote
|
|
21
|
+
# HEAD by default, which breaks that lookup: https://github.com/rickstaa/action-contains-tag/pull/18
|
|
22
|
+
- name: git config --global remote.origin.followRemoteHEAD never
|
|
23
|
+
shell: bash
|
|
24
|
+
run: git config --global remote.origin.followRemoteHEAD never
|
|
25
|
+
|
|
26
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
persist-credentials: false
|
|
30
|
+
|
|
31
|
+
- uses: rickstaa/action-contains-tag@a9ff27d505ba2bf074a2ebb48b208e76d35ff308 # v1.2.10
|
|
32
|
+
id: contains_tag
|
|
33
|
+
with:
|
|
34
|
+
reference: "main"
|
|
35
|
+
tag: "${{ github.ref_name }}"
|
|
36
|
+
|
|
37
|
+
unit-tests:
|
|
38
|
+
needs: on-main-branch-check
|
|
39
|
+
if: ${{ needs.on-main-branch-check.outputs.on_main == 'true' }}
|
|
40
|
+
permissions:
|
|
41
|
+
contents: read
|
|
42
|
+
uses: "./.github/workflows/tests.yml"
|
|
43
|
+
|
|
44
|
+
build-wheels:
|
|
45
|
+
needs: unit-tests
|
|
46
|
+
permissions:
|
|
47
|
+
contents: read
|
|
48
|
+
uses: "./.github/workflows/wheels.yml"
|
|
49
|
+
|
|
50
|
+
build-sdist:
|
|
51
|
+
name: build source distribution
|
|
52
|
+
needs: unit-tests
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
permissions:
|
|
55
|
+
contents: read
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
58
|
+
with:
|
|
59
|
+
fetch-depth: 0
|
|
60
|
+
persist-credentials: false
|
|
61
|
+
|
|
62
|
+
- name: Install uv
|
|
63
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
64
|
+
with:
|
|
65
|
+
version: ${{ env.UV_VERSION }}
|
|
66
|
+
python-version: "3.11"
|
|
67
|
+
enable-cache: 'false'
|
|
68
|
+
|
|
69
|
+
- name: Build package
|
|
70
|
+
run: uv build --sdist --out-dir dist
|
|
71
|
+
|
|
72
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
73
|
+
with:
|
|
74
|
+
name: multiqc-pivot-sdist
|
|
75
|
+
path: dist/*.tar.gz
|
|
76
|
+
|
|
77
|
+
publish-to-pypi:
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
needs: [build-wheels, build-sdist]
|
|
80
|
+
environment: pypi
|
|
81
|
+
permissions:
|
|
82
|
+
id-token: write
|
|
83
|
+
steps:
|
|
84
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
85
|
+
with:
|
|
86
|
+
path: packages
|
|
87
|
+
pattern: 'multiqc-pivot-*'
|
|
88
|
+
merge-multiple: true
|
|
89
|
+
|
|
90
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
91
|
+
with:
|
|
92
|
+
packages-dir: packages/
|
|
93
|
+
skip-existing: true
|
|
94
|
+
verbose: true
|
|
95
|
+
|
|
96
|
+
make-changelog:
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
needs: publish-to-pypi
|
|
99
|
+
permissions:
|
|
100
|
+
contents: read
|
|
101
|
+
outputs:
|
|
102
|
+
release_body: ${{ steps.git-cliff.outputs.content }}
|
|
103
|
+
steps:
|
|
104
|
+
- name: Checkout the Repository at the Tagged Commit
|
|
105
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
106
|
+
with:
|
|
107
|
+
fetch-depth: 0
|
|
108
|
+
ref: ${{ github.ref_name }}
|
|
109
|
+
persist-credentials: false
|
|
110
|
+
|
|
111
|
+
- name: Generate a Changelog
|
|
112
|
+
uses: orhun/git-cliff-action@3d96a18cc4ec17e9dc69ddcc424ccafaf1f78ce2 # v4.9.0
|
|
113
|
+
id: git-cliff
|
|
114
|
+
with:
|
|
115
|
+
config: pyproject.toml
|
|
116
|
+
args: --latest --verbose
|
|
117
|
+
env:
|
|
118
|
+
GITHUB_REPO: ${{ github.repository }}
|
|
119
|
+
|
|
120
|
+
make-github-release:
|
|
121
|
+
runs-on: ubuntu-latest
|
|
122
|
+
environment: github
|
|
123
|
+
permissions:
|
|
124
|
+
contents: write
|
|
125
|
+
pull-requests: read
|
|
126
|
+
needs: make-changelog
|
|
127
|
+
steps:
|
|
128
|
+
- name: Create Release
|
|
129
|
+
env:
|
|
130
|
+
GH_TOKEN: ${{ github.token }}
|
|
131
|
+
TAG: ${{ github.ref_name }}
|
|
132
|
+
NOTES: ${{ needs.make-changelog.outputs.release_body }}
|
|
133
|
+
run: gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --title "$TAG" --notes "$NOTES"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- "**"
|
|
7
|
+
tags:
|
|
8
|
+
- "!**"
|
|
9
|
+
workflow_call:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
env:
|
|
15
|
+
UV_VERSION: 0.11.16
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
unit-tests:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
matrix:
|
|
22
|
+
PYTHON_VERSION: ["3.10", "3.11", "3.12", "3.13"]
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
30
|
+
with:
|
|
31
|
+
version: ${{ env.UV_VERSION }}
|
|
32
|
+
python-version: ${{ matrix.PYTHON_VERSION }}
|
|
33
|
+
enable-cache: 'false'
|
|
34
|
+
|
|
35
|
+
- name: Test the library
|
|
36
|
+
run: |
|
|
37
|
+
uv run --locked poe check-all
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: wheels
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
workflow_call:
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
env:
|
|
12
|
+
UV_VERSION: 0.11.16
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build-wheels:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
|
+
with:
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
24
|
+
with:
|
|
25
|
+
version: ${{ env.UV_VERSION }}
|
|
26
|
+
python-version: "3.11"
|
|
27
|
+
enable-cache: 'false'
|
|
28
|
+
|
|
29
|
+
- name: Build wheels
|
|
30
|
+
run: uv build --wheel --out-dir wheelhouse
|
|
31
|
+
|
|
32
|
+
- name: Upload wheels
|
|
33
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
34
|
+
with:
|
|
35
|
+
name: multiqc-pivot-wheels
|
|
36
|
+
path: ./wheelhouse/multiqc_pivot*.whl
|
|
37
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
*.html
|
|
2
|
+
.DS_Store
|
|
3
|
+
.vscode/
|
|
4
|
+
|
|
5
|
+
# Byte-compiled / optimized / DLL files
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*$py.class
|
|
9
|
+
|
|
10
|
+
# C extensions
|
|
11
|
+
*.so
|
|
12
|
+
|
|
13
|
+
# Distribution / packaging
|
|
14
|
+
.Python
|
|
15
|
+
build/
|
|
16
|
+
develop-eggs/
|
|
17
|
+
dist/
|
|
18
|
+
downloads/
|
|
19
|
+
eggs/
|
|
20
|
+
.eggs/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py,cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
.pybuilder/
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
profile_default/
|
|
86
|
+
ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
# .python-version
|
|
92
|
+
|
|
93
|
+
# pipenv
|
|
94
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
95
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
96
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
97
|
+
# install all needed dependencies.
|
|
98
|
+
#Pipfile.lock
|
|
99
|
+
|
|
100
|
+
# poetry
|
|
101
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
102
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
103
|
+
# commonly ignored for libraries.
|
|
104
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
105
|
+
#poetry.lock
|
|
106
|
+
|
|
107
|
+
# pdm
|
|
108
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
109
|
+
#pdm.lock
|
|
110
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
111
|
+
# in version control.
|
|
112
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
113
|
+
.pdm.toml
|
|
114
|
+
|
|
115
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
116
|
+
__pypackages__/
|
|
117
|
+
|
|
118
|
+
# Celery stuff
|
|
119
|
+
celerybeat-schedule
|
|
120
|
+
celerybeat.pid
|
|
121
|
+
|
|
122
|
+
# SageMath parsed files
|
|
123
|
+
*.sage.py
|
|
124
|
+
|
|
125
|
+
# Environments
|
|
126
|
+
.env
|
|
127
|
+
.venv
|
|
128
|
+
venv/
|
|
129
|
+
env.bak/
|
|
130
|
+
venv.bak/
|
|
131
|
+
|
|
132
|
+
# Spyder project settings
|
|
133
|
+
.spyderproject
|
|
134
|
+
.spyproject
|
|
135
|
+
|
|
136
|
+
# Rope project settings
|
|
137
|
+
.ropeproject
|
|
138
|
+
|
|
139
|
+
# mkdocs documentation
|
|
140
|
+
/site
|
|
141
|
+
|
|
142
|
+
# mypy
|
|
143
|
+
.mypy_cache/
|
|
144
|
+
.dmypy.json
|
|
145
|
+
dmypy.json
|
|
146
|
+
|
|
147
|
+
# Pyre type checker
|
|
148
|
+
.pyre/
|
|
149
|
+
|
|
150
|
+
# pytype static type analyzer
|
|
151
|
+
.pytype/
|
|
152
|
+
|
|
153
|
+
# Cython debug symbols
|
|
154
|
+
cython_debug/
|
|
155
|
+
|
|
156
|
+
# PyCharm
|
|
157
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
158
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
159
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
160
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
161
|
+
.idea/
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Development and Testing
|
|
2
|
+
|
|
3
|
+
## Primary Development Commands
|
|
4
|
+
|
|
5
|
+
To check and resolve linting issues in the codebase, run:
|
|
6
|
+
|
|
7
|
+
```console
|
|
8
|
+
uv run ruff check --fix
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
To check and resolve formatting issues in the codebase, run:
|
|
12
|
+
|
|
13
|
+
```console
|
|
14
|
+
uv run ruff format
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
To check the unit tests in the codebase, run:
|
|
18
|
+
|
|
19
|
+
```console
|
|
20
|
+
uv run pytest
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
To check the typing in the codebase, run:
|
|
24
|
+
|
|
25
|
+
```console
|
|
26
|
+
uv run mypy
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To generate a code coverage report after testing locally, run:
|
|
30
|
+
|
|
31
|
+
```console
|
|
32
|
+
uv run coverage html
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To check the lock file is up to date:
|
|
36
|
+
|
|
37
|
+
```console
|
|
38
|
+
uv lock --check
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The lock file is resolved without any user-level uv configuration, which is how CI resolves it. If your `~/.config/uv/uv.toml` sets `exclude-newer` or similar, uv will want to re-resolve and will rewrite `uv.lock`. Set `UV_NO_CONFIG=1` in your shell while working here to match CI; the environment variable matters because `poe` runs each task through its own `uv run`, which a `--no-config` flag on the outer command does not reach.
|
|
42
|
+
|
|
43
|
+
## Shortcut Task Commands
|
|
44
|
+
|
|
45
|
+
###### For Running Individual Checks
|
|
46
|
+
|
|
47
|
+
```console
|
|
48
|
+
uv run poe check-format
|
|
49
|
+
uv run poe check-lint
|
|
50
|
+
uv run poe check-tests
|
|
51
|
+
uv run poe check-typing
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
###### For Running All Checks
|
|
55
|
+
|
|
56
|
+
```console
|
|
57
|
+
uv run poe check-all
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
###### For Running Individual Fixes
|
|
61
|
+
|
|
62
|
+
```console
|
|
63
|
+
uv run poe fix-format
|
|
64
|
+
uv run poe fix-lint
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
###### For Running All Fixes
|
|
68
|
+
|
|
69
|
+
```console
|
|
70
|
+
uv run poe fix-all
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
###### For Running All Fixes and Checks
|
|
74
|
+
|
|
75
|
+
```console
|
|
76
|
+
uv run poe fix-and-check-all
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Trying a change against a real report
|
|
80
|
+
|
|
81
|
+
Point MultiQC at any directory of QC outputs with a config that has a `sample_pivot` block. The
|
|
82
|
+
plugin is picked up automatically once the package is installed in the same environment:
|
|
83
|
+
|
|
84
|
+
```console
|
|
85
|
+
uv run multiqc --config my_config.yml ./path-to-data
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Creating a release on PyPI
|
|
89
|
+
|
|
90
|
+
> [!NOTE]
|
|
91
|
+
> This project follows [Semantic Versioning](https://semver.org/), aka SemVer. In brief:
|
|
92
|
+
>
|
|
93
|
+
> - MAJOR version when you make incompatible API changes
|
|
94
|
+
> - MINOR version when you add functionality in a backwards compatible manner
|
|
95
|
+
> - PATCH version when you make backwards compatible bug fixes
|
|
96
|
+
|
|
97
|
+
> [!IMPORTANT]
|
|
98
|
+
> Consider editing the changelog if there are any errors or necessary enhancements.
|
|
99
|
+
|
|
100
|
+
1. Clone the repository, ensure you are on the main branch, and that the working directory is clean.
|
|
101
|
+
2. Check out a new branch to prepare the library for release.
|
|
102
|
+
3. Bump the version of the library to the desired SemVer, e.g.:
|
|
103
|
+
```console
|
|
104
|
+
# Increment the minor segment
|
|
105
|
+
uv version --bump minor
|
|
106
|
+
|
|
107
|
+
# Update to a specific version number
|
|
108
|
+
uv version 0.2.0
|
|
109
|
+
```
|
|
110
|
+
4. Commit the version bump changes with a Git commit message like `chore(release): bump to #.#.#`.
|
|
111
|
+
5. Push the commit, open a PR, ensure tests pass, and seek reviews.
|
|
112
|
+
6. Squash merge the PR into the `main` branch.
|
|
113
|
+
7. Tag the new commit on the main branch with the bumped version number.
|
|
114
|
+
|
|
115
|
+
> [!WARNING]
|
|
116
|
+
> The tag **must** be a valid SemVer version number and **must** match the version set in (3). The [publishing GitHub Action](.github/workflows/publish_multiqc_pivot.yml) is activated by a new tag on the `main` branch containing a valid SemVer version.
|
|
117
|
+
|
|
118
|
+
GitHub Actions will take care of the remainder of the deployment and release process:
|
|
119
|
+
|
|
120
|
+
1. Unit tests will be re-run.
|
|
121
|
+
2. A source distribution will be built.
|
|
122
|
+
3. A wheel distribution will be built.
|
|
123
|
+
4. Assets will be deployed to PyPI with the new version.
|
|
124
|
+
5. A [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/)-aware changelog will be drafted.
|
|
125
|
+
6. A GitHub release will be created with the new version tag and the drafted changelog.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 Clint Valentine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: multiqc-pivot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A MultiQC plugin that folds related samples into one General Statistics row per group with labelled metric columns.
|
|
5
|
+
Project-URL: homepage, https://github.com/clintval/multiqc-pivot
|
|
6
|
+
Project-URL: repository, https://github.com/clintval/multiqc-pivot
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/clintval/multiqc-pivot/issues
|
|
8
|
+
Author-email: Clint Valentine <valentine.clint@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: multiqc<2,>=1.35
|
|
28
|
+
Requires-Dist: pydantic>=2
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# multiqc-pivot
|
|
32
|
+
|
|
33
|
+
[](https://github.com/clintval/multiqc-pivot/actions/workflows/tests.yml?query=branch%3Amain)
|
|
34
|
+
[](https://github.com/clintval/multiqc-pivot)
|
|
35
|
+
[](https://docs.basedpyright.com/latest/)
|
|
36
|
+
[](https://mypy-lang.org/)
|
|
37
|
+
[](https://docs.astral.sh/ruff/)
|
|
38
|
+
|
|
39
|
+
A [MultiQC](https://multiqc.info) plugin that folds related samples into one General Statistics row per group.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```console
|
|
44
|
+
pip install multiqc-pivot
|
|
45
|
+
```
|
|
46
|
+
## Introduction
|
|
47
|
+
|
|
48
|
+
MultiQC gives every sample its own row.
|
|
49
|
+
When one subject yields several samples that are measured by different methods, say a tumour and a normal, or two tissues and a paired-genotype check, the General Statistics table ends up with a block of half-empty rows per subject.
|
|
50
|
+
MultiQC's own [sample grouping](https://docs.seqera.io/multiqc/reports/customisation#sample-grouping) only fills the group's row for the handful of modules that know how to merge their metrics.
|
|
51
|
+
|
|
52
|
+
This plugin runs after every module has reported and rebuilds the table:
|
|
53
|
+
|
|
54
|
+
1. Rows for one group fold into a single row, and every folded column is prefixed by which method it came from.
|
|
55
|
+
2. The original rows stay beneath the group row, so they still can be viewed.
|
|
56
|
+
3. Rows for a level that does not belong in the table, such as per-library read QC, move out into their own table under General Statistics with whatever grouping they already had.
|
|
57
|
+
4. Hover text, color scales, formats and hidden-by-default state carry over from the module that produced each column.
|
|
58
|
+
|
|
59
|
+
## Example
|
|
60
|
+
|
|
61
|
+
The report below comes from the [test fixtures](tests/data/report) and the [configuration](tests/data/multiqc_config.yml) shown in the usage section:
|
|
62
|
+
|
|
63
|
+

|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
sample_pivot:
|
|
67
|
+
group: '^(?P<group>[^. ]+)\.'
|
|
68
|
+
levels:
|
|
69
|
+
- match: '\.subject$'
|
|
70
|
+
- match: '\.(?P<analyte>tissueA|tissueB)$'
|
|
71
|
+
label: '{analyte}'
|
|
72
|
+
- match: '\.(?P<analyte>tissueA|tissueB) \(filtered\)$'
|
|
73
|
+
label: '{analyte} (filtered)'
|
|
74
|
+
- match: '\.library\.'
|
|
75
|
+
table: Library statistics
|
|
76
|
+
label_order: [tissueA, tissueB, tissueB (filtered)]
|
|
77
|
+
tables:
|
|
78
|
+
Library statistics:
|
|
79
|
+
description: Per-library read QC.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
Add a `sample_pivot` block to any MultiQC config, for example with `--config my_config.yml`.
|
|
85
|
+
|
|
86
|
+
So, with these sample names:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
101.subject
|
|
90
|
+
101.tissueA
|
|
91
|
+
101.tissueB
|
|
92
|
+
101.tissueB (filtered data, though)
|
|
93
|
+
101.tissueA.library.L1
|
|
94
|
+
101.tissueA.library.L2
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This configuration produces one row named `101` carrying `Concordance`, `TissueA Median`, `TissueB Median`, `TissueB (filtered) % Aligned` and so on, and moves the library rows into a separate table:
|
|
98
|
+
|
|
99
|
+
```yaml
|
|
100
|
+
sample_pivot:
|
|
101
|
+
group: '^(?P<group>[^. ]+)\.'
|
|
102
|
+
levels:
|
|
103
|
+
- match: '\.subject$'
|
|
104
|
+
- match: '\.(?P<analyte>tissueA|tissueB)$'
|
|
105
|
+
label: '{analyte}'
|
|
106
|
+
- match: '\.(?P<analyte>tissueA|tissueB) \(filtered\)$'
|
|
107
|
+
label: '{analyte} (filtered)'
|
|
108
|
+
- match: '\.library\.'
|
|
109
|
+
table: Library statistics
|
|
110
|
+
label_order: [tissueA, tissueB, tissueB (filtered)]
|
|
111
|
+
tables:
|
|
112
|
+
Library statistics:
|
|
113
|
+
description: Per-library read QC; read pairs nest under their library.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### YAML Configuration Reference
|
|
117
|
+
|
|
118
|
+
| Key | Meaning |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| `group` | A regular expression searched in every matched sample name. Its `(?P<group>...)` capture names the folded row. Required. |
|
|
121
|
+
| `levels` | An ordered list; the first level whose `match` is found in a sample name wins. Required. |
|
|
122
|
+
| `levels[].match` | A regular expression searched in the sample name. Named captures are available to `label`. |
|
|
123
|
+
| `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. |
|
|
124
|
+
| `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. |
|
|
125
|
+
| `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}`. |
|
|
126
|
+
| `label_order` | Labels in the order their column blocks should appear. Labels not listed follow in order of first appearance. |
|
|
127
|
+
| `tables` | Presentation of the tables named by `levels[].table`, currently a `description` each. |
|
|
128
|
+
|
|
129
|
+
> [!TIP]
|
|
130
|
+
> A sample that matches no level is left where it was.
|
|
131
|
+
> A sample that matches a level but not `group` is left alone as well, with a warning in the log.
|
|
132
|
+
> Columns that a module did not declare a header for are dropped from folded rows, as MultiQC would have dropped them anyway.
|
|
133
|
+
|
|
134
|
+
### Limitations
|
|
135
|
+
|
|
136
|
+
1. Sample names are matched after MultiQC has cleaned them, so you must write patterns against the names you see in an un-pivoted report.
|
|
137
|
+
2. Only one level of nesting exists in a MultiQC table. Rows moved into a secondary table keep the nesting they already had; rows folded into a group row become its children, and cannot nest further.
|
|
138
|
+
3. Two rows in the same group that resolve to the same label collide. The first value is kept and a warning is logged, so make labels specific enough to tell such rows apart.
|
|
139
|
+
|
|
140
|
+
## Development and Testing
|
|
141
|
+
|
|
142
|
+
See the [contributing guide](./CONTRIBUTING.md) for more information.
|