From 1b6809ce30687562ed8c77e89cd628460a8ddddd Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Tue, 27 May 2025 11:59:25 +0200 Subject: [PATCH 01/12] docs: fix build badge in README --- .github/workflows/release.yml | 2 +- .github/workflows/testpypi.yml | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9345c75..3f47cab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Build and release Python package on PyPI +name: Build on: release: diff --git a/.github/workflows/testpypi.yml b/.github/workflows/testpypi.yml index b359283..9029f11 100644 --- a/.github/workflows/testpypi.yml +++ b/.github/workflows/testpypi.yml @@ -1,4 +1,4 @@ -name: Build and release Python package on TestPyPI +name: Test Build on: push: diff --git a/README.md b/README.md index 6cf1815..9a279ff 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/rmnldwg/lyscripts/blob/main/LICENSE) [![GitHub repo](https://img.shields.io/badge/rmnldwg%2Flymph-grey.svg?style=flat&logo=github)](https://github.com/rmnldwg/lyscripts) -[![build badge](https://github.com/rmnldwg/lyscripts/actions/workflows/build.yml/badge.svg?style=flat)](https://pypi.org/project/lyscripts/) +[![build badge](https://github.com/rmnldwg/lyscripts/actions/workflows/release.yml/badge.svg?style=flat)](https://pypi.org/project/lyscripts/) [![docs badge](https://readthedocs.org/projects/lyscripts/badge/?version=latest)](https://lyscripts.readthedocs.io/en/latest/?badge=latest) [![tests badge](https://github.com/rmnldwg/lyscripts/actions/workflows/tests.yml/badge.svg?style=flat)](https://lyscripts.readthedocs.io/en/latest/?badge=latest) From ba1ff6624b58610fe3b811c905811f6da4fdb5c4 Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Tue, 27 May 2025 12:03:17 +0200 Subject: [PATCH 02/12] docs: fix outdated links to rmnldwg --- .chglog/config.yml | 2 +- CHANGELOG.md | 114 +++++++++++++++++++++--------------------- README.md | 18 +++---- lyscripts/__init__.py | 2 +- lyscripts/schema.py | 6 +-- pyproject.toml | 2 +- 6 files changed, 72 insertions(+), 72 deletions(-) diff --git a/.chglog/config.yml b/.chglog/config.yml index e3b7d9d..0ea5514 100755 --- a/.chglog/config.yml +++ b/.chglog/config.yml @@ -2,7 +2,7 @@ style: github template: CHANGELOG.tpl.md info: title: CHANGELOG - repository_url: https://github.com/rmnldwg/lyscripts + repository_url: https://github.com/lycosystem/lyscripts options: commits: filters: diff --git a/CHANGELOG.md b/CHANGELOG.md index 071c764..fc5e40d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -819,63 +819,63 @@ returns `None` instead. Fixes [#11] ## [0.5.3] - 2022-08-22 -[1.0.0rc1]: https://github.com/rmnldwg/lyscripts/compare/1.0.0.a7...1.0.0rc1 -[1.0.0.a7]: https://github.com/rmnldwg/lyscripts/compare/1.0.0.a6...1.0.0.a7 -[1.0.0.a6]: https://github.com/rmnldwg/lyscripts/compare/1.0.0.a5...1.0.0.a6 -[1.0.0.a5]: https://github.com/rmnldwg/lyscripts/compare/1.0.0.a4...1.0.0.a5 -[1.0.0.a4]: https://github.com/rmnldwg/lyscripts/compare/1.0.0.a3...1.0.0.a4 -[1.0.0.a3]: https://github.com/rmnldwg/lyscripts/compare/1.0.0.a2...1.0.0.a3 -[1.0.0.a2]: https://github.com/rmnldwg/lyscripts/compare/1.0.0.a1...1.0.0.a2 -[1.0.0.a1]: https://github.com/rmnldwg/lyscripts/compare/1.0.0.a0...1.0.0.a1 -[1.0.0.a0]: https://github.com/rmnldwg/lyscripts/compare/0.7.3...1.0.0.a0 -[0.7.3]: https://github.com/rmnldwg/lyscripts/compare/0.7.2...0.7.3 -[0.7.2]: https://github.com/rmnldwg/lyscripts/compare/0.7.1...0.7.2 -[0.7.1]: https://github.com/rmnldwg/lyscripts/compare/0.7.0...0.7.1 -[0.7.0]: https://github.com/rmnldwg/lyscripts/compare/0.6.9...0.7.0 -[0.6.9]: https://github.com/rmnldwg/lyscripts/compare/0.6.8...0.6.9 -[0.6.8]: https://github.com/rmnldwg/lyscripts/compare/0.6.7...0.6.8 -[0.6.7]: https://github.com/rmnldwg/lyscripts/compare/0.6.6...0.6.7 -[0.6.6]: https://github.com/rmnldwg/lyscripts/compare/0.6.5...0.6.6 -[0.6.5]: https://github.com/rmnldwg/lyscripts/compare/0.6.4...0.6.5 -[0.6.4]: https://github.com/rmnldwg/lyscripts/compare/0.6.3...0.6.4 -[0.6.3]: https://github.com/rmnldwg/lyscripts/compare/0.6.2...0.6.3 -[0.6.2]: https://github.com/rmnldwg/lyscripts/compare/0.6.1...0.6.2 -[0.6.1]: https://github.com/rmnldwg/lyscripts/compare/0.6.0...0.6.1 -[0.6.0]: https://github.com/rmnldwg/lyscripts/compare/0.5.11...0.6.0 -[0.5.11]: https://github.com/rmnldwg/lyscripts/compare/0.5.10...0.5.11 -[0.5.10]: https://github.com/rmnldwg/lyscripts/compare/0.5.9...0.5.10 -[0.5.9]: https://github.com/rmnldwg/lyscripts/compare/0.5.8...0.5.9 -[0.5.8]: https://github.com/rmnldwg/lyscripts/compare/0.5.7...0.5.8 -[0.5.7]: https://github.com/rmnldwg/lyscripts/compare/0.5.6...0.5.7 -[0.5.6]: https://github.com/rmnldwg/lyscripts/compare/0.5.5...0.5.6 -[0.5.5]: https://github.com/rmnldwg/lyscripts/compare/0.5.4...0.5.5 -[0.5.4]: https://github.com/rmnldwg/lyscripts/compare/0.5.3...0.5.4 -[0.5.3]: https://github.com/rmnldwg/lyscripts/compare/0.5.2...0.5.3 - -[#2]: https://github.com/rmnldwg/lyscripts/issues/2 -[#5]: https://github.com/rmnldwg/lyscripts/issues/5 -[#8]: https://github.com/rmnldwg/lyscripts/issues/8 -[#11]: https://github.com/rmnldwg/lyscripts/issues/11 -[#13]: https://github.com/rmnldwg/lyscripts/issues/13 -[#15]: https://github.com/rmnldwg/lyscripts/issues/15 -[#16]: https://github.com/rmnldwg/lyscripts/issues/16 -[#17]: https://github.com/rmnldwg/lyscripts/issues/17 -[#20]: https://github.com/rmnldwg/lyscripts/issues/20 -[#21]: https://github.com/rmnldwg/lyscripts/issues/21 -[#23]: https://github.com/rmnldwg/lyscripts/issues/23 -[#25]: https://github.com/rmnldwg/lyscripts/issues/25 -[#30]: https://github.com/rmnldwg/lyscripts/issues/30 -[#31]: https://github.com/rmnldwg/lyscripts/issues/31 -[#33]: https://github.com/rmnldwg/lyscripts/issues/33 -[#40]: https://github.com/rmnldwg/lyscripts/issues/40 -[#41]: https://github.com/rmnldwg/lyscripts/issues/41 -[#44]: https://github.com/rmnldwg/lyscripts/issues/44 -[#45]: https://github.com/rmnldwg/lyscripts/issues/45 -[#51]: https://github.com/rmnldwg/lyscripts/issues/51 -[#53]: https://github.com/rmnldwg/lyscripts/issues/53 -[#54]: https://github.com/rmnldwg/lyscripts/issues/54 -[#57]: https://github.com/rmnldwg/lyscripts/issues/57 -[#70]: https://github.com/rmnldwg/lyscripts/issues/70 +[1.0.0rc1]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a7...1.0.0rc1 +[1.0.0.a7]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a6...1.0.0.a7 +[1.0.0.a6]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a5...1.0.0.a6 +[1.0.0.a5]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a4...1.0.0.a5 +[1.0.0.a4]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a3...1.0.0.a4 +[1.0.0.a3]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a2...1.0.0.a3 +[1.0.0.a2]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a1...1.0.0.a2 +[1.0.0.a1]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a0...1.0.0.a1 +[1.0.0.a0]: https://github.com/lycosystem/lyscripts/compare/0.7.3...1.0.0.a0 +[0.7.3]: https://github.com/lycosystem/lyscripts/compare/0.7.2...0.7.3 +[0.7.2]: https://github.com/lycosystem/lyscripts/compare/0.7.1...0.7.2 +[0.7.1]: https://github.com/lycosystem/lyscripts/compare/0.7.0...0.7.1 +[0.7.0]: https://github.com/lycosystem/lyscripts/compare/0.6.9...0.7.0 +[0.6.9]: https://github.com/lycosystem/lyscripts/compare/0.6.8...0.6.9 +[0.6.8]: https://github.com/lycosystem/lyscripts/compare/0.6.7...0.6.8 +[0.6.7]: https://github.com/lycosystem/lyscripts/compare/0.6.6...0.6.7 +[0.6.6]: https://github.com/lycosystem/lyscripts/compare/0.6.5...0.6.6 +[0.6.5]: https://github.com/lycosystem/lyscripts/compare/0.6.4...0.6.5 +[0.6.4]: https://github.com/lycosystem/lyscripts/compare/0.6.3...0.6.4 +[0.6.3]: https://github.com/lycosystem/lyscripts/compare/0.6.2...0.6.3 +[0.6.2]: https://github.com/lycosystem/lyscripts/compare/0.6.1...0.6.2 +[0.6.1]: https://github.com/lycosystem/lyscripts/compare/0.6.0...0.6.1 +[0.6.0]: https://github.com/lycosystem/lyscripts/compare/0.5.11...0.6.0 +[0.5.11]: https://github.com/lycosystem/lyscripts/compare/0.5.10...0.5.11 +[0.5.10]: https://github.com/lycosystem/lyscripts/compare/0.5.9...0.5.10 +[0.5.9]: https://github.com/lycosystem/lyscripts/compare/0.5.8...0.5.9 +[0.5.8]: https://github.com/lycosystem/lyscripts/compare/0.5.7...0.5.8 +[0.5.7]: https://github.com/lycosystem/lyscripts/compare/0.5.6...0.5.7 +[0.5.6]: https://github.com/lycosystem/lyscripts/compare/0.5.5...0.5.6 +[0.5.5]: https://github.com/lycosystem/lyscripts/compare/0.5.4...0.5.5 +[0.5.4]: https://github.com/lycosystem/lyscripts/compare/0.5.3...0.5.4 +[0.5.3]: https://github.com/lycosystem/lyscripts/compare/0.5.2...0.5.3 + +[#2]: https://github.com/lycosystem/lyscripts/issues/2 +[#5]: https://github.com/lycosystem/lyscripts/issues/5 +[#8]: https://github.com/lycosystem/lyscripts/issues/8 +[#11]: https://github.com/lycosystem/lyscripts/issues/11 +[#13]: https://github.com/lycosystem/lyscripts/issues/13 +[#15]: https://github.com/lycosystem/lyscripts/issues/15 +[#16]: https://github.com/lycosystem/lyscripts/issues/16 +[#17]: https://github.com/lycosystem/lyscripts/issues/17 +[#20]: https://github.com/lycosystem/lyscripts/issues/20 +[#21]: https://github.com/lycosystem/lyscripts/issues/21 +[#23]: https://github.com/lycosystem/lyscripts/issues/23 +[#25]: https://github.com/lycosystem/lyscripts/issues/25 +[#30]: https://github.com/lycosystem/lyscripts/issues/30 +[#31]: https://github.com/lycosystem/lyscripts/issues/31 +[#33]: https://github.com/lycosystem/lyscripts/issues/33 +[#40]: https://github.com/lycosystem/lyscripts/issues/40 +[#41]: https://github.com/lycosystem/lyscripts/issues/41 +[#44]: https://github.com/lycosystem/lyscripts/issues/44 +[#45]: https://github.com/lycosystem/lyscripts/issues/45 +[#51]: https://github.com/lycosystem/lyscripts/issues/51 +[#53]: https://github.com/lycosystem/lyscripts/issues/53 +[#54]: https://github.com/lycosystem/lyscripts/issues/54 +[#57]: https://github.com/lycosystem/lyscripts/issues/57 +[#70]: https://github.com/lycosystem/lyscripts/issues/70 [`emcee`]: https://emcee.readthedocs.io/en/stable/ [`rich`]: https://rich.readthedocs.io/en/latest/ diff --git a/README.md b/README.md index 9a279ff..c5e8d6f 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -social card +social card -[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/rmnldwg/lyscripts/blob/main/LICENSE) -[![GitHub repo](https://img.shields.io/badge/rmnldwg%2Flymph-grey.svg?style=flat&logo=github)](https://github.com/rmnldwg/lyscripts) -[![build badge](https://github.com/rmnldwg/lyscripts/actions/workflows/release.yml/badge.svg?style=flat)](https://pypi.org/project/lyscripts/) +[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/lycosystem/lyscripts/blob/main/LICENSE) +[![GitHub repo](https://img.shields.io/badge/lycosystem%2Flymph-grey.svg?style=flat&logo=github)](https://github.com/lycosystem/lyscripts) +[![build badge](https://github.com/lycosystem/lyscripts/actions/workflows/release.yml/badge.svg?style=flat)](https://pypi.org/project/lyscripts/) [![docs badge](https://readthedocs.org/projects/lyscripts/badge/?version=latest)](https://lyscripts.readthedocs.io/en/latest/?badge=latest) -[![tests badge](https://github.com/rmnldwg/lyscripts/actions/workflows/tests.yml/badge.svg?style=flat)](https://lyscripts.readthedocs.io/en/latest/?badge=latest) +[![tests badge](https://github.com/lycosystem/lyscripts/actions/workflows/tests.yml/badge.svg?style=flat)](https://lyscripts.readthedocs.io/en/latest/?badge=latest) ## What are these `lyscripts`? -This package provides convenient scripts for performing inference and learning regarding the lymphatic spread of head & neck cancer. Essentially, it provides a *command line interface* (CLI) to the [lymph](https://github.com/rmnldwg/lymph) library and the [lydata](https://github.com/rmnldwg/lydata) repository that stores lymphatic progression data. +This package provides convenient scripts for performing inference and learning regarding the lymphatic spread of head & neck cancer. Essentially, it provides a *command line interface* (CLI) to the [lymph](https://github.com/lycosystem/lymph) library and the [lydata](https://github.com/rmnldwg/lydata) repository that stores lymphatic progression data. -We are making these "convenience" scripts public, because doing so is one necessary requirement to making our research easily and fully reproducible. There exists another repository, [lynference](https://github.com/rmnldwg/lynference), where we stored the pipelines that produced our published results in a persistent way. +We are making these "convenience" scripts public, because doing so is one necessary requirement to making our research easily and fully reproducible. There exists another repository, [lynference](https://github.com/lycosystem/lynference), where we stored the pipelines that produced our published results in a persistent way. ## Installation @@ -23,7 +23,7 @@ pip install lyscripts or installed from source by cloning this repo ```bash -git clone https://github.com/rmnldwg/lyscripts.git +git clone https://github.com/lycosystem/lyscripts.git cd lyscripts pip install . ``` @@ -59,7 +59,7 @@ For these YAML files we provide a unified schema containing all possible fields ```json { "yaml.schemas": { - "https://raw.githubusercontent.com/rmnldwg/lyscripts/main/schemas/ly.json": "*.ly.yaml" + "https://raw.githubusercontent.com/lycosystem/lyscripts/main/schemas/ly.json": "*.ly.yaml" } } ``` diff --git a/lyscripts/__init__.py b/lyscripts/__init__.py index 0de3d58..f6b7de8 100644 --- a/lyscripts/__init__.py +++ b/lyscripts/__init__.py @@ -26,7 +26,7 @@ __description__ = "Package to interact with lymphatic progression data and models." __author__ = "Roman Ludwig" __email__ = "roman.ludwig@usz.ch" -__uri__ = "https://github.com/rmnldwg/lyscripts" +__uri__ = "https://github.com/lycosystem/lyscripts" # activate copy on write in pandas. # See https://pandas.pydata.org/docs/user_guide/copy_on_write.html diff --git a/lyscripts/schema.py b/lyscripts/schema.py index 4776afb..cab7c68 100644 --- a/lyscripts/schema.py +++ b/lyscripts/schema.py @@ -13,15 +13,15 @@ { "yaml.schemas": { - "https://raw.githubusercontent.com/rmnldwg/lyscripts/main/schemas/ly.json": "*.ly.yaml" + "https://raw.githubusercontent.com/lycosystem/lyscripts/main/schemas/ly.json": "*.ly.yaml" }, } Which would enable auto-completion and validation for all files with the extension ``.ly.yaml`` in the workspace. -.. _source code repository: https://github.com/rmnldwg/lyscripts -.. _URL for the schema: https://raw.githubusercontent.com/rmnldwg/lyscripts/main/schemas/ly.json +.. _source code repository: https://github.com/lycosystem/lyscripts +.. _URL for the schema: https://raw.githubusercontent.com/lycosystem/lyscripts/main/schemas/ly.json """ # noqa: E501 import json diff --git a/pyproject.toml b/pyproject.toml index c3ecdfb..38f67e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ dependencies = [ dynamic = ["version"] [project.urls] -source = "https://github.com/rmnldwg/lyscripts" +source = "https://github.com/lycosystem/lyscripts" documentation = "https://lyscripts.readthedocs.io" [project.optional-dependencies] From a1ec2bef2ded6a83e38c9a500d111a1d8465ce72 Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Wed, 18 Jun 2025 10:56:37 +0200 Subject: [PATCH 03/12] chore: update pre-commit & ruff rules --- .pre-commit-config.yaml | 11 ++++++++--- pyproject.toml | 8 ++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f6f627a..3b059e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,22 @@ +default_install_hook_types: [pre-commit, commit-msg] + repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer + - id: check-toml + - id: check-yaml + - id: check-json - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.4 + rev: v0.12.0 hooks: - id: ruff args: [ --fix ] - id: ruff-format - repo: https://github.com/compilerla/conventional-pre-commit - rev: v3.3.0 + rev: v4.2.0 hooks: - id: conventional-pre-commit stages: [commit-msg] diff --git a/pyproject.toml b/pyproject.toml index 38f67e8..1ea5847 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,6 +72,10 @@ test = [ "pytest", "pytest-mpl", ] +dev = [ + "pre-commit", + "git-cliff", +] [project.scripts] lyscripts = "lyscripts:main" @@ -90,12 +94,12 @@ version = {attr = "lyscripts._version.version"} testpaths = "." [tool.ruff.lint] -select = ["E", "F", "W", "B", "C", "R", "U", "D", "I", "S", "T", "A", "N"] +select = ["E", "F", "W", "B", "C", "R", "U", "D", "I", "S", "T", "A", "N", "COM", "FURB", "NPY", "UP"] ignore = ["D409"] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["E402"] -"**/{tests,docs}/*" = [ +"{tests,docs}/*" = [ "D103", "E402", "S101", From 9ed130c1b9f69a1d14f0af59cae2e397f517f1d7 Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:53:39 +0200 Subject: [PATCH 04/12] test: fix the dataset used for testing prevalences --- tests/compute/prevalences_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compute/prevalences_test.py b/tests/compute/prevalences_test.py index 7ccdba3..471b78d 100644 --- a/tests/compute/prevalences_test.py +++ b/tests/compute/prevalences_test.py @@ -23,7 +23,7 @@ def scenario_config() -> ScenarioConfig: @pytest.fixture def data() -> pd.DataFrame: """Load one of the lyDATA datasets.""" - data = next(load_datasets()) + data = next(load_datasets(year=2021, institution="usz")) return infer_and_combine_levels(data) From a89a8f15605e42b19987ccc29fbc5808f25f1eb2 Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:55:00 +0200 Subject: [PATCH 05/12] fix: divide by midext prevalence This fixes a bug I reintroduced bug where I didn't compute observed and model prevalence in an analogous and comparable way. Fixes: #72 --- lyscripts/compute/prevalences.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lyscripts/compute/prevalences.py b/lyscripts/compute/prevalences.py index 306013e..21c2548 100644 --- a/lyscripts/compute/prevalences.py +++ b/lyscripts/compute/prevalences.py @@ -78,13 +78,27 @@ def compute_prevalences( if isinstance(model, models.Unilateral | models.HPVUnilateral): involvement = involvement.get("ipsi") - prevalences.append( - model.marginalize( + prevalence = model.marginalize( + given_state_dist=obs_dist, + involvement=involvement, + **kwargs, + ) + + if isinstance(model, models.Midline): + # In this case, we need to renormalize the prevalence by the marginalized + # probability of all states with midline extension. We must do this, because + # we compute the analogous quantity for the data. In principle, we could + # also compute the prevalence of the diagnosis *and* midline extension, but + # we have decided to compute the diagnosis *given* midline extension. + # https://github.com/lycosystem/lyscripts/blob/ea49ec/lyscripts/compute/prevalences.py#L217-L225 + midext_prob = model.marginalize( + involvement=None, given_state_dist=obs_dist, - involvement=involvement, **kwargs, ) - ) + prevalence /= midext_prob + + prevalences.append(prevalence) return np.stack(prevalences) @@ -132,6 +146,12 @@ def observe_prevalence( has_midext = NoneQ() else: has_midext = C("midext") == scenario_config.midext + + # Note that below we compute the prevalence of the diagnosis *given* midline + # extension. This means, that when computing the prevalence of the diagnosis in + # the model, we need to renormalize by diving by the probability of midline + # extension. For an older - but pretty surely correct - implementation see + # https://github.com/lycosystem/lyscripts/blob/ea49ec/lyscripts/compute/prevalences.py#L217-L225 return data.ly.portion( query=generate_query_from_diagnosis(scenario_config.diagnosis), given=has_t_stage & has_midext, From ce620f13e65f94d0fb15f9a90b41edfb542ffe1f Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:02:26 +0200 Subject: [PATCH 06/12] ci: use tests action with coverage --- .github/workflows/tests.yml | 66 +++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 75e34fd..24f4097 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,38 +1,56 @@ -# This is a workflow that runs test on the package -name: Tests +name: tests -# Controls when the action will run. on: - # Triggers the workflow on push or pull request events but only for the main branch push: branches: [ main ] pull_request: branches: [ main ] - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: -# A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on + test: + name: Run tests & report coverage runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job + permissions: + pull-requests: write + contents: write steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Install python 3 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 with: - python-version: '3.10' - - name: install dependencies - run: | - python3 -m pip install -U pip setuptools setuptools-scm wheel - python3 -m pip install .[test] - - name: Run tests + python-version: "3.10" + + - name: Install dependencies run: | - python3 -m pytest --doctest-modules + python -m pip install --upgrade pip + python -m pip install .[tests] + + # Below, we first run pytest in the `tests/` folder. Because we use a `src` + # layout, this will fail if the package is not installed correctly. + - name: Test package is installable + run: pytest --cov=lydata --cov-config=pyproject.toml tests + env: + COVERAGE_FILE: .coverage.is_installable + + # Now, we execute all doctests in the `src` tree. This will NOT run with + # the installed code, but it doesn't matter, because we already know it is + # installable from the step above. + - name: Run doctests + if: success() || failure() # run these even if previous step fails + run: pytest --cov=lydata --cov-config=pyproject.toml --doctest-modules src + env: + COVERAGE_FILE: .coverage.doctests + GITHUB_TOKEN: ${{ secrets.LYCOSYSTEM_READALL }} + + # Lastly, we collect all files that start with `.coverage` into one file and + # create a report either as a comment on the PR or in a separate branch if its + # a commit to the main branch. From that branch we can put badges and coverage + # reports into e.g. our main README.md + - name: Add coverage comment + if: success() || failure() # run these even if previous step fails + uses: py-cov-action/python-coverage-comment-action@v3 + with: + GITHUB_TOKEN: ${{ github.token }} + MERGE_COVERAGE_FILES: true From dfc8e41edf49c96655b602f493b696284089c151 Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:09:05 +0200 Subject: [PATCH 07/12] build: switch to `src` layout Fixes: #74 --- pyproject.toml | 7 ++---- {lyscripts => src/lyscripts}/__init__.py | 0 {lyscripts => src/lyscripts}/__main__.py | 0 {lyscripts => src/lyscripts}/cli.py | 0 .../lyscripts}/compute/__init__.py | 0 .../lyscripts}/compute/__main__.py | 0 .../lyscripts}/compute/posteriors.py | 4 ++-- .../lyscripts}/compute/prevalences.py | 0 .../lyscripts}/compute/priors.py | 2 +- {lyscripts => src/lyscripts}/compute/risks.py | 2 +- {lyscripts => src/lyscripts}/compute/utils.py | 2 +- {lyscripts => src/lyscripts}/configs.py | 24 ++++++++++++------- {lyscripts => src/lyscripts}/data/__init__.py | 6 +++-- {lyscripts => src/lyscripts}/data/__main__.py | 7 ++++-- {lyscripts => src/lyscripts}/data/enhance.py | 0 {lyscripts => src/lyscripts}/data/filter.py | 6 ++--- {lyscripts => src/lyscripts}/data/generate.py | 2 +- {lyscripts => src/lyscripts}/data/join.py | 0 .../lyscripts}/data/lyproxify.py | 8 +++---- {lyscripts => src/lyscripts}/data/split.py | 0 {lyscripts => src/lyscripts}/data/utils.py | 0 {lyscripts => src/lyscripts}/decorators.py | 0 {lyscripts => src/lyscripts}/evaluate.py | 24 +++++++++++++------ {lyscripts => src/lyscripts}/plots.py | 2 +- {lyscripts => src/lyscripts}/sample.py | 6 ++--- {lyscripts => src/lyscripts}/schedule.py | 0 {lyscripts => src/lyscripts}/schema.py | 0 {lyscripts => src/lyscripts}/utils.py | 0 28 files changed, 60 insertions(+), 42 deletions(-) rename {lyscripts => src/lyscripts}/__init__.py (100%) rename {lyscripts => src/lyscripts}/__main__.py (100%) rename {lyscripts => src/lyscripts}/cli.py (100%) rename {lyscripts => src/lyscripts}/compute/__init__.py (100%) rename {lyscripts => src/lyscripts}/compute/__main__.py (100%) rename {lyscripts => src/lyscripts}/compute/posteriors.py (98%) rename {lyscripts => src/lyscripts}/compute/prevalences.py (100%) rename {lyscripts => src/lyscripts}/compute/priors.py (99%) rename {lyscripts => src/lyscripts}/compute/risks.py (99%) rename {lyscripts => src/lyscripts}/compute/utils.py (99%) rename {lyscripts => src/lyscripts}/configs.py (97%) rename {lyscripts => src/lyscripts}/data/__init__.py (90%) rename {lyscripts => src/lyscripts}/data/__main__.py (80%) rename {lyscripts => src/lyscripts}/data/enhance.py (100%) rename {lyscripts => src/lyscripts}/data/filter.py (96%) rename {lyscripts => src/lyscripts}/data/generate.py (99%) rename {lyscripts => src/lyscripts}/data/join.py (100%) rename {lyscripts => src/lyscripts}/data/lyproxify.py (99%) rename {lyscripts => src/lyscripts}/data/split.py (100%) rename {lyscripts => src/lyscripts}/data/utils.py (100%) rename {lyscripts => src/lyscripts}/decorators.py (100%) rename {lyscripts => src/lyscripts}/evaluate.py (92%) rename {lyscripts => src/lyscripts}/plots.py (99%) rename {lyscripts => src/lyscripts}/sample.py (99%) rename {lyscripts => src/lyscripts}/schedule.py (100%) rename {lyscripts => src/lyscripts}/schema.py (100%) rename {lyscripts => src/lyscripts}/utils.py (100%) diff --git a/pyproject.toml b/pyproject.toml index 1ea5847..b67f485 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,15 +81,12 @@ dev = [ lyscripts = "lyscripts:main" [tool.setuptools.packages.find] -include = ["lyscripts"] +where = ["src"] [tool.setuptools_scm] -write_to = "lyscripts/_version.py" +write_to = "src/lyscripts/_version.py" local_scheme = "no-local-version" -[tool.setuptools.dynamic] -version = {attr = "lyscripts._version.version"} - [tool.pytest.ini_options] testpaths = "." diff --git a/lyscripts/__init__.py b/src/lyscripts/__init__.py similarity index 100% rename from lyscripts/__init__.py rename to src/lyscripts/__init__.py diff --git a/lyscripts/__main__.py b/src/lyscripts/__main__.py similarity index 100% rename from lyscripts/__main__.py rename to src/lyscripts/__main__.py diff --git a/lyscripts/cli.py b/src/lyscripts/cli.py similarity index 100% rename from lyscripts/cli.py rename to src/lyscripts/cli.py diff --git a/lyscripts/compute/__init__.py b/src/lyscripts/compute/__init__.py similarity index 100% rename from lyscripts/compute/__init__.py rename to src/lyscripts/compute/__init__.py diff --git a/lyscripts/compute/__main__.py b/src/lyscripts/compute/__main__.py similarity index 100% rename from lyscripts/compute/__main__.py rename to src/lyscripts/compute/__main__.py diff --git a/lyscripts/compute/posteriors.py b/src/lyscripts/compute/posteriors.py similarity index 98% rename from lyscripts/compute/posteriors.py rename to src/lyscripts/compute/posteriors.py index c52589a..c017ccd 100644 --- a/lyscripts/compute/posteriors.py +++ b/src/lyscripts/compute/posteriors.py @@ -69,7 +69,7 @@ def compute_posteriors( given_diagnosis=diagnosis, mode=mode, **kwargs, - ) + ), ) return np.stack(posteriors) @@ -85,7 +85,7 @@ class PosteriorsCLI(BaseComputeCLI): ), ) posteriors: HDF5FileStorage = Field( - description="Storage for the computed posteriors." + description="Storage for the computed posteriors.", ) def cli_cmd(self) -> None: diff --git a/lyscripts/compute/prevalences.py b/src/lyscripts/compute/prevalences.py similarity index 100% rename from lyscripts/compute/prevalences.py rename to src/lyscripts/compute/prevalences.py diff --git a/lyscripts/compute/priors.py b/src/lyscripts/compute/priors.py similarity index 99% rename from lyscripts/compute/priors.py rename to src/lyscripts/compute/priors.py index 7b668f8..ae7307e 100644 --- a/lyscripts/compute/priors.py +++ b/src/lyscripts/compute/priors.py @@ -54,7 +54,7 @@ def compute_priors( sum( model.state_dist(t_stage=t, mode=mode) * p for t, p in zip(t_stages, t_stages_dist, strict=False) - ) + ), ) return np.stack(priors) diff --git a/lyscripts/compute/risks.py b/src/lyscripts/compute/risks.py similarity index 99% rename from lyscripts/compute/risks.py rename to src/lyscripts/compute/risks.py index d12455a..4b3e224 100644 --- a/lyscripts/compute/risks.py +++ b/src/lyscripts/compute/risks.py @@ -59,7 +59,7 @@ def compute_risks( console=console, ): risks.append( - model.marginalize(involvement=involvement, given_state_dist=posterior) + model.marginalize(involvement=involvement, given_state_dist=posterior), ) return np.stack(risks) diff --git a/lyscripts/compute/utils.py b/src/lyscripts/compute/utils.py similarity index 99% rename from lyscripts/compute/utils.py rename to src/lyscripts/compute/utils.py index d8d25c4..b3bed8a 100644 --- a/lyscripts/compute/utils.py +++ b/src/lyscripts/compute/utils.py @@ -116,7 +116,7 @@ class HDF5FileStorage(BaseModel): """HDF5 file storage for in- and outputs of computations.""" file: HasParentPath = Field( - description="Path to the HDF5 file. Parent directories are created if needed." + description="Path to the HDF5 file. Parent directories are created if needed.", ) dataset: str | None = Field( default=None, diff --git a/lyscripts/configs.py b/src/lyscripts/configs.py similarity index 97% rename from lyscripts/configs.py rename to src/lyscripts/configs.py index 11f0a50..f76726e 100644 --- a/lyscripts/configs.py +++ b/src/lyscripts/configs.py @@ -73,7 +73,7 @@ class DataConfig(BaseModel): description=( "Either a path to a CSV file or a config that specifies how and where " "to fetch the data from." - ) + ), ) side: Literal["ipsi", "contra"] | None = Field( default=None, @@ -129,14 +129,16 @@ class DistributionConfig(BaseModel): """Configuration defining a distribution over diagnose times.""" kind: Literal["frozen", "parametric"] = Field( - default="frozen", description="Parametric distributions may be updated." + default="frozen", + description="Parametric distributions may be updated.", ) func: FuncNameType = Field( default="binomial", description="Name of predefined function to use as distribution.", ) params: dict[str, int | float] = Field( - default={}, description="Parameters to pass to the predefined function." + default={}, + description="Parameters to pass to the predefined function.", ) @@ -228,14 +230,16 @@ class ModelConfig(BaseModel): description="Path to a Python file that defines a model.", ) class_name: Literal["Unilateral", "Bilateral", "Midline"] = Field( - default="Unilateral", description="Name of the model class to use." + default="Unilateral", + description="Name of the model class to use.", ) constructor: Literal["binary", "trinary"] = Field( default="binary", description="Trinary models differentiate btw. micro- and macroscopic disease.", ) max_time: int = Field( - default=10, description="Max. number of time-steps to evolve the model over." + default=10, + description="Max. number of time-steps to evolve the model over.", ) named_params: Sequence[str] = Field( default=None, @@ -387,7 +391,7 @@ class SamplingConfig(BaseModel): """Settings to configure the MCMC sampling.""" storage_file: Path = Field( - description="Path to HDF5 file store results or load last state." + description="Path to HDF5 file store results or load last state.", ) history_file: Path | None = Field( default=None, @@ -433,7 +437,8 @@ class SamplingConfig(BaseModel): description=("Number of steps to take in the MCMC sampling."), ) thin_by: int = Field( - default=10, description="How many samples to draw before for saving one." + default=10, + description="How many samples to draw before for saving one.", ) inverse_temp: float = Field( default=1.0, @@ -674,14 +679,15 @@ def _read_file(self, file_path: Path) -> dict[str, Any]: if data.get("version") != 1: raise ValueError( f"Config file {file_path} does not have a 'version: 1' key. " - "For compatibility reasons, all config files must have this key." + "For compatibility reasons, all config files must have this key.", ) return data def __call__(self) -> dict[str, Any]: """Reload the config files from the paths in the current state.""" yaml_file_to_reload = self.current_state.get( - self.yaml_file_path_field, self.yaml_file_path + self.yaml_file_path_field, + self.yaml_file_path, ) logger.debug(f"Reloading YAML files from {yaml_file_to_reload} (if it exists).") self.__init__( diff --git a/lyscripts/data/__init__.py b/src/lyscripts/data/__init__.py similarity index 90% rename from lyscripts/data/__init__.py rename to src/lyscripts/data/__init__.py index 42f0e0e..7e4f54f 100644 --- a/lyscripts/data/__init__.py +++ b/src/lyscripts/data/__init__.py @@ -18,13 +18,15 @@ from lyscripts.data import ( # noqa: F401 enhance, - filter, generate, join, lyproxify, split, ) +# Avoid conflict with built-in `filter` function +from lyscripts.data import filter as filter_ + class DataCLI(BaseSettings): """Work with lymphatic progression data through this CLI.""" @@ -32,7 +34,7 @@ class DataCLI(BaseSettings): lyproxify: CliSubCommand[lyproxify.LyproxifyCLI] join: CliSubCommand[join.JoinCLI] split: CliSubCommand[split.SplitCLI] - filter: CliSubCommand[filter.FilterCLI] + filter: CliSubCommand[filter_.FilterCLI] enhance: CliSubCommand[enhance.EnhanceCLI] generate: CliSubCommand[generate.GenerateCLI] diff --git a/lyscripts/data/__main__.py b/src/lyscripts/data/__main__.py similarity index 80% rename from lyscripts/data/__main__.py rename to src/lyscripts/data/__main__.py index 8d270aa..a78ddd1 100644 --- a/lyscripts/data/__main__.py +++ b/src/lyscripts/data/__main__.py @@ -4,7 +4,10 @@ from lyscripts import exit_cli from lyscripts.cli import RichDefaultHelpFormatter -from lyscripts.data import enhance, filter, generate, join, split +from lyscripts.data import enhance, generate, join, split + +# Avoid conflict with built-in `filter` function +from lyscripts.data import filter as filter_ def main(args: argparse.Namespace): @@ -23,7 +26,7 @@ def main(args: argparse.Namespace): generate._add_parser(subparsers, help_formatter=parser.formatter_class) join._add_parser(subparsers, help_formatter=parser.formatter_class) split._add_parser(subparsers, help_formatter=parser.formatter_class) - filter._add_parser(subparsers, help_formatter=parser.formatter_class) + filter_._add_parser(subparsers, help_formatter=parser.formatter_class) args = parser.parse_args() args.run_main(args, parser) diff --git a/lyscripts/data/enhance.py b/src/lyscripts/data/enhance.py similarity index 100% rename from lyscripts/data/enhance.py rename to src/lyscripts/data/enhance.py diff --git a/lyscripts/data/filter.py b/src/lyscripts/data/filter.py similarity index 96% rename from lyscripts/data/filter.py rename to src/lyscripts/data/filter.py index 73115aa..bf64566 100644 --- a/lyscripts/data/filter.py +++ b/src/lyscripts/data/filter.py @@ -30,10 +30,10 @@ class FilterCLI(BaseCLI): "The column to filter by. May be a tuple of three strings, since data " "has a three-level header. If it is only one string, the lydata package " "tries to map that to a three-level header." - ) + ), ) operator: Literal["==", "!=", ">", "<", ">=", "<=", "in", "contains"] = Field( - description="The operator to use for comparison." + description="The operator to use for comparison.", ) value: float | int | str = Field(description="The value to compare against.") output_file: Path = Field(description="The path to save the filtered dataset to.") @@ -48,7 +48,7 @@ def model_post_init(self, __context): else: raise ValueError( "The column attribute must be an iterable of three strings or a " - f"single string, but it is {self.column}." + f"single string, but it is {self.column}.", ) try: diff --git a/lyscripts/data/generate.py b/src/lyscripts/data/generate.py similarity index 99% rename from lyscripts/data/generate.py rename to src/lyscripts/data/generate.py index 486a9ba..8dadf03 100644 --- a/lyscripts/data/generate.py +++ b/src/lyscripts/data/generate.py @@ -42,7 +42,7 @@ class GenerateCLI(BaseCLI): description=( "Specify what fraction of generated patients should come from the " "respective T-Stage." - ) + ), ) modalities: dict[str, ModalityConfig] params: dict[str, float] diff --git a/lyscripts/data/join.py b/src/lyscripts/data/join.py similarity index 100% rename from lyscripts/data/join.py rename to src/lyscripts/data/join.py diff --git a/lyscripts/data/lyproxify.py b/src/lyscripts/data/lyproxify.py similarity index 99% rename from lyscripts/data/lyproxify.py rename to src/lyscripts/data/lyproxify.py index 63bebc3..02154f8 100644 --- a/lyscripts/data/lyproxify.py +++ b/src/lyscripts/data/lyproxify.py @@ -60,7 +60,7 @@ class LyproxifyCLI(BaseCLI): description=( "Location of Python file containing a `COLUMN_MAP` dictionary. It may also " "contain an `EXCLUDE` list of tuples `(column, check)` to exclude patients." - ) + ), ) drop_rows: list[int] = Field( default=[], @@ -165,7 +165,7 @@ def get_instruction_depth(nested_column_map: dict[tuple, dict[str, Any]]) -> int return 1 + get_instruction_depth(value) raise ValueError( - "Leaf of column map must be a dictionary with 'func' or 'default' key." + "Leaf of column map must be a dictionary with 'func' or 'default' key.", ) raise ValueError("Empty column map.") @@ -268,12 +268,12 @@ def transform_to_lyprox( ] except Exception as exc: raise ParsingError( - f"Exception encountered while parsing column {multi_idx_col}" + f"Exception encountered while parsing column {multi_idx_col}", ) from exc else: raise ParsingError( f"Column {multi_idx_col} has neither a `default` value nor `func` " - "describing how to fill this column." + "describing how to fill this column.", ) logger.info("Transformed raw data to LyProX format.") diff --git a/lyscripts/data/split.py b/src/lyscripts/data/split.py similarity index 100% rename from lyscripts/data/split.py rename to src/lyscripts/data/split.py diff --git a/lyscripts/data/utils.py b/src/lyscripts/data/utils.py similarity index 100% rename from lyscripts/data/utils.py rename to src/lyscripts/data/utils.py diff --git a/lyscripts/decorators.py b/src/lyscripts/decorators.py similarity index 100% rename from lyscripts/decorators.py rename to src/lyscripts/decorators.py diff --git a/lyscripts/evaluate.py b/src/lyscripts/evaluate.py similarity index 92% rename from lyscripts/evaluate.py rename to src/lyscripts/evaluate.py index 5f7f15e..d09c5dd 100644 --- a/lyscripts/evaluate.py +++ b/src/lyscripts/evaluate.py @@ -18,6 +18,8 @@ from lyscripts.utils import load_patient_data, load_yaml_params +RNG = np.random.default_rng() + def _add_parser( subparsers: argparse._SubParsersAction, @@ -39,7 +41,9 @@ def _add_arguments(parser: argparse.ArgumentParser): This is called by the parent module that is called via the command line. """ parser.add_argument( - "data", type=Path, help="Path to the tables of patient data (CSV)." + "data", + type=Path, + help="Path to the tables of patient data (CSV).", ) parser.add_argument("model", type=Path, help="Path to model output files (HDF5).") @@ -51,10 +55,16 @@ def _add_arguments(parser: argparse.ArgumentParser): help="Path to parameter file", ) parser.add_argument( - "--plots", default="./plots", type=Path, help="Directory for storing plots" + "--plots", + default="./plots", + type=Path, + help="Directory for storing plots", ) parser.add_argument( - "--metrics", default="./metrics.json", type=Path, help="Path to metrics file" + "--metrics", + default="./metrics.json", + type=Path, + help="Path to metrics file", ) parser.set_defaults(run_main=main) @@ -93,7 +103,7 @@ def compute_evidence( """ integrals = np.zeros(shape=num) for i in range(num): - rand_idx = np.random.choice(log_probs.shape[1], size=log_probs.shape[0]) + rand_idx = RNG.choice(log_probs.shape[1], size=log_probs.shape[0]) drawn_accuracy = log_probs[np.arange(log_probs.shape[0]), rand_idx].copy() integrals[i] = trapezoid(y=drawn_accuracy, x=temp_schedule) return np.mean(integrals), np.std(integrals) @@ -113,7 +123,7 @@ def compute_ti_results( if num_temps != len(h5_file["ti"]): raise RuntimeError( f"Parameters suggest temp schedule of length {num_temps}, " - f"but stored are {len(h5_file['ti'])}" + f"but stored are {len(h5_file['ti'])}", ) nwalker = ndim * params["sampling"]["walkers_per_dim"] @@ -152,7 +162,7 @@ def main(args: argparse.Namespace): ) logger.info( "Computed results of thermodynamic integration with " - f"{len(temp_schedule)} steps" + f"{len(temp_schedule)} steps", ) # store inverse temperatures and log-probs in CSV file @@ -164,7 +174,7 @@ def main(args: argparse.Namespace): temp_schedule, np.mean(ti_log_probs, axis=1), np.std(ti_log_probs, axis=1), - ] + ], ).T, columns=["β", "accuracy", "std"], ) diff --git a/lyscripts/plots.py b/src/lyscripts/plots.py similarity index 99% rename from lyscripts/plots.py rename to src/lyscripts/plots.py index 2b31313..07335b0 100644 --- a/lyscripts/plots.py +++ b/src/lyscripts/plots.py @@ -184,7 +184,7 @@ def from_hdf5( num_total = int(dataset.attrs["num_total"]) except KeyError as key_err: raise KeyError( - "Dataset does not contain observed prevalence data" + "Dataset does not contain observed prevalence data", ) from key_err return cls( diff --git a/lyscripts/sample.py b/src/lyscripts/sample.py similarity index 99% rename from lyscripts/sample.py rename to src/lyscripts/sample.py index 480499e..d154dfb 100644 --- a/lyscripts/sample.py +++ b/src/lyscripts/sample.py @@ -141,10 +141,10 @@ def ensure_initial_state(sampler: emcee.EnsembleSampler) -> np.ndarray: state = sampler.backend.get_last_sample() logger.info( f"Resuming from {sampler.backend.filename} with {sampler.iteration} " - "stored iterations." + "stored iterations.", ) except AttributeError: - state = np.random.uniform(size=(sampler.nwalkers, sampler.ndim)) + state = np.random.uniform(size=(sampler.nwalkers, sampler.ndim)) # noqa: NPY002 logger.debug(f"No stored samples found. Starting from random state {state}.") return state @@ -395,7 +395,7 @@ def cli_cmd(self) -> None: ndim = MODEL.get_num_dims() # emcee does not support numpy's new random number generator yet. - np.random.seed(self.sampling.seed) + np.random.seed(self.sampling.seed) # noqa: NPY002 with get_pool(self.sampling.cores) as pool: sampler = init_sampler(settings=self, ndim=ndim, pool=pool) diff --git a/lyscripts/schedule.py b/src/lyscripts/schedule.py similarity index 100% rename from lyscripts/schedule.py rename to src/lyscripts/schedule.py diff --git a/lyscripts/schema.py b/src/lyscripts/schema.py similarity index 100% rename from lyscripts/schema.py rename to src/lyscripts/schema.py diff --git a/lyscripts/utils.py b/src/lyscripts/utils.py similarity index 100% rename from lyscripts/utils.py rename to src/lyscripts/utils.py From 88c7f1203e845dc3a988752a4667867b97c7912a Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:18:37 +0200 Subject: [PATCH 08/12] chore: update changelog --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc5e40d..abcb7d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,35 @@ All notable changes to this project will be documented in this file. +## [1.0.0rc2] - 2025-06-26 + +### Bug Fixes + +- Divide by midext prevalence. Fixes [#72].\ + This fixes a bug we reintroduced where we didn't compute observed and + model prevalence in an analogous and comparable way. + +### Documentation + +- Fix build badge in README. +- Fix outdated links to rmnldwg. + +### Miscellaneous Tasks + +- Update pre-commit & ruff rules. + +### Testing + +- Fix the dataset used for testing prevalences. + +### Build + +- Switch to `src` layout. Fixes [#74]. + +### Ci + +- Use tests action with coverage. + ## [1.0.0rc1] - 2025-05-27 ### Bug Fixes @@ -819,6 +848,7 @@ returns `None` instead. Fixes [#11] ## [0.5.3] - 2022-08-22 +[1.0.0rc2]: https://github.com/lycosystem/lyscripts/compare/1.0.0rc1...1.0.0rc2 [1.0.0rc1]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a7...1.0.0rc1 [1.0.0.a7]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a6...1.0.0.a7 [1.0.0.a6]: https://github.com/lycosystem/lyscripts/compare/1.0.0.a5...1.0.0.a6 @@ -876,6 +906,8 @@ returns `None` instead. Fixes [#11] [#54]: https://github.com/lycosystem/lyscripts/issues/54 [#57]: https://github.com/lycosystem/lyscripts/issues/57 [#70]: https://github.com/lycosystem/lyscripts/issues/70 +[#72]: https://github.com/lycosystem/lyscripts/issues/72 +[#74]: https://github.com/lycosystem/lyscripts/issues/74 [`emcee`]: https://emcee.readthedocs.io/en/stable/ [`rich`]: https://rich.readthedocs.io/en/latest/ From ecb8b670d9e718e3eb62aefc08689be3e28f843d Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:20:08 +0200 Subject: [PATCH 09/12] docs: add coverage badge to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c5e8d6f..e757f19 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![build badge](https://github.com/lycosystem/lyscripts/actions/workflows/release.yml/badge.svg?style=flat)](https://pypi.org/project/lyscripts/) [![docs badge](https://readthedocs.org/projects/lyscripts/badge/?version=latest)](https://lyscripts.readthedocs.io/en/latest/?badge=latest) [![tests badge](https://github.com/lycosystem/lyscripts/actions/workflows/tests.yml/badge.svg?style=flat)](https://lyscripts.readthedocs.io/en/latest/?badge=latest) +[![Coverage badge](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/lycosystem/lyscripts/python-coverage-comment-action-data/endpoint.json)](https://htmlpreview.github.io/?https://github.com/lycosystem/lyscripts/blob/python-coverage-comment-action-data/htmlcov/index.html) ## What are these `lyscripts`? From bc104bc8a1c2b6da8bd221784f14d672ed601400 Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:27:56 +0200 Subject: [PATCH 10/12] ci: configure coverage correctly --- pyproject.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index b67f485..2b9e1a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,7 @@ docs = [ ] test = [ "pytest", + "pytest-cov", "pytest-mpl", ] dev = [ @@ -104,6 +105,14 @@ ignore = ["D409"] "S607", ] +[tool.coverage.paths] +source = [ + "src/", + "**/site-packages/", +] + +[tool.coverage.run] +relative_files = true # git-cliff ~ default configuration file # https://git-cliff.org/docs/configuration From a8f43ee18e2afcac3fabdae3044975e56d5b39b7 Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:31:13 +0200 Subject: [PATCH 11/12] ci: fix typo --- .github/workflows/tests.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 24f4097..02f6f12 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ on: workflow_dispatch: jobs: - test: + tests: name: Run tests & report coverage runs-on: ubuntu-latest permissions: diff --git a/pyproject.toml b/pyproject.toml index 2b9e1a7..22eb330 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ docs = [ "myst_parser", "autodoc_pydantic", ] -test = [ +tests = [ "pytest", "pytest-cov", "pytest-mpl", From 66e1d7956d7fb05b2bfc9e48b4d742b18d221860 Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:41:03 +0200 Subject: [PATCH 12/12] ci: replace lydata with lyscripts in test action --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 02f6f12..84ebe00 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,7 +30,7 @@ jobs: # Below, we first run pytest in the `tests/` folder. Because we use a `src` # layout, this will fail if the package is not installed correctly. - name: Test package is installable - run: pytest --cov=lydata --cov-config=pyproject.toml tests + run: pytest --cov=lyscripts --cov-config=pyproject.toml tests env: COVERAGE_FILE: .coverage.is_installable @@ -39,7 +39,7 @@ jobs: # installable from the step above. - name: Run doctests if: success() || failure() # run these even if previous step fails - run: pytest --cov=lydata --cov-config=pyproject.toml --doctest-modules src + run: pytest --cov=lyscripts --cov-config=pyproject.toml --doctest-modules src env: COVERAGE_FILE: .coverage.doctests GITHUB_TOKEN: ${{ secrets.LYCOSYSTEM_READALL }}