From 21491a6efd5fdf8002264b5882e7edec81844358 Mon Sep 17 00:00:00 2001
From: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 26 Jun 2025 14:44:08 +0000
Subject: [PATCH 1/5] Update coverage data
---
README.md | 66 +
badge.svg | 1 +
data.json | 1 +
endpoint.json | 1 +
htmlcov/class_index.html | 596 +++++++
htmlcov/coverage_html_cb_6fb7b396.js | 733 ++++++++
htmlcov/favicon_32_cb_58284776.png | Bin 0 -> 1732 bytes
htmlcov/function_index.html | 1556 +++++++++++++++++
htmlcov/index.html | 300 ++++
htmlcov/keybd_closed_cb_ce680311.png | Bin 0 -> 9004 bytes
htmlcov/status.json | 1 +
htmlcov/style_cb_81f8c14c.css | 337 ++++
htmlcov/z_055061514423972c___init___py.html | 118 ++
htmlcov/z_055061514423972c___main___py.html | 105 ++
htmlcov/z_055061514423972c_posteriors_py.html | 239 +++
.../z_055061514423972c_prevalences_py.html | 330 ++++
htmlcov/z_055061514423972c_priors_py.html | 208 +++
htmlcov/z_055061514423972c_risks_py.html | 237 +++
htmlcov/z_055061514423972c_utils_py.html | 373 ++++
htmlcov/z_5bf5c588c698c6cc___init___py.html | 172 ++
htmlcov/z_5bf5c588c698c6cc___main___py.html | 103 ++
htmlcov/z_5bf5c588c698c6cc__version_py.html | 118 ++
htmlcov/z_5bf5c588c698c6cc_cli_py.html | 187 ++
htmlcov/z_5bf5c588c698c6cc_configs_py.html | 847 +++++++++
htmlcov/z_5bf5c588c698c6cc_decorators_py.html | 185 ++
htmlcov/z_5bf5c588c698c6cc_evaluate_py.html | 309 ++++
htmlcov/z_5bf5c588c698c6cc_plots_py.html | 508 ++++++
htmlcov/z_5bf5c588c698c6cc_sample_py.html | 518 ++++++
htmlcov/z_5bf5c588c698c6cc_schedule_py.html | 182 ++
htmlcov/z_5bf5c588c698c6cc_schema_py.html | 162 ++
htmlcov/z_5bf5c588c698c6cc_utils_py.html | 296 ++++
htmlcov/z_9b7bcb970ba14d6a___init___py.html | 140 ++
htmlcov/z_9b7bcb970ba14d6a___main___py.html | 133 ++
htmlcov/z_9b7bcb970ba14d6a_enhance_py.html | 160 ++
htmlcov/z_9b7bcb970ba14d6a_filter_py.html | 196 +++
htmlcov/z_9b7bcb970ba14d6a_generate_py.html | 193 ++
htmlcov/z_9b7bcb970ba14d6a_join_py.html | 174 ++
htmlcov/z_9b7bcb970ba14d6a_lyproxify_py.html | 436 +++++
htmlcov/z_9b7bcb970ba14d6a_split_py.html | 170 ++
htmlcov/z_9b7bcb970ba14d6a_utils_py.html | 113 ++
40 files changed, 10504 insertions(+)
create mode 100644 README.md
create mode 100644 badge.svg
create mode 100644 data.json
create mode 100644 endpoint.json
create mode 100644 htmlcov/class_index.html
create mode 100644 htmlcov/coverage_html_cb_6fb7b396.js
create mode 100644 htmlcov/favicon_32_cb_58284776.png
create mode 100644 htmlcov/function_index.html
create mode 100644 htmlcov/index.html
create mode 100644 htmlcov/keybd_closed_cb_ce680311.png
create mode 100644 htmlcov/status.json
create mode 100644 htmlcov/style_cb_81f8c14c.css
create mode 100644 htmlcov/z_055061514423972c___init___py.html
create mode 100644 htmlcov/z_055061514423972c___main___py.html
create mode 100644 htmlcov/z_055061514423972c_posteriors_py.html
create mode 100644 htmlcov/z_055061514423972c_prevalences_py.html
create mode 100644 htmlcov/z_055061514423972c_priors_py.html
create mode 100644 htmlcov/z_055061514423972c_risks_py.html
create mode 100644 htmlcov/z_055061514423972c_utils_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc___init___py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc___main___py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc__version_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_cli_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_configs_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_decorators_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_evaluate_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_plots_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_sample_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_schedule_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_schema_py.html
create mode 100644 htmlcov/z_5bf5c588c698c6cc_utils_py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a___init___py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a___main___py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a_enhance_py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a_filter_py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a_generate_py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a_join_py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a_lyproxify_py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a_split_py.html
create mode 100644 htmlcov/z_9b7bcb970ba14d6a_utils_py.html
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..12a1fb9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,66 @@
+# Repository Coverage
+
+[Full report](https://htmlpreview.github.io/?https://github.com/lycosystem/lyscripts/blob/python-coverage-comment-action-data/htmlcov/index.html)
+
+| Name | Stmts | Miss | Cover | Missing |
+|-------------------------------------- | -------: | -------: | ------: | --------: |
+| src/lyscripts/\_\_init\_\_.py | 34 | 7 | 79% |57-58, 66-72 |
+| src/lyscripts/\_\_main\_\_.py | 3 | 3 | 0% | 3-6 |
+| src/lyscripts/\_version.py | 13 | 3 | 77% | 8-11 |
+| src/lyscripts/cli.py | 26 | 12 | 54% |61-67, 81-86 |
+| src/lyscripts/compute/\_\_init\_\_.py | 9 | 1 | 89% | 21 |
+| src/lyscripts/compute/\_\_main\_\_.py | 5 | 5 | 0% | 3-8 |
+| src/lyscripts/compute/posteriors.py | 46 | 19 | 59% |97-137, 141-142 |
+| src/lyscripts/compute/prevalences.py | 80 | 7 | 91% |58-60, 94-99, 232-233 |
+| src/lyscripts/compute/priors.py | 35 | 2 | 94% | 110-111 |
+| src/lyscripts/compute/risks.py | 51 | 33 | 35% |47-65, 81-135, 139-140 |
+| src/lyscripts/compute/utils.py | 120 | 6 | 95% |95, 146, 177, 188, 240, 250 |
+| src/lyscripts/configs.py | 255 | 27 | 89% |90, 122, 165, 173, 217, 271, 277-278, 286, 472, 506-509, 514, 585, 624-637, 680 |
+| src/lyscripts/data/\_\_init\_\_.py | 12 | 1 | 92% | 43 |
+| src/lyscripts/data/\_\_main\_\_.py | 18 | 18 | 0% | 3-36 |
+| src/lyscripts/data/enhance.py | 24 | 8 | 67% |39-58, 62-63 |
+| src/lyscripts/data/filter.py | 49 | 30 | 39% |43-66, 76-94, 98-99 |
+| src/lyscripts/data/generate.py | 39 | 4 | 90% |58, 63, 95-96 |
+| src/lyscripts/data/join.py | 20 | 9 | 55% |59-72, 76-77 |
+| src/lyscripts/data/lyproxify.py | 121 | 67 | 45% |29-32, 37-44, 88-117, 130-140, 171, 248-280, 291-305, 338-339 |
+| src/lyscripts/data/split.py | 30 | 14 | 53% |33-65, 72-73 |
+| src/lyscripts/data/utils.py | 9 | 0 | 100% | |
+| src/lyscripts/decorators.py | 41 | 4 | 90% | 53-55, 70 |
+| src/lyscripts/evaluate.py | 75 | 57 | 24% |29-35, 43-70, 87, 104-109, 120-141, 146-204, 208-212 |
+| src/lyscripts/plots.py | 166 | 23 | 86% |26-27, 46-47, 56, 94, 99, 104, 185-186, 336, 341, 370-392, 399 |
+| src/lyscripts/sample.py | 136 | 11 | 92% |35-36, 74, 132, 172, 188, 301, 309, 313, 420-421 |
+| src/lyscripts/schedule.py | 29 | 13 | 55% |25-27, 35, 44-45, 73-80, 84-85 |
+| src/lyscripts/schema.py | 21 | 3 | 86% | 60-61, 65 |
+| src/lyscripts/utils.py | 84 | 5 | 94% |25, 141-142, 196-197 |
+| **TOTAL** | **1551** | **392** | **75%** | |
+
+
+## Setup coverage badge
+
+Below are examples of the badges you can use in your main branch `README` file.
+
+### Direct image
+
+[](https://htmlpreview.github.io/?https://github.com/lycosystem/lyscripts/blob/python-coverage-comment-action-data/htmlcov/index.html)
+
+This is the one to use if your repository is private or if you don't want to customize anything.
+
+### [Shields.io](https://shields.io) Json Endpoint
+
+[](https://htmlpreview.github.io/?https://github.com/lycosystem/lyscripts/blob/python-coverage-comment-action-data/htmlcov/index.html)
+
+Using this one will allow you to [customize](https://shields.io/endpoint) the look of your badge.
+It won't work with private repositories. It won't be refreshed more than once per five minutes.
+
+### [Shields.io](https://shields.io) Dynamic Badge
+
+[](https://htmlpreview.github.io/?https://github.com/lycosystem/lyscripts/blob/python-coverage-comment-action-data/htmlcov/index.html)
+
+This one will always be the same color. It won't work for private repos. I'm not even sure why we included it.
+
+## What is that?
+
+This branch is part of the
+[python-coverage-comment-action](https://github.com/marketplace/actions/python-coverage-comment)
+GitHub Action. All the files in this branch are automatically generated and may be
+overwritten at any moment.
\ No newline at end of file
diff --git a/badge.svg b/badge.svg
new file mode 100644
index 0000000..993547f
--- /dev/null
+++ b/badge.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data.json b/data.json
new file mode 100644
index 0000000..1ec6703
--- /dev/null
+++ b/data.json
@@ -0,0 +1 @@
+{"coverage": 74.72598323662153, "raw_data": {"meta": {"format": 3, "version": "7.9.1", "timestamp": "2025-06-26T14:44:06.796026", "branch_coverage": false, "show_contexts": false}, "files": {"src/lyscripts/__init__.py": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 34, "percent_covered": 79.41176470588235, "percent_covered_display": "79", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [57, 58, 66, 68, 69, 70, 72], "excluded_lines": [], "functions": {"LyscriptsCLI.__init__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [57, 58], "excluded_lines": []}, "LyscriptsCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [66, 68, 69, 70, 72], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"LyscriptsCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [57, 58, 66, 68, 69, 70, 72], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": []}}}, "src/lyscripts/_version.py": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": []}}, "classes": {"": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": []}}}, "src/lyscripts/cli.py": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 35, 37, 44, 46, 49, 70], "summary": {"covered_lines": 14, "num_statements": 26, "percent_covered": 53.84615384615385, "percent_covered_display": "54", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67, 81, 82, 83, 84, 85, 86], "excluded_lines": [], "functions": {"assemble_main": {"executed_lines": [35, 46], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "assemble_main.main": {"executed_lines": [37, 44], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "somewhat_safely_get_loglevel": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67], "excluded_lines": []}, "configure_logging": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [81, 82, 83, 84, 85, 86], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 49, 70], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 35, 37, 44, 46, 49, 70], "summary": {"covered_lines": 14, "num_statements": 26, "percent_covered": 53.84615384615385, "percent_covered_display": "54", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67, 81, 82, 83, 84, 85, 86], "excluded_lines": []}}}, "src/lyscripts/compute/__init__.py": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 9, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": [], "functions": {"ComputeCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": []}, "": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"ComputeCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": []}, "": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/compute/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": []}}}, "src/lyscripts/compute/posteriors.py": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 51, 52, 53, 54, 55, 57, 58, 60, 66, 75, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 27, "num_statements": 46, "percent_covered": 58.69565217391305, "percent_covered_display": "59", "missing_lines": 19, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137, 141, 142], "excluded_lines": [], "functions": {"compute_posteriors": {"executed_lines": [51, 52, 53, 54, 55, 57, 58, 60, 66, 75], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PosteriorsCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137], "excluded_lines": []}, "": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 17, "num_statements": 19, "percent_covered": 89.47368421052632, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}}, "classes": {"PosteriorsCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137], "excluded_lines": []}, "": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 51, 52, 53, 54, 55, 57, 58, 60, 66, 75, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 27, "num_statements": 29, "percent_covered": 93.10344827586206, "percent_covered_display": "93", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}}}, "src/lyscripts/compute/prevalences.py": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 28, 40, 43, 54, 55, 57, 62, 63, 64, 66, 72, 73, 78, 79, 81, 87, 101, 103, 106, 108, 109, 110, 111, 112, 113, 114, 117, 141, 142, 144, 145, 146, 148, 155, 161, 162, 164, 170, 173, 175, 177, 178, 181, 183, 184, 185, 186, 188, 189, 190, 192, 201, 202, 204, 214, 219, 220, 221, 222, 231], "summary": {"covered_lines": 73, "num_statements": 80, "percent_covered": 91.25, "percent_covered_display": "91", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [58, 59, 60, 94, 99, 232, 233], "excluded_lines": [], "functions": {"compute_prevalences": {"executed_lines": [54, 55, 57, 62, 63, 64, 66, 72, 73, 78, 79, 81, 87, 101, 103], "summary": {"covered_lines": 15, "num_statements": 20, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [58, 59, 60, 94, 99], "excluded_lines": []}, "generate_query_from_diagnosis": {"executed_lines": [108, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "observe_prevalence": {"executed_lines": [141, 142, 144, 145, 146, 148, 155], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PrevalencesCLI.cli_cmd": {"executed_lines": [177, 178, 181, 183, 184, 185, 186, 188, 189, 190, 192, 201, 202, 204, 214, 219, 220, 221, 222], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 28, 40, 43, 106, 117, 161, 162, 164, 170, 173, 175, 231], "summary": {"covered_lines": 25, "num_statements": 27, "percent_covered": 92.5925925925926, "percent_covered_display": "93", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [232, 233], "excluded_lines": []}}, "classes": {"PrevalencesCLI": {"executed_lines": [177, 178, 181, 183, 184, 185, 186, 188, 189, 190, 192, 201, 202, 204, 214, 219, 220, 221, 222], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 28, 40, 43, 54, 55, 57, 62, 63, 64, 66, 72, 73, 78, 79, 81, 87, 101, 103, 106, 108, 109, 110, 111, 112, 113, 114, 117, 141, 142, 144, 145, 146, 148, 155, 161, 162, 164, 170, 173, 175, 231], "summary": {"covered_lines": 54, "num_statements": 61, "percent_covered": 88.52459016393442, "percent_covered_display": "89", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [58, 59, 60, 94, 99, 232, 233], "excluded_lines": []}}}, "src/lyscripts/compute/priors.py": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 42, 43, 44, 46, 52, 53, 60, 63, 64, 66, 68, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106, 109], "summary": {"covered_lines": 33, "num_statements": 35, "percent_covered": 94.28571428571429, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": [], "functions": {"compute_priors": {"executed_lines": [42, 43, 44, 46, 52, 53, 60], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PriorsCLI.cli_cmd": {"executed_lines": [84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 63, 64, 66, 68, 109], "summary": {"covered_lines": 14, "num_statements": 16, "percent_covered": 87.5, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": []}}, "classes": {"PriorsCLI": {"executed_lines": [84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 42, 43, 44, 46, 52, 53, 60, 63, 64, 66, 68, 109], "summary": {"covered_lines": 21, "num_statements": 23, "percent_covered": 91.30434782608695, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": []}}}, "src/lyscripts/compute/risks.py": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 51, "percent_covered": 35.294117647058826, "percent_covered_display": "35", "missing_lines": 33, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65, 81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135, 139, 140], "excluded_lines": [], "functions": {"compute_risks": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65], "excluded_lines": []}, "RisksCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 22, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 22, "excluded_lines": 0}, "missing_lines": [81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135], "excluded_lines": []}, "": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 20, "percent_covered": 90.0, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [139, 140], "excluded_lines": []}}, "classes": {"RisksCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 22, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 22, "excluded_lines": 0}, "missing_lines": [81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135], "excluded_lines": []}, "": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 29, "percent_covered": 62.06896551724138, "percent_covered_display": "62", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65, 139, 140], "excluded_lines": []}}}, "src/lyscripts/compute/utils.py": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 49, 55, 57, 58, 59, 60, 62, 63, 66, 68, 69, 70, 71, 72, 73, 74, 77, 92, 94, 97, 98, 99, 101, 104, 106, 107, 108, 111, 112, 115, 116, 118, 121, 128, 145, 148, 149, 151, 153, 155, 156, 158, 159, 161, 163, 165, 166, 168, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 187, 189, 191, 194, 210, 211, 212, 213, 214, 215, 216, 217, 219, 222, 239, 242, 243, 244, 246, 247, 248, 249, 252, 254, 257, 259, 260, 261, 263, 264, 265, 266, 267, 269, 271, 272, 273, 275, 276], "summary": {"covered_lines": 114, "num_statements": 120, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [95, 146, 177, 188, 240, 250], "excluded_lines": [], "functions": {"is_hdf5_compatible": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "to_hdf5_attrs": {"executed_lines": [57, 58, 59, 60, 62, 63], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "from_hdf5_attrs": {"executed_lines": [68, 69, 70, 71, 72, 73, 74], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "extract_modalities": {"executed_lines": [92, 94, 97, 98, 99, 101], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [95], "excluded_lines": []}, "ensure_parent_dir": {"executed_lines": [106, 107, 108], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage._get_dataset": {"executed_lines": [145, 148, 149], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [146], "excluded_lines": []}, "HDF5FileStorage.load": {"executed_lines": [153, 155, 156, 158, 159], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage.get_attrs": {"executed_lines": [163, 165, 166, 168, 169], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage.save": {"executed_lines": [173, 175, 176, 178, 180], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [177], "excluded_lines": []}, "HDF5FileStorage.set_attrs": {"executed_lines": [184, 186, 187, 189, 191], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [188], "excluded_lines": []}, "reduce_pattern": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 219], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "complete_pattern": {"executed_lines": [239, 242, 243, 244, 246, 247, 248, 249, 252, 254], "summary": {"covered_lines": 10, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [240, 250], "excluded_lines": []}, "get_cached": {"executed_lines": [259, 260, 261, 263, 264, 275, 276], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_cached.log_cache_info_wrapper": {"executed_lines": [265, 266, 267, 269, 271, 272, 273], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 55, 66, 77, 104, 111, 112, 115, 116, 118, 121, 128, 151, 161, 171, 182, 194, 222, 257], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"BaseComputeCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage": {"executed_lines": [145, 148, 149, 153, 155, 156, 158, 159, 163, 165, 166, 168, 169, 173, 175, 176, 178, 180, 184, 186, 187, 189, 191], "summary": {"covered_lines": 23, "num_statements": 26, "percent_covered": 88.46153846153847, "percent_covered_display": "88", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [146, 177, 188], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 49, 55, 57, 58, 59, 60, 62, 63, 66, 68, 69, 70, 71, 72, 73, 74, 77, 92, 94, 97, 98, 99, 101, 104, 106, 107, 108, 111, 112, 115, 116, 118, 121, 128, 151, 161, 171, 182, 194, 210, 211, 212, 213, 214, 215, 216, 217, 219, 222, 239, 242, 243, 244, 246, 247, 248, 249, 252, 254, 257, 259, 260, 261, 263, 264, 265, 266, 267, 269, 271, 272, 273, 275, 276], "summary": {"covered_lines": 91, "num_statements": 94, "percent_covered": 96.80851063829788, "percent_covered_display": "97", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [95, 240, 250], "excluded_lines": []}}}, "src/lyscripts/configs.py": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 89, 92, 94, 96, 102, 104, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 161, 162, 164, 167, 168, 170, 171, 176, 177, 179, 182, 186, 187, 189, 190, 202, 204, 205, 206, 208, 209, 211, 214, 216, 219, 220, 222, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288, 296, 298, 299, 306, 307, 316, 321, 325, 332, 336, 341, 343, 348, 349, 350, 355, 357, 359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 457, 464, 466, 467, 469, 470, 475, 476, 478, 482, 487, 491, 495, 496, 498, 500, 501, 503, 505, 511, 513, 519, 521, 522, 523, 524, 525, 526, 529, 550, 551, 553, 554, 555, 561, 562, 565, 572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595, 598, 604, 605, 606, 608, 609, 610, 612, 613, 616, 640, 643, 644, 657, 672, 673, 675, 677, 678, 679, 684, 686, 688, 692, 693, 699, 701, 703, 713, 714, 716, 718, 728, 729, 738, 743, 744], "summary": {"covered_lines": 228, "num_statements": 255, "percent_covered": 89.41176470588235, "percent_covered_display": "89", "missing_lines": 27, "excluded_lines": 0}, "missing_lines": [90, 122, 165, 173, 217, 271, 277, 278, 286, 472, 506, 507, 509, 514, 585, 624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637, 680], "excluded_lines": [], "functions": {"DataConfig.load": {"executed_lines": [89, 92], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [90], "excluded_lines": []}, "DataConfig.get_load_kwargs": {"executed_lines": [96], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_pattern": {"executed_lines": [104], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DiagnosisConfig.to_involvement": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [122], "excluded_lines": []}, "retrieve_graph_representation": {"executed_lines": [161, 162, 164, 167, 168, 170, 171], "summary": {"covered_lines": 7, "num_statements": 9, "percent_covered": 77.77777777777777, "percent_covered_display": "78", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [165, 173], "excluded_lines": []}, "GraphConfig.from_model": {"executed_lines": [189, 190], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "has_model_symbol": {"executed_lines": [204, 205, 206, 208, 209, 211], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_symmetry_kwargs": {"executed_lines": [216, 219, 220, 222], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [217], "excluded_lines": []}, "ModelConfig.from_model": {"executed_lines": [261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288], "summary": {"covered_lines": 11, "num_statements": 15, "percent_covered": 73.33333333333333, "percent_covered_display": "73", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [271, 277, 278, 286], "excluded_lines": []}, "modalityconfig_from_model": {"executed_lines": [298, 299], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DeprecatedModelConfig.model_post_init": {"executed_lines": [343, 348, 349, 350, 355], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DeprecatedModelConfig.translate": {"executed_lines": [359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SamplingConfig.load": {"executed_lines": [457], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "map_to_optional_bool": {"executed_lines": [466, 467, 469, 470], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [472], "excluded_lines": []}, "ScenarioConfig.model_post_init": {"executed_lines": [500, 501], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ScenarioConfig.interpolate": {"executed_lines": [505], "summary": {"covered_lines": 1, "num_statements": 4, "percent_covered": 25.0, "percent_covered_display": "25", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [506, 507, 509], "excluded_lines": []}, "ScenarioConfig.normalize": {"executed_lines": [513], "summary": {"covered_lines": 1, "num_statements": 2, "percent_covered": 50.0, "percent_covered_display": "50", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [514], "excluded_lines": []}, "_construct_model_from_external": {"executed_lines": [521, 522, 523, 524, 525, 526], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "construct_model": {"executed_lines": [550, 551, 553, 554, 555, 561, 562], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "add_distributions": {"executed_lines": [572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595], "summary": {"covered_lines": 17, "num_statements": 18, "percent_covered": 94.44444444444444, "percent_covered_display": "94", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [585], "excluded_lines": []}, "add_modalities": {"executed_lines": [604, 605, 606, 608, 609, 610, 612, 613], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "add_data": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__init__": {"executed_lines": [672, 673], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DynamicYamlConfigSettingsSource._read_file": {"executed_lines": [677, 678, 679, 684], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [680], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__call__": {"executed_lines": [688, 692, 693, 699], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__repr__": {"executed_lines": [703], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BaseCLI.settings_customise_sources": {"executed_lines": [738, 743, 744], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 94, 102, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 176, 177, 179, 182, 186, 187, 202, 214, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 296, 306, 307, 316, 321, 325, 332, 336, 341, 357, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 464, 475, 476, 478, 482, 487, 491, 495, 496, 498, 503, 511, 519, 529, 565, 598, 616, 640, 643, 644, 657, 675, 686, 701, 713, 714, 716, 718, 728, 729], "summary": {"covered_lines": 112, "num_statements": 112, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"CrossValidationConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DataConfig": {"executed_lines": [89, 92, 96], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [90], "excluded_lines": []}, "DiagnosisConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [122], "excluded_lines": []}, "DistributionConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "InvolvementConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "GraphConfig": {"executed_lines": [189, 190], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ModelConfig": {"executed_lines": [261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288], "summary": {"covered_lines": 11, "num_statements": 15, "percent_covered": 73.33333333333333, "percent_covered_display": "73", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [271, 277, 278, 286], "excluded_lines": []}, "DeprecatedModelConfig": {"executed_lines": [343, 348, 349, 350, 355, 359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SamplingConfig": {"executed_lines": [457], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ScenarioConfig": {"executed_lines": [500, 501, 505, 513], "summary": {"covered_lines": 4, "num_statements": 8, "percent_covered": 50.0, "percent_covered_display": "50", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [506, 507, 509, 514], "excluded_lines": []}, "DynamicYamlConfigSettingsSource": {"executed_lines": [672, 673, 677, 678, 679, 684, 688, 692, 693, 699, 703], "summary": {"covered_lines": 11, "num_statements": 12, "percent_covered": 91.66666666666667, "percent_covered_display": "92", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [680], "excluded_lines": []}, "BaseCLI": {"executed_lines": [738, 743, 744], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 94, 102, 104, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 161, 162, 164, 167, 168, 170, 171, 176, 177, 179, 182, 186, 187, 202, 204, 205, 206, 208, 209, 211, 214, 216, 219, 220, 222, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 296, 298, 299, 306, 307, 316, 321, 325, 332, 336, 341, 357, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 464, 466, 467, 469, 470, 475, 476, 478, 482, 487, 491, 495, 496, 498, 503, 511, 519, 521, 522, 523, 524, 525, 526, 529, 550, 551, 553, 554, 555, 561, 562, 565, 572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595, 598, 604, 605, 606, 608, 609, 610, 612, 613, 616, 640, 643, 644, 657, 675, 686, 701, 713, 714, 716, 718, 728, 729], "summary": {"covered_lines": 174, "num_statements": 190, "percent_covered": 91.57894736842105, "percent_covered_display": "92", "missing_lines": 16, "excluded_lines": 0}, "missing_lines": [165, 173, 217, 472, 585, 624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637], "excluded_lines": []}}}, "src/lyscripts/data/__init__.py": {"executed_lines": [1, 17, 19, 28, 31, 32, 34, 35, 36, 37, 38, 39, 41], "summary": {"covered_lines": 11, "num_statements": 12, "percent_covered": 91.66666666666667, "percent_covered_display": "92", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [43], "excluded_lines": [], "functions": {"DataCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [43], "excluded_lines": []}, "": {"executed_lines": [1, 17, 19, 28, 31, 32, 34, 35, 36, 37, 38, 39, 41], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"DataCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [43], "excluded_lines": []}, "": {"executed_lines": [1, 17, 19, 28, 31, 32, 34, 35, 36, 37, 38, 39, 41], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/data/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 15, 20, 21, 25, 26, 27, 28, 29, 31, 32, 35, 36], "excluded_lines": [], "functions": {"main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 10, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 10, "excluded_lines": 0}, "missing_lines": [15, 20, 21, 25, 26, 27, 28, 29, 31, 32], "excluded_lines": []}, "": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 35, 36], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 15, 20, 21, 25, 26, 27, 28, 29, 31, 32, 35, 36], "excluded_lines": []}}}, "src/lyscripts/data/enhance.py": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 24, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58, 62, 63], "excluded_lines": [], "functions": {"EnhanceCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [62, 63], "excluded_lines": []}}, "classes": {"EnhanceCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [62, 63], "excluded_lines": []}}}, "src/lyscripts/data/filter.py": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 49, "percent_covered": 38.775510204081634, "percent_covered_display": "39", "missing_lines": 30, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66, 76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94, 98, 99], "excluded_lines": [], "functions": {"FilterCLI.model_post_init": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66], "excluded_lines": []}, "FilterCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 21, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [98, 99], "excluded_lines": []}}, "classes": {"FilterCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 28, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 28, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66, 76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 21, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [98, 99], "excluded_lines": []}}}, "src/lyscripts/data/generate.py": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 55, 56, 57, 60, 62, 65, 67, 76, 78, 79, 80, 81, 82, 84, 89, 91, 94], "summary": {"covered_lines": 35, "num_statements": 39, "percent_covered": 89.74358974358974, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [58, 63, 95, 96], "excluded_lines": [], "functions": {"GenerateCLI.model_post_init": {"executed_lines": [55, 56, 57, 60, 62, 65], "summary": {"covered_lines": 6, "num_statements": 8, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [58, 63], "excluded_lines": []}, "GenerateCLI.cli_cmd": {"executed_lines": [76, 78, 79, 80, 81, 82, 84, 89, 91], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 67, 94], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [95, 96], "excluded_lines": []}}, "classes": {"GenerateCLI": {"executed_lines": [55, 56, 57, 60, 62, 65, 76, 78, 79, 80, 81, 82, 84, 89, 91], "summary": {"covered_lines": 15, "num_statements": 17, "percent_covered": 88.23529411764706, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [58, 63], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 67, 94], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [95, 96], "excluded_lines": []}}}, "src/lyscripts/data/join.py": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 75], "summary": {"covered_lines": 11, "num_statements": 20, "percent_covered": 55.0, "percent_covered_display": "55", "missing_lines": 9, "excluded_lines": 0}, "missing_lines": [59, 61, 62, 63, 64, 66, 72, 76, 77], "excluded_lines": [], "functions": {"JoinCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [59, 61, 62, 63, 64, 66, 72], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 75], "summary": {"covered_lines": 11, "num_statements": 13, "percent_covered": 84.61538461538461, "percent_covered_display": "85", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [76, 77], "excluded_lines": []}}, "classes": {"JoinCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [59, 61, 62, 63, 64, 66, 72], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 75], "summary": {"covered_lines": 11, "num_statements": 13, "percent_covered": 84.61538461538461, "percent_covered_display": "85", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [76, 77], "excluded_lines": []}}}, "src/lyscripts/data/lyproxify.py": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 19, 20, 21, 22, 24, 27, 35, 47, 48, 50, 51, 55, 65, 72, 76, 78, 120, 121, 124, 143, 160, 161, 162, 163, 165, 167, 174, 197, 198, 199, 200, 201, 202, 203, 204, 206, 208, 211, 283, 308, 325, 326, 328, 329, 330, 332, 333, 334, 337], "summary": {"covered_lines": 54, "num_statements": 121, "percent_covered": 44.62809917355372, "percent_covered_display": "45", "missing_lines": 67, "excluded_lines": 0}, "missing_lines": [29, 30, 32, 37, 38, 39, 41, 42, 44, 88, 90, 94, 100, 101, 102, 103, 104, 106, 107, 108, 109, 111, 112, 114, 115, 117, 130, 132, 133, 134, 139, 140, 171, 248, 250, 251, 253, 254, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 269, 270, 274, 279, 280, 291, 292, 293, 295, 296, 297, 298, 300, 301, 302, 304, 305, 338, 339], "excluded_lines": [], "functions": {"ensure_python_file": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [29, 30, 32], "excluded_lines": []}, "ensure_column_map": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [37, 38, 39, 41, 42, 44], "excluded_lines": []}, "LyproxifyCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [88, 90, 94, 100, 101, 102, 103, 104, 106, 107, 108, 109, 111, 112, 114, 115, 117], "excluded_lines": []}, "clean_header": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [130, 132, 133, 134, 139, 140], "excluded_lines": []}, "get_instruction_depth": {"executed_lines": [160, 161, 162, 163, 165, 167], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [171], "excluded_lines": []}, "generate_markdown_docs": {"executed_lines": [197, 198, 199, 200, 201, 202, 203, 204, 206, 208], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "transform_to_lyprox": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0}, "missing_lines": [248, 250, 251, 253, 254, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 269, 270, 274, 279, 280], "excluded_lines": []}, "leftright_to_ipsicontra": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [291, 292, 293, 295, 296, 297, 298, 300, 301, 302, 304, 305], "excluded_lines": []}, "exclude_patients": {"executed_lines": [325, 326, 328, 329, 330, 332, 333, 334], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 19, 20, 21, 22, 24, 27, 35, 47, 48, 50, 51, 55, 65, 72, 76, 78, 120, 121, 124, 143, 174, 211, 283, 308, 337], "summary": {"covered_lines": 30, "num_statements": 32, "percent_covered": 93.75, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [338, 339], "excluded_lines": []}}, "classes": {"LyproxifyCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [88, 90, 94, 100, 101, 102, 103, 104, 106, 107, 108, 109, 111, 112, 114, 115, 117], "excluded_lines": []}, "ParsingError": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 19, 20, 21, 22, 24, 27, 35, 47, 48, 50, 51, 55, 65, 72, 76, 78, 120, 121, 124, 143, 160, 161, 162, 163, 165, 167, 174, 197, 198, 199, 200, 201, 202, 203, 204, 206, 208, 211, 283, 308, 325, 326, 328, 329, 330, 332, 333, 334, 337], "summary": {"covered_lines": 54, "num_statements": 104, "percent_covered": 51.92307692307692, "percent_covered_display": "52", "missing_lines": 50, "excluded_lines": 0}, "missing_lines": [29, 30, 32, 37, 38, 39, 41, 42, 44, 130, 132, 133, 134, 139, 140, 171, 248, 250, 251, 253, 254, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 269, 270, 274, 279, 280, 291, 292, 293, 295, 296, 297, 298, 300, 301, 302, 304, 305, 338, 339], "excluded_lines": []}}}, "src/lyscripts/data/split.py": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 30, "percent_covered": 53.333333333333336, "percent_covered_display": "53", "missing_lines": 14, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65, 72, 73], "excluded_lines": [], "functions": {"SplitCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [72, 73], "excluded_lines": []}}, "classes": {"SplitCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [72, 73], "excluded_lines": []}}}, "src/lyscripts/data/utils.py": {"executed_lines": [1, 3, 5, 6, 8, 11, 12, 14, 15, 16], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": [], "functions": {"save_table_to_csv": {"executed_lines": [14, 15, 16], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 11, 12], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 3, 5, 6, 8, 11, 12, 14, 15, 16], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/decorators.py": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 18, 19, 20, 23, 30, 33, 34, 36, 37, 38, 39, 41, 42, 51, 57, 59, 62, 65, 66, 68, 69, 72, 74, 77, 80, 81, 83, 84, 86, 88], "summary": {"covered_lines": 37, "num_statements": 41, "percent_covered": 90.2439024390244, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [53, 54, 55, 70], "excluded_lines": [], "functions": {"assemble_signature": {"executed_lines": [18, 19, 20], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state": {"executed_lines": [30, 59], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state.log_decorator": {"executed_lines": [33, 34, 57], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state.log_decorator.wrapper": {"executed_lines": [36, 37, 38, 39, 41, 42, 51], "summary": {"covered_lines": 7, "num_statements": 10, "percent_covered": 70.0, "percent_covered_display": "70", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [53, 54, 55], "excluded_lines": []}, "check_input_file_exists": {"executed_lines": [65, 66, 74], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_input_file_exists.inner": {"executed_lines": [68, 69, 72], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [70], "excluded_lines": []}, "check_output_dir_exists": {"executed_lines": [80, 81, 88], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_output_dir_exists.inner": {"executed_lines": [83, 84, 86], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 23, 62, 77], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 18, 19, 20, 23, 30, 33, 34, 36, 37, 38, 39, 41, 42, 51, 57, 59, 62, 65, 66, 68, 69, 72, 74, 77, 80, 81, 83, 84, 86, 88], "summary": {"covered_lines": 37, "num_statements": 41, "percent_covered": 90.2439024390244, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [53, 54, 55, 70], "excluded_lines": []}}}, "src/lyscripts/evaluate.py": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 75, "percent_covered": 24.0, "percent_covered_display": "24", "missing_lines": 57, "excluded_lines": 0}, "missing_lines": [29, 35, 43, 48, 50, 57, 63, 70, 87, 104, 105, 106, 107, 108, 109, 120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141, 146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204, 208, 209, 211, 212], "excluded_lines": [], "functions": {"_add_parser": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [29, 35], "excluded_lines": []}, "_add_arguments": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [43, 48, 50, 57, 63, 70], "excluded_lines": []}, "comp_bic": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [87], "excluded_lines": []}, "compute_evidence": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [104, 105, 106, 107, 108, 109], "excluded_lines": []}, "compute_ti_results": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 14, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 14, "excluded_lines": 0}, "missing_lines": [120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141], "excluded_lines": []}, "main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 24, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 24, "excluded_lines": 0}, "missing_lines": [146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 22, "percent_covered": 81.81818181818181, "percent_covered_display": "82", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [208, 209, 211, 212], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 75, "percent_covered": 24.0, "percent_covered_display": "24", "missing_lines": 57, "excluded_lines": 0}, "missing_lines": [29, 35, 43, 48, 50, 57, 63, 70, 87, 104, 105, 106, 107, 108, 109, 120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141, 146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204, 208, 209, 211, 212], "excluded_lines": []}}}, "src/lyscripts/plots.py": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 61, 64, 66, 69, 74, 75, 76, 77, 78, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 112, 115, 116, 118, 120, 121, 123, 125, 126, 135, 136, 137, 138, 139, 140, 142, 144, 146, 148, 150, 152, 154, 155, 157, 158, 160, 163, 164, 166, 167, 169, 170, 179, 180, 181, 182, 183, 184, 190, 198, 199, 201, 202, 204, 206, 208, 216, 218, 225, 227, 234, 239, 240, 241, 243, 244, 246, 247, 249, 252, 268, 269, 270, 271, 273, 274, 275, 276, 279, 281, 282, 288, 289, 290, 291, 294, 303, 306, 307, 310, 311, 314, 335, 338, 340, 343, 345, 354, 355, 357, 358, 360, 363, 395, 396, 397, 402, 403, 404, 410, 411], "summary": {"covered_lines": 143, "num_statements": 166, "percent_covered": 86.144578313253, "percent_covered_display": "86", "missing_lines": 23, "excluded_lines": 0}, "missing_lines": [26, 27, 46, 47, 56, 94, 99, 104, 185, 186, 336, 341, 370, 375, 377, 378, 380, 381, 382, 383, 385, 392, 399], "excluded_lines": [], "functions": {"floor_at_decimal": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [46, 47], "excluded_lines": []}, "ceil_at_decimal": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [56], "excluded_lines": []}, "floor_to_step": {"executed_lines": [61], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ceil_to_step": {"executed_lines": [66], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "clean_and_check": {"executed_lines": [74, 75, 76, 77, 78], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AbstractDistribution.draw": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [94], "excluded_lines": []}, "AbstractDistribution.left_percentile": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [99], "excluded_lines": []}, "AbstractDistribution.right_percentile": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [104], "excluded_lines": []}, "AbstractDistribution._get_label": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AbstractDistribution.label": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.values": {"executed_lines": [123], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.from_hdf5": {"executed_lines": [135, 136, 137, 138, 139, 140], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.left_percentile": {"executed_lines": [144], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.right_percentile": {"executed_lines": [148], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.draw": {"executed_lines": [152, 154, 155, 157, 158, 160], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.from_hdf5": {"executed_lines": [179, 180, 181, 182, 183, 184, 190], "summary": {"covered_lines": 7, "num_statements": 9, "percent_covered": 77.77777777777777, "percent_covered_display": "78", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [185, 186], "excluded_lines": []}, "BetaPosterior._get_label": {"executed_lines": [199], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.num_fail": {"executed_lines": [204], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.pdf": {"executed_lines": [208], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.left_percentile": {"executed_lines": [218], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.right_percentile": {"executed_lines": [227], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.draw": {"executed_lines": [239, 240, 241, 243, 244, 246, 247, 249], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_size": {"executed_lines": [268, 269, 270, 271, 273, 274, 275, 276], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_label": {"executed_lines": [281, 282, 288, 289, 290, 291], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_xlims": {"executed_lines": [303, 306, 307, 310, 311], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "draw": {"executed_lines": [335, 338, 340, 343, 345, 354, 355, 357, 358, 360], "summary": {"covered_lines": 10, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [336, 341], "excluded_lines": []}, "split_legends": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 10, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 10, "excluded_lines": 0}, "missing_lines": [370, 375, 377, 378, 380, 381, 382, 383, 385, 392], "excluded_lines": []}, "use_mpl_stylesheet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [399], "excluded_lines": []}, "save_figure": {"executed_lines": [410, 411], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 64, 69, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 115, 116, 118, 120, 121, 125, 126, 142, 146, 150, 163, 164, 166, 167, 169, 170, 198, 201, 202, 206, 216, 225, 234, 252, 279, 294, 314, 363, 395, 396, 397, 402, 403, 404], "summary": {"covered_lines": 69, "num_statements": 71, "percent_covered": 97.1830985915493, "percent_covered_display": "97", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [26, 27], "excluded_lines": []}}, "classes": {"AbstractDistribution": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 4, "percent_covered": 25.0, "percent_covered_display": "25", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [94, 99, 104], "excluded_lines": []}, "Histogram": {"executed_lines": [123, 135, 136, 137, 138, 139, 140, 144, 148, 152, 154, 155, 157, 158, 160], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior": {"executed_lines": [179, 180, 181, 182, 183, 184, 190, 199, 204, 208, 218, 227, 239, 240, 241, 243, 244, 246, 247, 249], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [185, 186], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 61, 64, 66, 69, 74, 75, 76, 77, 78, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 115, 116, 118, 120, 121, 125, 126, 142, 146, 150, 163, 164, 166, 167, 169, 170, 198, 201, 202, 206, 216, 225, 234, 252, 268, 269, 270, 271, 273, 274, 275, 276, 279, 281, 282, 288, 289, 290, 291, 294, 303, 306, 307, 310, 311, 314, 335, 338, 340, 343, 345, 354, 355, 357, 358, 360, 363, 395, 396, 397, 402, 403, 404, 410, 411], "summary": {"covered_lines": 107, "num_statements": 125, "percent_covered": 85.6, "percent_covered_display": "86", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [26, 27, 46, 47, 56, 336, 341, 370, 375, 377, 378, 380, 381, 382, 383, 385, 392, 399], "excluded_lines": []}}}, "src/lyscripts/sample.py": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 68, 69, 71, 73, 75, 78, 79, 81, 83, 84, 85, 86, 89, 90, 92, 93, 95, 97, 98, 100, 101, 103, 106, 107, 109, 110, 112, 114, 115, 117, 118, 120, 123, 126, 135, 140, 141, 142, 146, 147, 148, 150, 153, 162, 163, 175, 184, 185, 187, 190, 193, 209, 215, 217, 225, 257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300, 304, 305, 307, 311, 316, 322, 325, 327, 328, 334, 347, 348, 350, 351, 352, 359, 365, 366, 368, 385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410, 419], "summary": {"covered_lines": 125, "num_statements": 136, "percent_covered": 91.91176470588235, "percent_covered_display": "92", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [35, 36, 74, 132, 172, 188, 301, 309, 313, 420, 421], "excluded_lines": [], "functions": {"CompletedItersColumn.__init__": {"executed_lines": [68, 69], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "CompletedItersColumn.render": {"executed_lines": [73, 75], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [74], "excluded_lines": []}, "ItersPerSecondColumn.render": {"executed_lines": [83, 84, 85, 86], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime.update": {"executed_lines": [97, 98], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime.relative_diff": {"executed_lines": [103], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted.update": {"executed_lines": [114, 115], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted.newly_accepted": {"executed_lines": [120], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_prob_fn": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [132], "excluded_lines": []}, "ensure_initial_state": {"executed_lines": [140, 141, 142, 146, 147, 148, 150], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ensure_history_table": {"executed_lines": [162, 163], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [172], "excluded_lines": []}, "update_history_table": {"executed_lines": [184, 185, 187, 190], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [188], "excluded_lines": []}, "is_converged": {"executed_lines": [209], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "_get_columns": {"executed_lines": [217], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "run_sampling": {"executed_lines": [257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [301], "excluded_lines": []}, "DummyPool.__enter__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [309], "excluded_lines": []}, "DummyPool.__exit__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [313], "excluded_lines": []}, "get_pool": {"executed_lines": [322], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "init_sampler": {"executed_lines": [327, 328, 334], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SampleCLI.cli_cmd": {"executed_lines": [385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 71, 78, 79, 81, 89, 90, 92, 93, 95, 100, 101, 106, 107, 109, 110, 112, 117, 118, 123, 126, 135, 153, 175, 193, 215, 225, 304, 305, 307, 311, 316, 325, 347, 348, 350, 351, 352, 359, 365, 366, 368, 419], "summary": {"covered_lines": 61, "num_statements": 65, "percent_covered": 93.84615384615384, "percent_covered_display": "94", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [35, 36, 420, 421], "excluded_lines": []}}, "classes": {"CompletedItersColumn": {"executed_lines": [68, 69, 73, 75], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [74], "excluded_lines": []}, "ItersPerSecondColumn": {"executed_lines": [83, 84, 85, 86], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime": {"executed_lines": [97, 98, 103], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted": {"executed_lines": [114, 115, 120], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DummyPool": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [309, 313], "excluded_lines": []}, "SampleCLI": {"executed_lines": [385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 71, 78, 79, 81, 89, 90, 92, 93, 95, 100, 101, 106, 107, 109, 110, 112, 117, 118, 123, 126, 135, 140, 141, 142, 146, 147, 148, 150, 153, 162, 163, 175, 184, 185, 187, 190, 193, 209, 215, 217, 225, 257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300, 304, 305, 307, 311, 316, 322, 325, 327, 328, 334, 347, 348, 350, 351, 352, 359, 365, 366, 368, 419], "summary": {"covered_lines": 99, "num_statements": 107, "percent_covered": 92.5233644859813, "percent_covered_display": "93", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [35, 36, 132, 172, 188, 301, 420, 421], "excluded_lines": []}}}, "src/lyscripts/schedule.py": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 29, "percent_covered": 55.172413793103445, "percent_covered_display": "55", "missing_lines": 13, "excluded_lines": 0}, "missing_lines": [25, 26, 27, 35, 44, 45, 73, 75, 76, 78, 80, 84, 85], "excluded_lines": [], "functions": {"geometric_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [25, 26, 27], "excluded_lines": []}, "linear_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [35], "excluded_lines": []}, "power_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [44, 45], "excluded_lines": []}, "ScheduleCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [73, 75, 76, 78, 80], "excluded_lines": []}, "": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [84, 85], "excluded_lines": []}}, "classes": {"ScheduleCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [73, 75, 76, 78, 80], "excluded_lines": []}, "": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 24, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [25, 26, 27, 35, 44, 45, 84, 85], "excluded_lines": []}}}, "src/lyscripts/schema.py": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 21, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [60, 61, 65], "excluded_lines": [], "functions": {"main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [60, 61], "excluded_lines": []}, "": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 19, "percent_covered": 94.73684210526316, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [65], "excluded_lines": []}}, "classes": {"SchemaSettings": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 21, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [60, 61, 65], "excluded_lines": []}}}, "src/lyscripts/utils.py": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 23, 24, 26, 27, 30, 33, 42, 43, 45, 46, 47, 48, 50, 53, 63, 65, 66, 67, 68, 70, 72, 75, 91, 93, 94, 95, 97, 98, 100, 102, 105, 115, 117, 118, 119, 120, 122, 124, 127, 137, 138, 139, 140, 143, 146, 151, 152, 154, 155, 156, 159, 160, 162, 163, 164, 165, 168, 169, 177, 178, 179, 180, 183, 184, 192, 193, 195, 199], "summary": {"covered_lines": 79, "num_statements": 84, "percent_covered": 94.04761904761905, "percent_covered_display": "94", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [25, 141, 142, 196, 197], "excluded_lines": [], "functions": {"binom_pmf": {"executed_lines": [23, 24, 26, 27, 30], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [25], "excluded_lines": []}, "get_dict_depth": {"executed_lines": [42, 43, 45, 46, 47, 48, 50], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "delete_private_keys": {"executed_lines": [63, 65, 66, 67, 68, 70, 72], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "flatten": {"executed_lines": [91, 93, 94, 95, 97, 98, 100, 102], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "unflatten": {"executed_lines": [115, 117, 118, 119, 120, 122, 124], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_modalities_subset": {"executed_lines": [137, 138, 139, 140, 143], "summary": {"covered_lines": 5, "num_statements": 7, "percent_covered": 71.42857142857143, "percent_covered_display": "71", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}, "load_patient_data": {"executed_lines": [151, 152, 154, 155, 156], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "load_yaml_params": {"executed_lines": [162, 163, 164, 165], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "load_model_samples": {"executed_lines": [177, 178, 179, 180], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_hdf5_backend": {"executed_lines": [192, 193, 195, 199], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [196, 197], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 33, 53, 75, 105, 127, 146, 159, 160, 168, 169, 183, 184], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 23, 24, 26, 27, 30, 33, 42, 43, 45, 46, 47, 48, 50, 53, 63, 65, 66, 67, 68, 70, 72, 75, 91, 93, 94, 95, 97, 98, 100, 102, 105, 115, 117, 118, 119, 120, 122, 124, 127, 137, 138, 139, 140, 143, 146, 151, 152, 154, 155, 156, 159, 160, 162, 163, 164, 165, 168, 169, 177, 178, 179, 180, 183, 184, 192, 193, 195, 199], "summary": {"covered_lines": 79, "num_statements": 84, "percent_covered": 94.04761904761905, "percent_covered_display": "94", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [25, 141, 142, 196, 197], "excluded_lines": []}}}}, "totals": {"covered_lines": 1159, "num_statements": 1551, "percent_covered": 74.72598323662153, "percent_covered_display": "75", "missing_lines": 392, "excluded_lines": 0}}, "coverage_path": "."}
\ No newline at end of file
diff --git a/endpoint.json b/endpoint.json
new file mode 100644
index 0000000..7adc848
--- /dev/null
+++ b/endpoint.json
@@ -0,0 +1 @@
+{"schemaVersion": 1, "label": "Coverage", "message": "74%", "color": "orange"}
\ No newline at end of file
diff --git a/htmlcov/class_index.html b/htmlcov/class_index.html
new file mode 100644
index 0000000..7e51ec3
--- /dev/null
+++ b/htmlcov/class_index.html
@@ -0,0 +1,596 @@
+
+
+
+
+ Coverage report
+
+
+
+
+
+
+
+
+
+ No items found using the specified filter.
+
+ 6 empty classes skipped.
+
+
+
+
diff --git a/htmlcov/coverage_html_cb_6fb7b396.js b/htmlcov/coverage_html_cb_6fb7b396.js
new file mode 100644
index 0000000..1face13
--- /dev/null
+++ b/htmlcov/coverage_html_cb_6fb7b396.js
@@ -0,0 +1,733 @@
+// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+// For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+// Coverage.py HTML report browser code.
+/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
+/*global coverage: true, document, window, $ */
+
+coverage = {};
+
+// General helpers
+function debounce(callback, wait) {
+ let timeoutId = null;
+ return function(...args) {
+ clearTimeout(timeoutId);
+ timeoutId = setTimeout(() => {
+ callback.apply(this, args);
+ }, wait);
+ };
+};
+
+function checkVisible(element) {
+ const rect = element.getBoundingClientRect();
+ const viewBottom = Math.max(document.documentElement.clientHeight, window.innerHeight);
+ const viewTop = 30;
+ return !(rect.bottom < viewTop || rect.top >= viewBottom);
+}
+
+function on_click(sel, fn) {
+ const elt = document.querySelector(sel);
+ if (elt) {
+ elt.addEventListener("click", fn);
+ }
+}
+
+// Helpers for table sorting
+function getCellValue(row, column = 0) {
+ const cell = row.cells[column] // nosemgrep: eslint.detect-object-injection
+ if (cell.childElementCount == 1) {
+ var child = cell.firstElementChild;
+ if (child.tagName === "A") {
+ child = child.firstElementChild;
+ }
+ if (child instanceof HTMLDataElement && child.value) {
+ return child.value;
+ }
+ }
+ return cell.innerText || cell.textContent;
+}
+
+function rowComparator(rowA, rowB, column = 0) {
+ let valueA = getCellValue(rowA, column);
+ let valueB = getCellValue(rowB, column);
+ if (!isNaN(valueA) && !isNaN(valueB)) {
+ return valueA - valueB;
+ }
+ return valueA.localeCompare(valueB, undefined, {numeric: true});
+}
+
+function sortColumn(th) {
+ // Get the current sorting direction of the selected header,
+ // clear state on other headers and then set the new sorting direction.
+ const currentSortOrder = th.getAttribute("aria-sort");
+ [...th.parentElement.cells].forEach(header => header.setAttribute("aria-sort", "none"));
+ var direction;
+ if (currentSortOrder === "none") {
+ direction = th.dataset.defaultSortOrder || "ascending";
+ }
+ else if (currentSortOrder === "ascending") {
+ direction = "descending";
+ }
+ else {
+ direction = "ascending";
+ }
+ th.setAttribute("aria-sort", direction);
+
+ const column = [...th.parentElement.cells].indexOf(th)
+
+ // Sort all rows and afterwards append them in order to move them in the DOM.
+ Array.from(th.closest("table").querySelectorAll("tbody tr"))
+ .sort((rowA, rowB) => rowComparator(rowA, rowB, column) * (direction === "ascending" ? 1 : -1))
+ .forEach(tr => tr.parentElement.appendChild(tr));
+
+ // Save the sort order for next time.
+ if (th.id !== "region") {
+ let th_id = "file"; // Sort by file if we don't have a column id
+ let current_direction = direction;
+ const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE);
+ if (stored_list) {
+ ({th_id, direction} = JSON.parse(stored_list))
+ }
+ localStorage.setItem(coverage.INDEX_SORT_STORAGE, JSON.stringify({
+ "th_id": th.id,
+ "direction": current_direction
+ }));
+ if (th.id !== th_id || document.getElementById("region")) {
+ // Sort column has changed, unset sorting by function or class.
+ localStorage.setItem(coverage.SORTED_BY_REGION, JSON.stringify({
+ "by_region": false,
+ "region_direction": current_direction
+ }));
+ }
+ }
+ else {
+ // Sort column has changed to by function or class, remember that.
+ localStorage.setItem(coverage.SORTED_BY_REGION, JSON.stringify({
+ "by_region": true,
+ "region_direction": direction
+ }));
+ }
+}
+
+// Find all the elements with data-shortcut attribute, and use them to assign a shortcut key.
+coverage.assign_shortkeys = function () {
+ document.querySelectorAll("[data-shortcut]").forEach(element => {
+ document.addEventListener("keypress", event => {
+ if (event.target.tagName.toLowerCase() === "input") {
+ return; // ignore keypress from search filter
+ }
+ if (event.key === element.dataset.shortcut) {
+ element.click();
+ }
+ });
+ });
+};
+
+// Create the events for the filter box.
+coverage.wire_up_filter = function () {
+ // Populate the filter and hide100 inputs if there are saved values for them.
+ const saved_filter_value = localStorage.getItem(coverage.FILTER_STORAGE);
+ if (saved_filter_value) {
+ document.getElementById("filter").value = saved_filter_value;
+ }
+ const saved_hide100_value = localStorage.getItem(coverage.HIDE100_STORAGE);
+ if (saved_hide100_value) {
+ document.getElementById("hide100").checked = JSON.parse(saved_hide100_value);
+ }
+
+ // Cache elements.
+ const table = document.querySelector("table.index");
+ const table_body_rows = table.querySelectorAll("tbody tr");
+ const no_rows = document.getElementById("no_rows");
+
+ // Observe filter keyevents.
+ const filter_handler = (event => {
+ // Keep running total of each metric, first index contains number of shown rows
+ const totals = new Array(table.rows[0].cells.length).fill(0);
+ // Accumulate the percentage as fraction
+ totals[totals.length - 1] = { "numer": 0, "denom": 0 }; // nosemgrep: eslint.detect-object-injection
+
+ var text = document.getElementById("filter").value;
+ // Store filter value
+ localStorage.setItem(coverage.FILTER_STORAGE, text);
+ const casefold = (text === text.toLowerCase());
+ const hide100 = document.getElementById("hide100").checked;
+ // Store hide value.
+ localStorage.setItem(coverage.HIDE100_STORAGE, JSON.stringify(hide100));
+
+ // Hide / show elements.
+ table_body_rows.forEach(row => {
+ var show = false;
+ // Check the text filter.
+ for (let column = 0; column < totals.length; column++) {
+ cell = row.cells[column];
+ if (cell.classList.contains("name")) {
+ var celltext = cell.textContent;
+ if (casefold) {
+ celltext = celltext.toLowerCase();
+ }
+ if (celltext.includes(text)) {
+ show = true;
+ }
+ }
+ }
+
+ // Check the "hide covered" filter.
+ if (show && hide100) {
+ const [numer, denom] = row.cells[row.cells.length - 1].dataset.ratio.split(" ");
+ show = (numer !== denom);
+ }
+
+ if (!show) {
+ // hide
+ row.classList.add("hidden");
+ return;
+ }
+
+ // show
+ row.classList.remove("hidden");
+ totals[0]++;
+
+ for (let column = 0; column < totals.length; column++) {
+ // Accumulate dynamic totals
+ cell = row.cells[column] // nosemgrep: eslint.detect-object-injection
+ if (cell.classList.contains("name")) {
+ continue;
+ }
+ if (column === totals.length - 1) {
+ // Last column contains percentage
+ const [numer, denom] = cell.dataset.ratio.split(" ");
+ totals[column]["numer"] += parseInt(numer, 10); // nosemgrep: eslint.detect-object-injection
+ totals[column]["denom"] += parseInt(denom, 10); // nosemgrep: eslint.detect-object-injection
+ }
+ else {
+ totals[column] += parseInt(cell.textContent, 10); // nosemgrep: eslint.detect-object-injection
+ }
+ }
+ });
+
+ // Show placeholder if no rows will be displayed.
+ if (!totals[0]) {
+ // Show placeholder, hide table.
+ no_rows.style.display = "block";
+ table.style.display = "none";
+ return;
+ }
+
+ // Hide placeholder, show table.
+ no_rows.style.display = null;
+ table.style.display = null;
+
+ const footer = table.tFoot.rows[0];
+ // Calculate new dynamic sum values based on visible rows.
+ for (let column = 0; column < totals.length; column++) {
+ // Get footer cell element.
+ const cell = footer.cells[column]; // nosemgrep: eslint.detect-object-injection
+ if (cell.classList.contains("name")) {
+ continue;
+ }
+
+ // Set value into dynamic footer cell element.
+ if (column === totals.length - 1) {
+ // Percentage column uses the numerator and denominator,
+ // and adapts to the number of decimal places.
+ const match = /\.([0-9]+)/.exec(cell.textContent);
+ const places = match ? match[1].length : 0;
+ const { numer, denom } = totals[column]; // nosemgrep: eslint.detect-object-injection
+ cell.dataset.ratio = `${numer} ${denom}`;
+ // Check denom to prevent NaN if filtered files contain no statements
+ cell.textContent = denom
+ ? `${(numer * 100 / denom).toFixed(places)}%`
+ : `${(100).toFixed(places)}%`;
+ }
+ else {
+ cell.textContent = totals[column]; // nosemgrep: eslint.detect-object-injection
+ }
+ }
+ });
+
+ document.getElementById("filter").addEventListener("input", debounce(filter_handler));
+ document.getElementById("hide100").addEventListener("input", debounce(filter_handler));
+
+ // Trigger change event on setup, to force filter on page refresh
+ // (filter value may still be present).
+ document.getElementById("filter").dispatchEvent(new Event("input"));
+ document.getElementById("hide100").dispatchEvent(new Event("input"));
+};
+coverage.FILTER_STORAGE = "COVERAGE_FILTER_VALUE";
+coverage.HIDE100_STORAGE = "COVERAGE_HIDE100_VALUE";
+
+// Set up the click-to-sort columns.
+coverage.wire_up_sorting = function () {
+ document.querySelectorAll("[data-sortable] th[aria-sort]").forEach(
+ th => th.addEventListener("click", e => sortColumn(e.target))
+ );
+
+ // Look for a localStorage item containing previous sort settings:
+ let th_id = "file", direction = "ascending";
+ const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE);
+ if (stored_list) {
+ ({th_id, direction} = JSON.parse(stored_list));
+ }
+ let by_region = false, region_direction = "ascending";
+ const sorted_by_region = localStorage.getItem(coverage.SORTED_BY_REGION);
+ if (sorted_by_region) {
+ ({
+ by_region,
+ region_direction
+ } = JSON.parse(sorted_by_region));
+ }
+
+ const region_id = "region";
+ if (by_region && document.getElementById(region_id)) {
+ direction = region_direction;
+ }
+ // If we are in a page that has a column with id of "region", sort on
+ // it if the last sort was by function or class.
+ let th;
+ if (document.getElementById(region_id)) {
+ th = document.getElementById(by_region ? region_id : th_id);
+ }
+ else {
+ th = document.getElementById(th_id);
+ }
+ th.setAttribute("aria-sort", direction === "ascending" ? "descending" : "ascending");
+ th.click()
+};
+
+coverage.INDEX_SORT_STORAGE = "COVERAGE_INDEX_SORT_2";
+coverage.SORTED_BY_REGION = "COVERAGE_SORT_REGION";
+
+// Loaded on index.html
+coverage.index_ready = function () {
+ coverage.assign_shortkeys();
+ coverage.wire_up_filter();
+ coverage.wire_up_sorting();
+
+ on_click(".button_prev_file", coverage.to_prev_file);
+ on_click(".button_next_file", coverage.to_next_file);
+
+ on_click(".button_show_hide_help", coverage.show_hide_help);
+};
+
+// -- pyfile stuff --
+
+coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS";
+
+coverage.pyfile_ready = function () {
+ // If we're directed to a particular line number, highlight the line.
+ var frag = location.hash;
+ if (frag.length > 2 && frag[1] === "t") {
+ document.querySelector(frag).closest(".n").classList.add("highlight");
+ coverage.set_sel(parseInt(frag.substr(2), 10));
+ }
+ else {
+ coverage.set_sel(0);
+ }
+
+ on_click(".button_toggle_run", coverage.toggle_lines);
+ on_click(".button_toggle_mis", coverage.toggle_lines);
+ on_click(".button_toggle_exc", coverage.toggle_lines);
+ on_click(".button_toggle_par", coverage.toggle_lines);
+
+ on_click(".button_next_chunk", coverage.to_next_chunk_nicely);
+ on_click(".button_prev_chunk", coverage.to_prev_chunk_nicely);
+ on_click(".button_top_of_page", coverage.to_top);
+ on_click(".button_first_chunk", coverage.to_first_chunk);
+
+ on_click(".button_prev_file", coverage.to_prev_file);
+ on_click(".button_next_file", coverage.to_next_file);
+ on_click(".button_to_index", coverage.to_index);
+
+ on_click(".button_show_hide_help", coverage.show_hide_help);
+
+ coverage.filters = undefined;
+ try {
+ coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE);
+ } catch(err) {}
+
+ if (coverage.filters) {
+ coverage.filters = JSON.parse(coverage.filters);
+ }
+ else {
+ coverage.filters = {run: false, exc: true, mis: true, par: true};
+ }
+
+ for (cls in coverage.filters) {
+ coverage.set_line_visibilty(cls, coverage.filters[cls]); // nosemgrep: eslint.detect-object-injection
+ }
+
+ coverage.assign_shortkeys();
+ coverage.init_scroll_markers();
+ coverage.wire_up_sticky_header();
+
+ document.querySelectorAll("[id^=ctxs]").forEach(
+ cbox => cbox.addEventListener("click", coverage.expand_contexts)
+ );
+
+ // Rebuild scroll markers when the window height changes.
+ window.addEventListener("resize", coverage.build_scroll_markers);
+};
+
+coverage.toggle_lines = function (event) {
+ const btn = event.target.closest("button");
+ const category = btn.value
+ const show = !btn.classList.contains("show_" + category);
+ coverage.set_line_visibilty(category, show);
+ coverage.build_scroll_markers();
+ coverage.filters[category] = show;
+ try {
+ localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters));
+ } catch(err) {}
+};
+
+coverage.set_line_visibilty = function (category, should_show) {
+ const cls = "show_" + category;
+ const btn = document.querySelector(".button_toggle_" + category);
+ if (btn) {
+ if (should_show) {
+ document.querySelectorAll("#source ." + category).forEach(e => e.classList.add(cls));
+ btn.classList.add(cls);
+ }
+ else {
+ document.querySelectorAll("#source ." + category).forEach(e => e.classList.remove(cls));
+ btn.classList.remove(cls);
+ }
+ }
+};
+
+// Return the nth line div.
+coverage.line_elt = function (n) {
+ return document.getElementById("t" + n)?.closest("p");
+};
+
+// Set the selection. b and e are line numbers.
+coverage.set_sel = function (b, e) {
+ // The first line selected.
+ coverage.sel_begin = b;
+ // The next line not selected.
+ coverage.sel_end = (e === undefined) ? b+1 : e;
+};
+
+coverage.to_top = function () {
+ coverage.set_sel(0, 1);
+ coverage.scroll_window(0);
+};
+
+coverage.to_first_chunk = function () {
+ coverage.set_sel(0, 1);
+ coverage.to_next_chunk();
+};
+
+coverage.to_prev_file = function () {
+ window.location = document.getElementById("prevFileLink").href;
+}
+
+coverage.to_next_file = function () {
+ window.location = document.getElementById("nextFileLink").href;
+}
+
+coverage.to_index = function () {
+ location.href = document.getElementById("indexLink").href;
+}
+
+coverage.show_hide_help = function () {
+ const helpCheck = document.getElementById("help_panel_state")
+ helpCheck.checked = !helpCheck.checked;
+}
+
+// Return a string indicating what kind of chunk this line belongs to,
+// or null if not a chunk.
+coverage.chunk_indicator = function (line_elt) {
+ const classes = line_elt?.className;
+ if (!classes) {
+ return null;
+ }
+ const match = classes.match(/\bshow_\w+\b/);
+ if (!match) {
+ return null;
+ }
+ return match[0];
+};
+
+coverage.to_next_chunk = function () {
+ const c = coverage;
+
+ // Find the start of the next colored chunk.
+ var probe = c.sel_end;
+ var chunk_indicator, probe_line;
+ while (true) {
+ probe_line = c.line_elt(probe);
+ if (!probe_line) {
+ return;
+ }
+ chunk_indicator = c.chunk_indicator(probe_line);
+ if (chunk_indicator) {
+ break;
+ }
+ probe++;
+ }
+
+ // There's a next chunk, `probe` points to it.
+ var begin = probe;
+
+ // Find the end of this chunk.
+ var next_indicator = chunk_indicator;
+ while (next_indicator === chunk_indicator) {
+ probe++;
+ probe_line = c.line_elt(probe);
+ next_indicator = c.chunk_indicator(probe_line);
+ }
+ c.set_sel(begin, probe);
+ c.show_selection();
+};
+
+coverage.to_prev_chunk = function () {
+ const c = coverage;
+
+ // Find the end of the prev colored chunk.
+ var probe = c.sel_begin-1;
+ var probe_line = c.line_elt(probe);
+ if (!probe_line) {
+ return;
+ }
+ var chunk_indicator = c.chunk_indicator(probe_line);
+ while (probe > 1 && !chunk_indicator) {
+ probe--;
+ probe_line = c.line_elt(probe);
+ if (!probe_line) {
+ return;
+ }
+ chunk_indicator = c.chunk_indicator(probe_line);
+ }
+
+ // There's a prev chunk, `probe` points to its last line.
+ var end = probe+1;
+
+ // Find the beginning of this chunk.
+ var prev_indicator = chunk_indicator;
+ while (prev_indicator === chunk_indicator) {
+ probe--;
+ if (probe <= 0) {
+ return;
+ }
+ probe_line = c.line_elt(probe);
+ prev_indicator = c.chunk_indicator(probe_line);
+ }
+ c.set_sel(probe+1, end);
+ c.show_selection();
+};
+
+// Returns 0, 1, or 2: how many of the two ends of the selection are on
+// the screen right now?
+coverage.selection_ends_on_screen = function () {
+ if (coverage.sel_begin === 0) {
+ return 0;
+ }
+
+ const begin = coverage.line_elt(coverage.sel_begin);
+ const end = coverage.line_elt(coverage.sel_end-1);
+
+ return (
+ (checkVisible(begin) ? 1 : 0)
+ + (checkVisible(end) ? 1 : 0)
+ );
+};
+
+coverage.to_next_chunk_nicely = function () {
+ if (coverage.selection_ends_on_screen() === 0) {
+ // The selection is entirely off the screen:
+ // Set the top line on the screen as selection.
+
+ // This will select the top-left of the viewport
+ // As this is most likely the span with the line number we take the parent
+ const line = document.elementFromPoint(0, 0).parentElement;
+ if (line.parentElement !== document.getElementById("source")) {
+ // The element is not a source line but the header or similar
+ coverage.select_line_or_chunk(1);
+ }
+ else {
+ // We extract the line number from the id
+ coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10));
+ }
+ }
+ coverage.to_next_chunk();
+};
+
+coverage.to_prev_chunk_nicely = function () {
+ if (coverage.selection_ends_on_screen() === 0) {
+ // The selection is entirely off the screen:
+ // Set the lowest line on the screen as selection.
+
+ // This will select the bottom-left of the viewport
+ // As this is most likely the span with the line number we take the parent
+ const line = document.elementFromPoint(document.documentElement.clientHeight-1, 0).parentElement;
+ if (line.parentElement !== document.getElementById("source")) {
+ // The element is not a source line but the header or similar
+ coverage.select_line_or_chunk(coverage.lines_len);
+ }
+ else {
+ // We extract the line number from the id
+ coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10));
+ }
+ }
+ coverage.to_prev_chunk();
+};
+
+// Select line number lineno, or if it is in a colored chunk, select the
+// entire chunk
+coverage.select_line_or_chunk = function (lineno) {
+ var c = coverage;
+ var probe_line = c.line_elt(lineno);
+ if (!probe_line) {
+ return;
+ }
+ var the_indicator = c.chunk_indicator(probe_line);
+ if (the_indicator) {
+ // The line is in a highlighted chunk.
+ // Search backward for the first line.
+ var probe = lineno;
+ var indicator = the_indicator;
+ while (probe > 0 && indicator === the_indicator) {
+ probe--;
+ probe_line = c.line_elt(probe);
+ if (!probe_line) {
+ break;
+ }
+ indicator = c.chunk_indicator(probe_line);
+ }
+ var begin = probe + 1;
+
+ // Search forward for the last line.
+ probe = lineno;
+ indicator = the_indicator;
+ while (indicator === the_indicator) {
+ probe++;
+ probe_line = c.line_elt(probe);
+ indicator = c.chunk_indicator(probe_line);
+ }
+
+ coverage.set_sel(begin, probe);
+ }
+ else {
+ coverage.set_sel(lineno);
+ }
+};
+
+coverage.show_selection = function () {
+ // Highlight the lines in the chunk
+ document.querySelectorAll("#source .highlight").forEach(e => e.classList.remove("highlight"));
+ for (let probe = coverage.sel_begin; probe < coverage.sel_end; probe++) {
+ coverage.line_elt(probe).querySelector(".n").classList.add("highlight");
+ }
+
+ coverage.scroll_to_selection();
+};
+
+coverage.scroll_to_selection = function () {
+ // Scroll the page if the chunk isn't fully visible.
+ if (coverage.selection_ends_on_screen() < 2) {
+ const element = coverage.line_elt(coverage.sel_begin);
+ coverage.scroll_window(element.offsetTop - 60);
+ }
+};
+
+coverage.scroll_window = function (to_pos) {
+ window.scroll({top: to_pos, behavior: "smooth"});
+};
+
+coverage.init_scroll_markers = function () {
+ // Init some variables
+ coverage.lines_len = document.querySelectorAll("#source > p").length;
+
+ // Build html
+ coverage.build_scroll_markers();
+};
+
+coverage.build_scroll_markers = function () {
+ const temp_scroll_marker = document.getElementById("scroll_marker")
+ if (temp_scroll_marker) temp_scroll_marker.remove();
+ // Don't build markers if the window has no scroll bar.
+ if (document.body.scrollHeight <= window.innerHeight) {
+ return;
+ }
+
+ const marker_scale = window.innerHeight / document.body.scrollHeight;
+ const line_height = Math.min(Math.max(3, window.innerHeight / coverage.lines_len), 10);
+
+ let previous_line = -99, last_mark, last_top;
+
+ const scroll_marker = document.createElement("div");
+ scroll_marker.id = "scroll_marker";
+ document.getElementById("source").querySelectorAll(
+ "p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par"
+ ).forEach(element => {
+ const line_top = Math.floor(element.offsetTop * marker_scale);
+ const line_number = parseInt(element.querySelector(".n a").id.substr(1));
+
+ if (line_number === previous_line + 1) {
+ // If this solid missed block just make previous mark higher.
+ last_mark.style.height = `${line_top + line_height - last_top}px`;
+ }
+ else {
+ // Add colored line in scroll_marker block.
+ last_mark = document.createElement("div");
+ last_mark.id = `m${line_number}`;
+ last_mark.classList.add("marker");
+ last_mark.style.height = `${line_height}px`;
+ last_mark.style.top = `${line_top}px`;
+ scroll_marker.append(last_mark);
+ last_top = line_top;
+ }
+
+ previous_line = line_number;
+ });
+
+ // Append last to prevent layout calculation
+ document.body.append(scroll_marker);
+};
+
+coverage.wire_up_sticky_header = function () {
+ const header = document.querySelector("header");
+ const header_bottom = (
+ header.querySelector(".content h2").getBoundingClientRect().top -
+ header.getBoundingClientRect().top
+ );
+
+ function updateHeader() {
+ if (window.scrollY > header_bottom) {
+ header.classList.add("sticky");
+ }
+ else {
+ header.classList.remove("sticky");
+ }
+ }
+
+ window.addEventListener("scroll", updateHeader);
+ updateHeader();
+};
+
+coverage.expand_contexts = function (e) {
+ var ctxs = e.target.parentNode.querySelector(".ctxs");
+
+ if (!ctxs.classList.contains("expanded")) {
+ var ctxs_text = ctxs.textContent;
+ var width = Number(ctxs_text[0]);
+ ctxs.textContent = "";
+ for (var i = 1; i < ctxs_text.length; i += width) {
+ key = ctxs_text.substring(i, i + width).trim();
+ ctxs.appendChild(document.createTextNode(contexts[key]));
+ ctxs.appendChild(document.createElement("br"));
+ }
+ ctxs.classList.add("expanded");
+ }
+};
+
+document.addEventListener("DOMContentLoaded", () => {
+ if (document.body.classList.contains("indexfile")) {
+ coverage.index_ready();
+ }
+ else {
+ coverage.pyfile_ready();
+ }
+});
diff --git a/htmlcov/favicon_32_cb_58284776.png b/htmlcov/favicon_32_cb_58284776.png
new file mode 100644
index 0000000000000000000000000000000000000000..8649f0475d8d20793b2ec431fe25a186a414cf10
GIT binary patch
literal 1732
zcmV;#20QtQP)K2KOkBOVxIZChq#W-v7@TU%U6P(wycKT1hUJUToW3ke1U1ONa4
z000000000000000bb)GRa9mqwR9|UWHy;^RUrt?IT__Y0JUcxmBP0(51q1>E00030
z|NrOz)aw7%8sJzM<5^g%z7^qE`}_Ot|JUUG(NUkWzR|7K?Zo%@_v-8G-1N%N=D$;;
zw;keH4dGY$`1t4M=HK_s*zm^0#KgqfwWhe3qO_HtvXYvtjgX>;-~C$L`&k>^R)9)7
zdPh2TL^pCnHC#0+_4D)M`p?qp!pq{jO_{8;$fbaflbx`Tn52n|n}8VFRTA1&ugOP<
zPd{uvFjz7t*Vot1&d$l-xWCk}s;sQLO(Bskh6gqNJv>#iB=ypG1e3K!K4yc7!~M
zfj4S*g^zZ7eP$+_Sl07Z646l;%urinP#D8a6TwRtnLIRcI!r4f@bK~9-`~;E(N?Lv
zSEst7s;rcxsi~}{Nsytfz@MtUoR*iFc8!#vvx}Umhm4blk(_~MdVD-@dW&>!Nn~ro
z_E~-ESVQAj6Wmn;(olz(O&_{U2*pZBc1aYjMh>Dq3z|6`jW`RDHV=t3I6yRKJ~LOX
zz_z!!vbVXPqob#=pj3^VMT?x6t(irRmSKsMo1~LLkB&=#j!=M%NP35mfqim$drWb9
zYIb>no_LUwc!r^NkDzs4YHu@=ZHRzrafWDZd1EhEVq=tGX?tK$pIa)DTh#bkvh!J-
z?^%@YS!U*0E8$q$_*aOTQ&)Ra64g>ep;BdcQgvlg8qQHrP*E$;P{-m=A*@axn@$bO
zO-Y4JzS&EAi%YG}N?cn?YFS7ivPY=EMV6~YH;+Xxu|tefLS|Aza)Cg6us#)=JW!uH
zQa?H>d^j+YHCtyjL^LulF*05|F$RG!AX_OHVI&MtA~_@=5_lU|0000rbW%=J06GH4
z^5LD8b8apw8vNh1ua1mF{{Hy)_U`NA;Nacc+sCpuHXa-V{r&yz?c(9#+}oX+NmiRW
z+W-IqK1oDDR5;6GfCDCOP5}iL5fK(cB~ET81`MFgF2kGa9AjhSIk~-E-4&*tPPKdiilQJ11k_J082ZS
z>@TvivP!5ZFG?t@{t+GpR3XR&@*hA_VE1|Lo8@L@)l*h(Z@=?c-NS$Fk&&61IzUU9
z*nPqBM=OBZ-6ka1SJgGAS-Us5EN)r#dUX%>wQZLa2ytPCtMKp)Ob
z*xcu38Z&d5<-NBS)@jRD+*!W*cf-m_wmxDEqBf?czI%3U0J$Xik;lA`jg}VH?(S(V
zE!M3;X2B8w0TnnW&6(8;_Uc)WD;Ms6PKP+s(sFgO!}B!^ES~GDt4qLPxwYB)^7)XA
zZwo9zDy-B0B+jT6V=!=bo(zs_8{eBA78gT9GH$(DVhz;4VAYwz+bOIdZ-PNb|I&rl
z^XG=vFLF)1{&nT2*0vMz#}7^9hXzzf&ZdKlEj{LihP;|;Ywqn35ajP?H?7t|i-Un%
z&&kxee@9B{nwgv1+S-~0)E1{ob1^Wn`F2isurqThKK=3%&;`@{0{!D-
z&CSj80t;uPu&FaJFtSXKH#ajgGj}=sEad7US6jP0|Db@0j)?(5@sf<7`~a9>s;wCa
zm^)spe{uxGFmrJYI9cOh7s$>8Npkt-5EWB1UKc`{W{y5Ce$1+nM9Cr;);=Ju#N^62OSlJMn7omiUgP&ErsYzT~iGxcW
aE(`!K@+CXylaC4j0000
+
+
+
+ Coverage report
+
+
+
+
+
+
+
+
+
+ No items found using the specified filter.
+
+ 1 empty function skipped.
+
+
+
+
diff --git a/htmlcov/index.html b/htmlcov/index.html
new file mode 100644
index 0000000..ccdc805
--- /dev/null
+++ b/htmlcov/index.html
@@ -0,0 +1,300 @@
+
+
+
+
+ Coverage report
+
+
+
+
+
+
+
+
+
+ No items found using the specified filter.
+
+
+
+
+
diff --git a/htmlcov/keybd_closed_cb_ce680311.png b/htmlcov/keybd_closed_cb_ce680311.png
new file mode 100644
index 0000000000000000000000000000000000000000..ba119c47df81ed2bbd27a06988abf700139c4f99
GIT binary patch
literal 9004
zcmeHLc{tSF+aIY=A^R4_poB4tZAN2XC;O7M(inrW3}(h&Q4}dl*&-65$i9^&vW6_#
zcM4g`Qix=GhkBl;=lwnJ@Ap2}^}hc-b6vBXb3XUyzR%~}_c`-Dw+!?&>5p(90RRB>
zXe~7($~PP3eT?=X<@3~Q1w84vX~IoSx~1#~02+TopXK(db;4v6!{+W`RHLkkHO
zo;+s?)puc`+$yOwHv>I$5^8v^F3<|$44HA8AFnFB0cAP|C`p}aSMJK*-CUB{eQ!;K
z-9Ju3OQ+xVPr3P#o4>_lNBT;M+1vgV&B~6!naOGHb-LFA9TkfHv1IFA1Y!Iz!Zl3)
z%c#-^zNWPq7U_}6I7aHSmFWi125RZrNBKyvnV^?64)zviS;E!UD%LaGRl6@zn!3E{
zJ`B$5``cH_3a)t1#6I7d==JeB_IcSU%=I#DrRCBGm8GvCmA=+XHEvC2SIfsNa0(h9
z7P^C4U`W@@`9p>2f^zyb5B=lpc*RZMn-%%IqrxSWQF8{ec3i?-AB(_IVe
z)XgT>Y^u41MwOMFvU=I4?!^#jaS-%bjnx@
zmL44yVEslR_ynm18F!u}Ru#moEn3EE?1=9@$B1Z5aLi5b8{&?V(IAYBzIar!SiY3<
z`l0V)djHtrImy}(!7x-Pmq+njM)JFQ9mx*(C+9a3M)(_SW|lrN=gfxFhStu^zvynS
zm@gl;>d8i8wpUkX42vS3BEzE3-yctH%t0#N%s+6-&_<*Fe7+h=`=FM?DOg1)eGL~~
zQvIFm$D*lqEh07XrXY=jb%hdyP4)`wyMCb$=-z9(lOme9=tirVkb)_GOl2MJn;=Ky
z^0pV1owR7KP-BSxhI@@@+gG0roD-kXE1;!#R7KY1QiUbyDdTElm|ul7{mMdF1%UDJ
z_vp=Vo!TCF?D*?u%
zk~}4!xK2MSQd-QKC0${G=ZRv2x8%8ZqdfR!?Dv=5Mj^8WU)?iH;C?o6rSQy*^YwQb
zf@5V)q=xah#a3UEIBC~N7on(p4jQd4K$|i7k`d8mw|M{Mxapl46Z^X^9U}JgqH#;T
z`CTzafpMD+J-LjzF+3Xau>xM_sXisRj6m-287~i9g|%gHc}v77>n_+p7ZgmJszx!b
zSmL4wV;&*5Z|zaCk`rOYFdOjZLLQr!WSV6AlaqYh_OE)>rYdtx`gk$yAMO=-E1b~J
zIZY6gM*}1UWsJ)TW(pf1=h?lJy_0TFOr|nALGW>$IE1E7z+$`^2WJY+>$$nJo8Rs`
z)xS>AH{N~X3+b=2+8Q_|n(1JoGv55r>TuwBV~MXE&9?3Zw>cIxnOPNs#gh~C4Zo=k
z&!s;5)^6UG>!`?hh0Q|r|Qbm>}pgtOt23Vh!NSibozH$`#LSiYL)HR4bkfEJMa
zBHwC3TaHx|BzD|MXAr>mm&FbZXeEX-=W}Ji&!pji4sO$#0Wk^Q7j%{8#bJPn$C=E%
zPlB}0)@Ti^r_HMJrTMN?9~4LQbIiUiOKBVNm_QjABKY4;zC88yVjvB>ZETNzr%^(~
zI3U&Ont?P`r&4
z#Bp)jcVV_N_{c1_qW}_`dQm)D`NG?h{+S!YOaUgWna4i8SuoLcXAZ|#Jh&GNn7B}3
z?vZ8I{LpmCYT=@6)dLPd@|(;d<08ufov%+V?$mgUYQHYTrc%eA=CDUzK}v|G&9}yJ
z)|g*=+RH1IQ>rvkY9UIam=fkxWDyGIKQ2RU{GqOQjD8nG#sl+$V=?wpzJdT=wlNWr
z1%lw&+;kVs(z?e=YRWRA&jc75rQ~({*TS<(
z8X!j>B}?Bxrrp%wEE7yBefQ?*nM20~+ZoQK(NO_wA`RNhsqVkXHy|sod@mqen=B#@
zmLi=x2*o9rWqTMWoB&qdZph$~qkJJTVNc*8^hU?gH_fY{GYPEBE8Q{j0Y$tvjMv%3
z)j#EyBf^7n)2d8IXDYX2O0S%ZTnGhg4Ss#sEIATKpE_E4TU=GimrD5F6K(%*+T-!o
z?Se7^Vm`$ZKDwq+=~jf?w0qC$Kr&R-;IF#{iLF*8zKu8(=#chRO;>x
zdM;h{i{RLpJgS!B-ueTFs8&4U4+D8|7nP~UZ@P`J;*0sj^#f_WqT#xpA?@qHonGB&
zQ<^;OLtOG1w#)N~&@b0caUL7syAsAxV#R`n>-+eVL9aZwnlklzE>-6!1#!tVA`uNo
z>Gv^P)sohc~g_1YMC;^f(N<{2y5C^;QCEXo;LQ^#$0
zr>jCrdoeXuff!dJ^`#=Wy2Gumo^Qt7BZrI~G+Pyl_kL>is3P0^JlE;Sjm-YfF~I>t
z_KeNpK|5U&F4;v?WSl(jxUWDarfcIcl=-6!8>^S`57!M6;hZea5IFA@)2+*Rt85
zi-MBs_b^DU8LygXXQGkG+86N7<%M|baM(orG*ASffC`p!?@m{qd}IcYmZyi^d}#Q&
zNjk-0@CajpUI-gPm20ERVDO!L8@p`tMJ69FD(ASIkdoLdiRV6h9TPKRz>2WK4upHd
z6OZK33EP?`GoJkXh)S035}uLUO$;TlXwNdMg-WOhLB)7a`-%*a9lFmjf6n+4ZmIHN
z-V@$
z8PXsoR4*`5RwXz=A8|5;aXKtSHFccj%dG7cO~UBJnt)61K>-uPX)`vu{7fcX6_>zZ
zw_2V&Li+7mxbf!f7{Rk&VVyY!UtZywac%g!cH+xh#j$a`uf?XWl<``t`36W;p7=_*
zO6uf~2{sAdkZn=Ts@p0>8N8rzw2ZLS@$ibV-c-QmG@%|3gUUrRxu=e*ekhTa+f?8q
z3$JVGPr9w$VQG~QCq~Y=2ThLIH!T@(>{NihJ6nj*HA_C#Popv)CBa)+UI-bx8u8zfCT^*1|k
z&N9oFYsZEijPn31Yx_yO5pFs>0tOAV=oRx~Wpy5ie&S_449m4R^{LWQMA~}vocV1O
zIf#1ZV85E>tvZE4mz~zn{hs!pkIQM;EvZMimqiPAJu-9P@mId&nb$lsrICS=)zU3~
zn>a#9>}5*3N)9;PTMZ)$`5k}
z?iG}Rwj$>Y*|(D3S3e&fxhaPHma8@vwu(cwdlaCjX+NIK6=$H4U`rfzcWQVOhp{fnzuZhgCCGpw|p
zTi`>cv~xVzdx|^`C0vXdlMwPae3S?>3|7v$e*Bs6-5gS>>FMHk_r2M(ADOV{KV7+6
zA@5Q(mdx%7J}MY}K461iuQ}5GwDGI=Yc&g0MZHu)7gC3{5@QZj6SJl*o0MS2Cl_ia
zyK?9QmC9tJ6yn{EA-erJ4wk$+!E#X(s~9h^HOmQ_|6V_s1)k;%9Q6Niw}SyT?jxl4
z;HYz2$Nj$8Q_*Xo`TWEUx^Q9b+ik@$o39`mlY&P}G8wnjdE+Dlj?uL;$aB$n;x
zWoh-M_u>9}_Ok@d_uidMqz10zJc}RQijPW3Fs&~1am=j*+A$QWTvxf9)6n;n8zTQW
z!Q_J1%apTsJzLF`#^P_#mRv2Ya_keUE7iMSP!ha-WQoo0vZZG?gyR;+4q8F6tL#u<
zRj8Hu5f-p1$J;)4?WpGL{4@HmJ6&tF9A5Tc8Trp>;Y>{^s?Q1&bam}?OjsnKd?|Z82aix26wUOLxbEW~E)|CgJ#)MLf_me#
zv4?F$o@A~Um)6>HlM0=3Bd-vc91EM}D+t6-@!}O%i*&Wl%@#C8X+?5+nv`oPu!!=5
znbL+Fk_#J_%8vOq^FIv~5N(nk03kyo1p@l|1c+rO^zCG3bk2?|%AF;*|4si1XM<`a
z1NY0-8$wv?&129!(g_A1lXR!+pD*1*cF?T~e1d6*G1Fz)jcSaZoKpxtA%FNnKP2jo
zLXn@OR#1z@6zuH%mMB98}-t
zHJqClsZ!G5xMSgIs_=<8sBePXxfoXsuvy`|buON9BX%s-o>OVLA)k3W=wKnw1?so$
zEjm0aS=zu@Xu#;{A)QTjJ$a9_={++ACkRY*sk3jLk&Fu}RxR<-DXR<`5`$VNG*wJE
zidM6VzaQ!M0gbQM98@x@;#0qUS8O)p6mrYwTk*;8J~!ovbY6jon^Ki}uggd3#J5G8
z>awvtF85Y<9yE{Iag}J7O7)1O=ylk^255@XmV5J06-{xaaSNASZoTKKp~$tSxdUI~
zU1RZ&UuW37Ro&_ryj^cSt$Jd&pt|+h!A&dwcr&`S=R5E`=6Tm`+(qGm@$YZ8(8@a$
zXfo@Rwtvm7N3RMmVCb7radAs-@QtCXx^CQ-<)V>QPLZy@jH{#dc4#(y
zV)6Hp{ZMz!|NG8!>i01gZMy)G<8Hf2X7e&LH_gOaajW<<^Xi55@OnlY*|S|*TS8;u_nHbv7lgmmZ+Q<5
zi!*lLCJmdpyzl(L${$C?(pVo|oR%r~x_B_ocPePa_);27^=n4L=`toZ;xdBut9rSv
z?wDQ7j2I3WQBdhz%X7`2YaG_y|wA!7|s?k;A&WNMLMTZEzCaE^d??E&u?f=ejQBR~|<
z)=thyP2(p8r6mt?Ad}tXAP_GvF9|P630I;$1cpQ+Ay7C34hK^ZV3H4kjPV8&NP>G5
zKRDEIBrFl{M#j4mfP0)68&?mqJP1S?2mU0djAGTjDV;wZ?6vplNn~3Hn$nP>%!dMi
zz@bnC7zzi&k&s{QDWkf&zgrVXKUJjY3Gv3bL0}S4h>OdgEJ$Q^&p-VAr3J}^a*+rz
z!jW7(h*+GuCyqcC{MD(Ovj^!{pB^OKUe|uy&bD?CN>KZrf3?v>>l*xSvnQiH-o^ViN$%FRdm9url;%(*jf5H$*S)8;i0xWHdl>$p);nH9v0)YfW?Vz$!
zNCeUbi9`NEg(i^57y=fzM@1o*z*Bf6?QCV>2p9}(BLlYsOCfMjFv1pw1mlo)Py{8v
zppw{MDfEeWN+n>Ne~oI7%9cU}mz0r3!es2gNF0t5jkGipjIo2lz;-e)7}Ul_#!eDv
zw;#>kI>;#-pyfeu3Fsd^2F@6=oh#8r9;A!G0`-mm7%{=S;Ec(bJ=I_`FodKGQVNEY
zmXwr4{9*jpDl%4{ggQZ5Ac
z%wYTdl*!1c5^)%^E78Q&)ma|27c6j(a=)g4sGrp$r{jv>>M2
z6y)E5|Aooe!PSfKzvKA>`a6pfK3=E8vL14ksP&f=>gOP?}rG6ye@9ZR3
zJF*vsh*P$w390i!FV~~_Hv6t2Zl<4VUi|rNja#boFt{%q~xGb
z(2petq9A*_>~B*>?d?Olx^lmYg4)}sH2>G42RE;
literal 0
HcmV?d00001
diff --git a/htmlcov/status.json b/htmlcov/status.json
new file mode 100644
index 0000000..232ff31
--- /dev/null
+++ b/htmlcov/status.json
@@ -0,0 +1 @@
+{"note":"This file is an internal implementation detail to speed up HTML report generation. Its format can change at any time. You might be looking for the JSON report: https://coverage.rtfd.io/cmd.html#cmd-json","format":5,"version":"7.9.1","globals":"78e84f760f4a15b01f0493820b8aab4c","files":{"z_5bf5c588c698c6cc___init___py":{"hash":"cd9cf9aa912f02155af58c7537c296f8","index":{"url":"z_5bf5c588c698c6cc___init___py.html","file":"src/lyscripts/__init__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":34,"n_excluded":0,"n_missing":7,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc___main___py":{"hash":"688ae147ef12b10ab9787478138c52da","index":{"url":"z_5bf5c588c698c6cc___main___py.html","file":"src/lyscripts/__main__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":3,"n_excluded":0,"n_missing":3,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc__version_py":{"hash":"8d0943fd619368a6838316fdbe9bf82a","index":{"url":"z_5bf5c588c698c6cc__version_py.html","file":"src/lyscripts/_version.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":13,"n_excluded":0,"n_missing":3,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_cli_py":{"hash":"b9105b2f4782eea0c4c7357613d2dfa7","index":{"url":"z_5bf5c588c698c6cc_cli_py.html","file":"src/lyscripts/cli.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":26,"n_excluded":0,"n_missing":12,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_055061514423972c___init___py":{"hash":"4587c7447681dd0a28dee41eca8fd090","index":{"url":"z_055061514423972c___init___py.html","file":"src/lyscripts/compute/__init__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":9,"n_excluded":0,"n_missing":1,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_055061514423972c___main___py":{"hash":"6a0bdc66902725a1720ac3d75db6e969","index":{"url":"z_055061514423972c___main___py.html","file":"src/lyscripts/compute/__main__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":5,"n_excluded":0,"n_missing":5,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_055061514423972c_posteriors_py":{"hash":"369f3d9010aa72b2bd34bc161fe01025","index":{"url":"z_055061514423972c_posteriors_py.html","file":"src/lyscripts/compute/posteriors.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":46,"n_excluded":0,"n_missing":19,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_055061514423972c_prevalences_py":{"hash":"b0cbdf81221c8f32ec27191c706f6c50","index":{"url":"z_055061514423972c_prevalences_py.html","file":"src/lyscripts/compute/prevalences.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":80,"n_excluded":0,"n_missing":7,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_055061514423972c_priors_py":{"hash":"c9925520e21e1710ca9171c6770c3e20","index":{"url":"z_055061514423972c_priors_py.html","file":"src/lyscripts/compute/priors.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":35,"n_excluded":0,"n_missing":2,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_055061514423972c_risks_py":{"hash":"a754c2897e9a0bdd2e69b96b45dbd486","index":{"url":"z_055061514423972c_risks_py.html","file":"src/lyscripts/compute/risks.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":51,"n_excluded":0,"n_missing":33,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_055061514423972c_utils_py":{"hash":"5d1481ec413fa3e3564577c174854a96","index":{"url":"z_055061514423972c_utils_py.html","file":"src/lyscripts/compute/utils.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":120,"n_excluded":0,"n_missing":6,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_configs_py":{"hash":"1603a9ead1b54855e0054e4a88aeea3b","index":{"url":"z_5bf5c588c698c6cc_configs_py.html","file":"src/lyscripts/configs.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":255,"n_excluded":0,"n_missing":27,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a___init___py":{"hash":"e6ebf387a32b452c0b132db1bc40f4dc","index":{"url":"z_9b7bcb970ba14d6a___init___py.html","file":"src/lyscripts/data/__init__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":12,"n_excluded":0,"n_missing":1,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a___main___py":{"hash":"6f0c2597cfa98ee3fadd28928f0b59a9","index":{"url":"z_9b7bcb970ba14d6a___main___py.html","file":"src/lyscripts/data/__main__.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":18,"n_excluded":0,"n_missing":18,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a_enhance_py":{"hash":"daaae3cc10335b73cc4cd13a6649adf5","index":{"url":"z_9b7bcb970ba14d6a_enhance_py.html","file":"src/lyscripts/data/enhance.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":24,"n_excluded":0,"n_missing":8,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a_filter_py":{"hash":"302cfef0c03c9c928241037432629697","index":{"url":"z_9b7bcb970ba14d6a_filter_py.html","file":"src/lyscripts/data/filter.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":49,"n_excluded":0,"n_missing":30,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a_generate_py":{"hash":"5f82e5dfa2ada81bfe6a3c4fe1e56c0e","index":{"url":"z_9b7bcb970ba14d6a_generate_py.html","file":"src/lyscripts/data/generate.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":39,"n_excluded":0,"n_missing":4,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a_join_py":{"hash":"d5ae1efd4c338a18cb81aff4cf26f3cd","index":{"url":"z_9b7bcb970ba14d6a_join_py.html","file":"src/lyscripts/data/join.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":20,"n_excluded":0,"n_missing":9,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a_lyproxify_py":{"hash":"28bce4df322af2c94ae8d9fd2d4ea088","index":{"url":"z_9b7bcb970ba14d6a_lyproxify_py.html","file":"src/lyscripts/data/lyproxify.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":121,"n_excluded":0,"n_missing":67,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a_split_py":{"hash":"bc8f8c55ce94e7faf55afa156977968b","index":{"url":"z_9b7bcb970ba14d6a_split_py.html","file":"src/lyscripts/data/split.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":30,"n_excluded":0,"n_missing":14,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_9b7bcb970ba14d6a_utils_py":{"hash":"69c2607314172ebe2d9aae816b2846a3","index":{"url":"z_9b7bcb970ba14d6a_utils_py.html","file":"src/lyscripts/data/utils.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":9,"n_excluded":0,"n_missing":0,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_decorators_py":{"hash":"ab992d78a550f290f0a15fd360bd6868","index":{"url":"z_5bf5c588c698c6cc_decorators_py.html","file":"src/lyscripts/decorators.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":41,"n_excluded":0,"n_missing":4,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_evaluate_py":{"hash":"feb4d5f70d877520af63e2ffc1bb7beb","index":{"url":"z_5bf5c588c698c6cc_evaluate_py.html","file":"src/lyscripts/evaluate.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":75,"n_excluded":0,"n_missing":57,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_plots_py":{"hash":"0b5c3adc29ddbba1145ad27d826f98eb","index":{"url":"z_5bf5c588c698c6cc_plots_py.html","file":"src/lyscripts/plots.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":166,"n_excluded":0,"n_missing":23,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_sample_py":{"hash":"7f2875a7dbb5c94328f9325e0656c871","index":{"url":"z_5bf5c588c698c6cc_sample_py.html","file":"src/lyscripts/sample.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":136,"n_excluded":0,"n_missing":11,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_schedule_py":{"hash":"240eec11df40be8426b5201a24be2ddd","index":{"url":"z_5bf5c588c698c6cc_schedule_py.html","file":"src/lyscripts/schedule.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":29,"n_excluded":0,"n_missing":13,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_schema_py":{"hash":"abe23b86d4cae3c39895b659c8a8552e","index":{"url":"z_5bf5c588c698c6cc_schema_py.html","file":"src/lyscripts/schema.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":21,"n_excluded":0,"n_missing":3,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}},"z_5bf5c588c698c6cc_utils_py":{"hash":"523f50924333299a9638fc5393a1b12d","index":{"url":"z_5bf5c588c698c6cc_utils_py.html","file":"src/lyscripts/utils.py","description":"","nums":{"precision":0,"n_files":1,"n_statements":84,"n_excluded":0,"n_missing":5,"n_branches":0,"n_partial_branches":0,"n_missing_branches":0}}}}}
\ No newline at end of file
diff --git a/htmlcov/style_cb_81f8c14c.css b/htmlcov/style_cb_81f8c14c.css
new file mode 100644
index 0000000..e54e87a
--- /dev/null
+++ b/htmlcov/style_cb_81f8c14c.css
@@ -0,0 +1,337 @@
+@charset "UTF-8";
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+/* Don't edit this .css file. Edit the .scss file instead! */
+html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
+
+body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 1em; background: #fff; color: #000; }
+
+@media (prefers-color-scheme: dark) { body { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { body { color: #eee; } }
+
+html > body { font-size: 16px; }
+
+a:active, a:focus { outline: 2px dashed #007acc; }
+
+p { font-size: .875em; line-height: 1.4em; }
+
+table { border-collapse: collapse; }
+
+td { vertical-align: top; }
+
+table tr.hidden { display: none !important; }
+
+p#no_rows { display: none; font-size: 1.15em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
+
+a.nav { text-decoration: none; color: inherit; }
+
+a.nav:hover { text-decoration: underline; color: inherit; }
+
+.hidden { display: none; }
+
+header { background: #f8f8f8; width: 100%; z-index: 2; border-bottom: 1px solid #ccc; }
+
+@media (prefers-color-scheme: dark) { header { background: black; } }
+
+@media (prefers-color-scheme: dark) { header { border-color: #333; } }
+
+header .content { padding: 1rem 3.5rem; }
+
+header h2 { margin-top: .5em; font-size: 1em; }
+
+header h2 a.button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; background: #eee; color: inherit; text-decoration: none; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; }
+
+@media (prefers-color-scheme: dark) { header h2 a.button { background: #333; } }
+
+@media (prefers-color-scheme: dark) { header h2 a.button { border-color: #444; } }
+
+header h2 a.button.current { border: 2px solid; background: #fff; border-color: #999; cursor: default; }
+
+@media (prefers-color-scheme: dark) { header h2 a.button.current { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { header h2 a.button.current { border-color: #777; } }
+
+header p.text { margin: .5em 0 -.5em; color: #666; font-style: italic; }
+
+@media (prefers-color-scheme: dark) { header p.text { color: #aaa; } }
+
+header.sticky { position: fixed; left: 0; right: 0; height: 2.5em; }
+
+header.sticky .text { display: none; }
+
+header.sticky h1, header.sticky h2 { font-size: 1em; margin-top: 0; display: inline-block; }
+
+header.sticky .content { padding: 0.5rem 3.5rem; }
+
+header.sticky .content p { font-size: 1em; }
+
+header.sticky ~ #source { padding-top: 6.5em; }
+
+main { position: relative; z-index: 1; }
+
+footer { margin: 1rem 3.5rem; }
+
+footer .content { padding: 0; color: #666; font-style: italic; }
+
+@media (prefers-color-scheme: dark) { footer .content { color: #aaa; } }
+
+#index { margin: 1rem 0 0 3.5rem; }
+
+h1 { font-size: 1.25em; display: inline-block; }
+
+#filter_container { float: right; margin: 0 2em 0 0; line-height: 1.66em; }
+
+#filter_container #filter { width: 10em; padding: 0.2em 0.5em; border: 2px solid #ccc; background: #fff; color: #000; }
+
+@media (prefers-color-scheme: dark) { #filter_container #filter { border-color: #444; } }
+
+@media (prefers-color-scheme: dark) { #filter_container #filter { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { #filter_container #filter { color: #eee; } }
+
+#filter_container #filter:focus { border-color: #007acc; }
+
+#filter_container :disabled ~ label { color: #ccc; }
+
+@media (prefers-color-scheme: dark) { #filter_container :disabled ~ label { color: #444; } }
+
+#filter_container label { font-size: .875em; color: #666; }
+
+@media (prefers-color-scheme: dark) { #filter_container label { color: #aaa; } }
+
+header button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; background: #eee; color: inherit; text-decoration: none; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; }
+
+@media (prefers-color-scheme: dark) { header button { background: #333; } }
+
+@media (prefers-color-scheme: dark) { header button { border-color: #444; } }
+
+header button:active, header button:focus { outline: 2px dashed #007acc; }
+
+header button.run { background: #eeffee; }
+
+@media (prefers-color-scheme: dark) { header button.run { background: #373d29; } }
+
+header button.run.show_run { background: #dfd; border: 2px solid #00dd00; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { header button.run.show_run { background: #373d29; } }
+
+header button.mis { background: #ffeeee; }
+
+@media (prefers-color-scheme: dark) { header button.mis { background: #4b1818; } }
+
+header button.mis.show_mis { background: #fdd; border: 2px solid #ff0000; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { header button.mis.show_mis { background: #4b1818; } }
+
+header button.exc { background: #f7f7f7; }
+
+@media (prefers-color-scheme: dark) { header button.exc { background: #333; } }
+
+header button.exc.show_exc { background: #eee; border: 2px solid #808080; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { header button.exc.show_exc { background: #333; } }
+
+header button.par { background: #ffffd5; }
+
+@media (prefers-color-scheme: dark) { header button.par { background: #650; } }
+
+header button.par.show_par { background: #ffa; border: 2px solid #bbbb00; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { header button.par.show_par { background: #650; } }
+
+#help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; }
+
+#source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; }
+
+#help_panel_wrapper { float: right; position: relative; }
+
+#keyboard_icon { margin: 5px; }
+
+#help_panel_state { display: none; }
+
+#help_panel { top: 25px; right: 0; padding: .75em; border: 1px solid #883; color: #333; }
+
+#help_panel .keyhelp p { margin-top: .75em; }
+
+#help_panel .legend { font-style: italic; margin-bottom: 1em; }
+
+.indexfile #help_panel { width: 25em; }
+
+.pyfile #help_panel { width: 18em; }
+
+#help_panel_state:checked ~ #help_panel { display: block; }
+
+kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; border-radius: 3px; }
+
+#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
+
+#source p { position: relative; white-space: pre; }
+
+#source p * { box-sizing: border-box; }
+
+#source p .n { float: left; text-align: right; width: 3.5rem; box-sizing: border-box; margin-left: -3.5rem; padding-right: 1em; color: #999; user-select: none; }
+
+@media (prefers-color-scheme: dark) { #source p .n { color: #777; } }
+
+#source p .n.highlight { background: #ffdd00; }
+
+#source p .n a { scroll-margin-top: 6em; text-decoration: none; color: #999; }
+
+@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } }
+
+#source p .n a:hover { text-decoration: underline; color: #999; }
+
+@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } }
+
+#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; }
+
+@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } }
+
+#source p .t:hover { background: #f2f2f2; }
+
+@media (prefers-color-scheme: dark) { #source p .t:hover { background: #282828; } }
+
+#source p .t:hover ~ .r .annotate.long { display: block; }
+
+#source p .t .com { color: #008000; font-style: italic; line-height: 1px; }
+
+@media (prefers-color-scheme: dark) { #source p .t .com { color: #6a9955; } }
+
+#source p .t .key { font-weight: bold; line-height: 1px; }
+
+#source p .t .str, #source p .t .fst { color: #0451a5; }
+
+@media (prefers-color-scheme: dark) { #source p .t .str, #source p .t .fst { color: #9cdcfe; } }
+
+#source p.mis .t { border-left: 0.2em solid #ff0000; }
+
+#source p.mis.show_mis .t { background: #fdd; }
+
+@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t { background: #4b1818; } }
+
+#source p.mis.show_mis .t:hover { background: #f2d2d2; }
+
+@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t:hover { background: #532323; } }
+
+#source p.run .t { border-left: 0.2em solid #00dd00; }
+
+#source p.run.show_run .t { background: #dfd; }
+
+@media (prefers-color-scheme: dark) { #source p.run.show_run .t { background: #373d29; } }
+
+#source p.run.show_run .t:hover { background: #d2f2d2; }
+
+@media (prefers-color-scheme: dark) { #source p.run.show_run .t:hover { background: #404633; } }
+
+#source p.exc .t { border-left: 0.2em solid #808080; }
+
+#source p.exc.show_exc .t { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t { background: #333; } }
+
+#source p.exc.show_exc .t:hover { background: #e2e2e2; }
+
+@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t:hover { background: #3c3c3c; } }
+
+#source p.par .t { border-left: 0.2em solid #bbbb00; }
+
+#source p.par.show_par .t { background: #ffa; }
+
+@media (prefers-color-scheme: dark) { #source p.par.show_par .t { background: #650; } }
+
+#source p.par.show_par .t:hover { background: #f2f2a2; }
+
+@media (prefers-color-scheme: dark) { #source p.par.show_par .t:hover { background: #6d5d0c; } }
+
+#source p .r { position: absolute; top: 0; right: 2.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
+
+#source p .annotate { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; color: #666; padding-right: .5em; }
+
+@media (prefers-color-scheme: dark) { #source p .annotate { color: #ddd; } }
+
+#source p .annotate.short:hover ~ .long { display: block; }
+
+#source p .annotate.long { width: 30em; right: 2.5em; }
+
+#source p input { display: none; }
+
+#source p input ~ .r label.ctx { cursor: pointer; border-radius: .25em; }
+
+#source p input ~ .r label.ctx::before { content: "â–¶ "; }
+
+#source p input ~ .r label.ctx:hover { background: #e8f4ff; color: #666; }
+
+@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { background: #0f3a42; } }
+
+@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { color: #aaa; } }
+
+#source p input:checked ~ .r label.ctx { background: #d0e8ff; color: #666; border-radius: .75em .75em 0 0; padding: 0 .5em; margin: -.25em 0; }
+
+@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { background: #056; } }
+
+@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { color: #aaa; } }
+
+#source p input:checked ~ .r label.ctx::before { content: "â–¼ "; }
+
+#source p input:checked ~ .ctxs { padding: .25em .5em; overflow-y: scroll; max-height: 10.5em; }
+
+#source p label.ctx { color: #999; display: inline-block; padding: 0 .5em; font-size: .8333em; }
+
+@media (prefers-color-scheme: dark) { #source p label.ctx { color: #777; } }
+
+#source p .ctxs { display: block; max-height: 0; overflow-y: hidden; transition: all .2s; padding: 0 .5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; white-space: nowrap; background: #d0e8ff; border-radius: .25em; margin-right: 1.75em; text-align: right; }
+
+@media (prefers-color-scheme: dark) { #source p .ctxs { background: #056; } }
+
+#index { font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.875em; }
+
+#index table.index { margin-left: -.5em; }
+
+#index td, #index th { text-align: right; padding: .25em .5em; border-bottom: 1px solid #eee; }
+
+@media (prefers-color-scheme: dark) { #index td, #index th { border-color: #333; } }
+
+#index td.name, #index th.name { text-align: left; width: auto; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; min-width: 15em; }
+
+#index th { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-style: italic; color: #333; cursor: pointer; }
+
+@media (prefers-color-scheme: dark) { #index th { color: #ddd; } }
+
+#index th:hover { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #index th:hover { background: #333; } }
+
+#index th .arrows { color: #666; font-size: 85%; font-family: sans-serif; font-style: normal; pointer-events: none; }
+
+#index th[aria-sort="ascending"], #index th[aria-sort="descending"] { white-space: nowrap; background: #eee; padding-left: .5em; }
+
+@media (prefers-color-scheme: dark) { #index th[aria-sort="ascending"], #index th[aria-sort="descending"] { background: #333; } }
+
+#index th[aria-sort="ascending"] .arrows::after { content: " â–²"; }
+
+#index th[aria-sort="descending"] .arrows::after { content: " â–¼"; }
+
+#index td.name { font-size: 1.15em; }
+
+#index td.name a { text-decoration: none; color: inherit; }
+
+#index td.name .no-noun { font-style: italic; }
+
+#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; }
+
+#index tr.region:hover { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #index tr.region:hover { background: #333; } }
+
+#index tr.region:hover td.name { text-decoration: underline; color: inherit; }
+
+#scroll_marker { position: fixed; z-index: 3; right: 0; top: 0; width: 16px; height: 100%; background: #fff; border-left: 1px solid #eee; will-change: transform; }
+
+@media (prefers-color-scheme: dark) { #scroll_marker { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { #scroll_marker { border-color: #333; } }
+
+#scroll_marker .marker { background: #ccc; position: absolute; min-height: 3px; width: 100%; }
+
+@media (prefers-color-scheme: dark) { #scroll_marker .marker { background: #444; } }
diff --git a/htmlcov/z_055061514423972c___init___py.html b/htmlcov/z_055061514423972c___init___py.html
new file mode 100644
index 0000000..b0c99dc
--- /dev/null
+++ b/htmlcov/z_055061514423972c___init___py.html
@@ -0,0 +1,118 @@
+
+
+
+
+ Coverage for src/lyscripts/compute/__init__.py: 89%
+
+
+
+
+
+
+
+ 1"""Commands to compute prior and posterior state distributions from model samples.
+ 2
+ 3This can in turn speed up the computation of risks and prevalences.
+ 4"""
+ 5
+ 6from pydantic_settings import BaseSettings, CliApp, CliSubCommand
+ 7
+ 8from lyscripts.compute import posteriors, prevalences, priors, risks
+ 9
+ 10
+ 11class ComputeCLI(BaseSettings):
+ 12 """Compute priors, posteriors, risks, and prevalences from model samples."""
+ 13
+ 14 priors: CliSubCommand[priors.PriorsCLI]
+ 15 posteriors: CliSubCommand[posteriors.PosteriorsCLI]
+ 16 risks: CliSubCommand[risks.RisksCLI]
+ 17 prevalences: CliSubCommand[prevalences.PrevalencesCLI]
+ 18
+ 19 def cli_cmd(self) -> None:
+ 20 """Start the ``compute`` subcommand."""
+ 21 CliApp.run_subcommand(self)
+
+
+
+
diff --git a/htmlcov/z_055061514423972c___main___py.html b/htmlcov/z_055061514423972c___main___py.html
new file mode 100644
index 0000000..fa31306
--- /dev/null
+++ b/htmlcov/z_055061514423972c___main___py.html
@@ -0,0 +1,105 @@
+
+
+
+
+ Coverage for src/lyscripts/compute/__main__.py: 0%
+
+
+
+
+
+
+
+ 1"""Run the compute module as a script."""
+ 2
+ 3from lyscripts.cli import assemble_main
+ 4from lyscripts.compute import ComputeCLI
+ 5
+ 6if __name__ == "__main__":
+ 7 main = assemble_main(settings_cls=ComputeCLI, prog_name="compute")
+ 8 main()
+
+
+
+
diff --git a/htmlcov/z_055061514423972c_posteriors_py.html b/htmlcov/z_055061514423972c_posteriors_py.html
new file mode 100644
index 0000000..196b9b8
--- /dev/null
+++ b/htmlcov/z_055061514423972c_posteriors_py.html
@@ -0,0 +1,239 @@
+
+
+
+
+ Coverage for src/lyscripts/compute/posteriors.py: 59%
+
+
+
+
+
+
+
+ 1"""Compute posterior state distributions.
+ 2
+ 3The posteriors are computed from drawn samples for a list of defined scenarios. If
+ 4priors have already been computed from the samples and the ``--cache_dir`` argument
+ 5is the same as during that computation, the priors will automatically be loaded from
+ 6the cache.
+ 7"""
+ 8
+ 9from typing import Literal
+ 10
+ 11import numpy as np
+ 12from loguru import logger
+ 13from lymph import models
+ 14from pydantic import Field
+ 15from rich import progress
+ 16
+ 17from lyscripts.cli import assemble_main
+ 18from lyscripts.compute.priors import compute_priors
+ 19from lyscripts.compute.utils import BaseComputeCLI, HDF5FileStorage, get_cached
+ 20from lyscripts.configs import (
+ 21 DistributionConfig,
+ 22 GraphConfig,
+ 23 ModalityConfig,
+ 24 ModelConfig,
+ 25 add_distributions,
+ 26 add_modalities,
+ 27 construct_model,
+ 28)
+ 29from lyscripts.utils import console
+ 30
+ 31
+ 32def compute_posteriors(
+ 33 model_config: ModelConfig,
+ 34 graph_config: GraphConfig,
+ 35 dist_configs: dict[str, DistributionConfig],
+ 36 modality_configs: dict[str, ModalityConfig],
+ 37 priors: np.ndarray,
+ 38 diagnosis: dict[Literal["ipsi", "contra"], dict],
+ 39 midext: bool | None = None,
+ 40 mode: Literal["HMM", "BN"] = "HMM",
+ 41 progress_desc: str = "Computing posteriors from priors",
+ 42) -> np.ndarray:
+ 43 """Compute posterior state distributions from ``priors``.
+ 44
+ 45 This calls the ``model`` method :py:meth:`~lymph.types.Model.posterior_state_dist`
+ 46 for each of the pre-computed ``priors``, given the specified ``diagnosis`` pattern.
+ 47
+ 48 For the :py:class:`~lymph.models.Midline` model, the ``midext`` argument can be
+ 49 used to specify whether the midline extension is present or not.
+ 50 """
+ 51 model = construct_model(model_config, graph_config)
+ 52 model = add_distributions(model, dist_configs)
+ 53 model = add_modalities(model, modality_configs)
+ 54 posteriors = []
+ 55 kwargs = {"midext": midext} if isinstance(model, models.Midline) else {}
+ 56
+ 57 if isinstance(model, models.Unilateral | models.HPVUnilateral):
+ 58 diagnosis = diagnosis.get("ipsi")
+ 59
+ 60 for prior in progress.track(
+ 61 sequence=priors,
+ 62 description=progress_desc,
+ 63 total=len(priors),
+ 64 console=console,
+ 65 ):
+ 66 posteriors.append(
+ 67 model.posterior_state_dist(
+ 68 given_state_dist=prior,
+ 69 given_diagnosis=diagnosis,
+ 70 mode=mode,
+ 71 **kwargs,
+ 72 ),
+ 73 )
+ 74
+ 75 return np.stack(posteriors)
+ 76
+ 77
+ 78class PosteriorsCLI(BaseComputeCLI):
+ 79 """Compute posterior state distributions for different diagnosis scenarios."""
+ 80
+ 81 modalities: dict[str, ModalityConfig] = Field(
+ 82 default={},
+ 83 description=(
+ 84 "Maps names of diagnostic modalities to their specificity/sensitivity."
+ 85 ),
+ 86 )
+ 87 posteriors: HDF5FileStorage = Field(
+ 88 description="Storage for the computed posteriors.",
+ 89 )
+ 90
+ 91 def cli_cmd(self) -> None:
+ 92 """Start the ``posteriors`` subcommand.
+ 93
+ 94 This will compute the posterior state distributions, given a personalized
+ 95 diagnosis pattern, for each of the scenarios provided to the command.
+ 96 """
+ 97 logger.debug(self.model_dump_json(indent=2))
+ 98
+ 99 global_attrs = self.model_dump(
+ 100 include={"model", "graph", "distributions", "modalities"},
+ 101 )
+ 102 self.posteriors.set_attrs(attrs=global_attrs, dataset="/")
+ 103
+ 104 samples = self.sampling.load()
+ 105 cached_compute_priors = get_cached(compute_priors, self.cache_dir)
+ 106 cached_compute_posteriors = get_cached(compute_posteriors, self.cache_dir)
+ 107 num_scens = len(self.scenarios)
+ 108
+ 109 for i, scenario in enumerate(self.scenarios):
+ 110 _fields = {"t_stages", "t_stages_dist", "mode"}
+ 111 prior_kwargs = scenario.model_dump(include=_fields)
+ 112
+ 113 _priors = cached_compute_priors(
+ 114 model_config=self.model,
+ 115 graph_config=self.graph,
+ 116 dist_configs=self.distributions,
+ 117 samples=samples,
+ 118 progress_desc=f"Computing priors for scenario {i + 1}/{num_scens}",
+ 119 **prior_kwargs,
+ 120 )
+ 121
+ 122 _fields = {"diagnosis", "midext", "mode"}
+ 123 posterior_kwargs = scenario.model_dump(include=_fields)
+ 124
+ 125 posteriors = cached_compute_posteriors(
+ 126 model_config=self.model,
+ 127 graph_config=self.graph,
+ 128 dist_configs=self.distributions,
+ 129 modality_configs=self.modalities,
+ 130 priors=_priors,
+ 131 progress_desc=f"Computing posteriors for scenario {i + 1}/{num_scens}",
+ 132 **posterior_kwargs,
+ 133 )
+ 134
+ 135 self.posteriors.save(values=posteriors, dataset=f"{i:03d}")
+ 136 self.posteriors.set_attrs(attrs=prior_kwargs, dataset=f"{i:03d}")
+ 137 self.posteriors.set_attrs(attrs=posterior_kwargs, dataset=f"{i:03d}")
+ 138
+ 139
+ 140if __name__ == "__main__":
+ 141 main = assemble_main(settings_cls=PosteriorsCLI, prog_name="compute posteriors")
+ 142 main()
+
+
+
+
diff --git a/htmlcov/z_055061514423972c_prevalences_py.html b/htmlcov/z_055061514423972c_prevalences_py.html
new file mode 100644
index 0000000..51f0c9e
--- /dev/null
+++ b/htmlcov/z_055061514423972c_prevalences_py.html
@@ -0,0 +1,330 @@
+
+
+
+
+ Coverage for src/lyscripts/compute/prevalences.py: 91%
+
+
+
+
+
+
+
+ 1"""Prevalence prediction module.
+ 2
+ 3This computes the prevalence of an observed involvement pattern, given a trained model.
+ 4It can also compare this prediction to the observed prevalence in the data. As for the
+ 5risk prediction, this uses caching and computes the priors first.
+ 6"""
+ 7
+ 8from collections.abc import Callable
+ 9from typing import Literal
+ 10
+ 11import lydata # noqa: F401
+ 12import numpy as np
+ 13import pandas as pd
+ 14from loguru import logger
+ 15from lydata import C, Q
+ 16from lydata.accessor import NoneQ, QueryPortion
+ 17from lymph import models
+ 18from pydantic import Field
+ 19from rich import progress
+ 20
+ 21from lyscripts.cli import assemble_main
+ 22from lyscripts.compute.priors import compute_priors
+ 23from lyscripts.compute.utils import (
+ 24 BaseComputeCLI,
+ 25 HDF5FileStorage,
+ 26 get_cached,
+ 27)
+ 28from lyscripts.configs import (
+ 29 DataConfig,
+ 30 DiagnosisConfig,
+ 31 DistributionConfig,
+ 32 GraphConfig,
+ 33 ModalityConfig,
+ 34 ModelConfig,
+ 35 ScenarioConfig,
+ 36 add_distributions,
+ 37 add_modalities,
+ 38 construct_model,
+ 39)
+ 40from lyscripts.utils import console
+ 41
+ 42
+ 43def compute_prevalences(
+ 44 model_config: ModelConfig,
+ 45 graph_config: GraphConfig,
+ 46 dist_configs: dict[str, DistributionConfig],
+ 47 modality_configs: dict[str, ModalityConfig],
+ 48 priors: np.ndarray,
+ 49 diagnosis: dict[Literal["ipsi", "contra"], dict],
+ 50 midext: bool | None = None,
+ 51 progress_desc: str = "Computing prevalences from priors",
+ 52) -> np.ndarray:
+ 53 """Compute the prevalence of a diagnosis given the priors and the model."""
+ 54 model = construct_model(model_config, graph_config)
+ 55 model = add_distributions(model, dist_configs)
+ 56
+ 57 if len(modality_configs) != 1:
+ 58 msg = "Only one modality is supported for prevalence prediction."
+ 59 logger.error(msg)
+ 60 raise ValueError(msg)
+ 61
+ 62 model = add_modalities(model, modality_configs)
+ 63 prevalences = []
+ 64 kwargs = {"midext": midext} if isinstance(model, models.Midline) else {}
+ 65
+ 66 for prior in progress.track(
+ 67 sequence=priors,
+ 68 description=progress_desc,
+ 69 total=len(priors),
+ 70 console=console,
+ 71 ):
+ 72 obs_dist = model.obs_dist(given_state_dist=prior)
+ 73 involvement = {
+ 74 side: diagnosis.get(side).get(next(iter(modality_configs)))
+ 75 for side in ["ipsi", "contra"]
+ 76 }
+ 77
+ 78 if isinstance(model, models.Unilateral | models.HPVUnilateral):
+ 79 involvement = involvement.get("ipsi")
+ 80
+ 81 prevalence = model.marginalize(
+ 82 given_state_dist=obs_dist,
+ 83 involvement=involvement,
+ 84 **kwargs,
+ 85 )
+ 86
+ 87 if isinstance(model, models.Midline):
+ 88 # In this case, we need to renormalize the prevalence by the marginalized
+ 89 # probability of all states with midline extension. We must do this, because
+ 90 # we compute the analogous quantity for the data. In principle, we could
+ 91 # also compute the prevalence of the diagnosis *and* midline extension, but
+ 92 # we have decided to compute the diagnosis *given* midline extension.
+ 93 # https://github.com/lycosystem/lyscripts/blob/ea49ec/lyscripts/compute/prevalences.py#L217-L225
+ 94 midext_prob = model.marginalize(
+ 95 involvement=None,
+ 96 given_state_dist=obs_dist,
+ 97 **kwargs,
+ 98 )
+ 99 prevalence /= midext_prob
+ 100
+ 101 prevalences.append(prevalence)
+ 102
+ 103 return np.stack(prevalences)
+ 104
+ 105
+ 106def generate_query_from_diagnosis(diagnosis: DiagnosisConfig) -> Q:
+ 107 """Transform a diagnosis into a query for the data."""
+ 108 result = NoneQ()
+ 109 for side in ["ipsi", "contra"]:
+ 110 for modality, pattern in getattr(diagnosis, side, {}).items():
+ 111 for lnl, value in pattern.items():
+ 112 column = (modality, side, lnl)
+ 113 result &= C(column) == value
+ 114 return result
+ 115
+ 116
+ 117def observe_prevalence(
+ 118 data: pd.DataFrame,
+ 119 scenario_config: ScenarioConfig,
+ 120 mapping: dict[int, str] | Callable[[int], str] | None = None,
+ 121) -> QueryPortion:
+ 122 """Extract prevalence defined in a ``scenario`` from the ``data``.
+ 123
+ 124 ``mapping`` defines how the T-stages in the data are supposed to be mapped to the
+ 125 T-stages defined in the ``scenario``.
+ 126
+ 127 It returns the number of patients that match the given scenario and the total
+ 128 number of patients that are considered. E.g., in the example below we 79 patients
+ 129 are of late T-stage and have a tumor extending over the midline. Of those, 30 were
+ 130 diagnosed with contralateral involvement in LNL II based on a CT scan.
+ 131
+ 132 >>> data = next(lydata.load_datasets(year=2021, institution="usz"))
+ 133 >>> scenario_config = ScenarioConfig(
+ 134 ... t_stages=["late"],
+ 135 ... midext=True,
+ 136 ... diagnosis=DiagnosisConfig(contra={"CT": {"II": True}}),
+ 137 ... )
+ 138 >>> observe_prevalence(data, scenario_config)
+ 139 QueryPortion(match=np.int64(7), total=np.int64(79))
+ 140 """
+ 141 mapping = mapping or DataConfig.model_fields["mapping"].default_factory()
+ 142 data["tumor", "1", "t_stage"] = data.ly.t_stage.map(mapping)
+ 143
+ 144 has_t_stage = C("t_stage").isin(scenario_config.t_stages)
+ 145 if scenario_config.midext is None:
+ 146 has_midext = NoneQ()
+ 147 else:
+ 148 has_midext = C("midext") == scenario_config.midext
+ 149
+ 150 # Note that below we compute the prevalence of the diagnosis *given* midline
+ 151 # extension. This means, that when computing the prevalence of the diagnosis in
+ 152 # the model, we need to renormalize by diving by the probability of midline
+ 153 # extension. For an older - but pretty surely correct - implementation see
+ 154 # https://github.com/lycosystem/lyscripts/blob/ea49ec/lyscripts/compute/prevalences.py#L217-L225
+ 155 return data.ly.portion(
+ 156 query=generate_query_from_diagnosis(scenario_config.diagnosis),
+ 157 given=has_t_stage & has_midext,
+ 158 )
+ 159
+ 160
+ 161class PrevalencesCLI(BaseComputeCLI):
+ 162 """Predict the prevalence of an involvement pattern from model samples."""
+ 163
+ 164 modalities: dict[str, ModalityConfig] = Field(
+ 165 default={},
+ 166 description=(
+ 167 "Maps names of diagnostic modalities to their specificity/sensitivity."
+ 168 ),
+ 169 )
+ 170 prevalences: HDF5FileStorage = Field(
+ 171 description="Storage for the computed prevalences.",
+ 172 )
+ 173 data: DataConfig
+ 174
+ 175 def cli_cmd(self) -> None:
+ 176 """Start the ``prevalences`` subcommand."""
+ 177 logger.debug(self.model_dump_json(indent=2))
+ 178 global_attrs = self.model_dump(
+ 179 include={"model", "graph", "distributions", "modalities"},
+ 180 )
+ 181 self.prevalences.set_attrs(attrs=global_attrs, dataset="/")
+ 182
+ 183 samples = self.sampling.load()
+ 184 cached_compute_priors = get_cached(compute_priors, self.cache_dir)
+ 185 cached_compute_prevalences = get_cached(compute_prevalences, self.cache_dir)
+ 186 num_scens = len(self.scenarios)
+ 187
+ 188 for i, scenario in enumerate(self.scenarios):
+ 189 _fields = {"t_stages", "t_stages_dist", "mode"}
+ 190 prior_kwargs = scenario.model_dump(include=_fields)
+ 191
+ 192 _priors = cached_compute_priors(
+ 193 model_config=self.model,
+ 194 graph_config=self.graph,
+ 195 dist_configs=self.distributions,
+ 196 samples=samples,
+ 197 progress_desc=f"Computing priors for scenario {i + 1}/{num_scens}",
+ 198 **prior_kwargs,
+ 199 )
+ 200
+ 201 _fields = {"diagnosis", "midext"}
+ 202 prevalence_kwargs = scenario.model_dump(include=_fields)
+ 203
+ 204 prevalences = cached_compute_prevalences(
+ 205 model_config=self.model,
+ 206 graph_config=self.graph,
+ 207 dist_configs=self.distributions,
+ 208 modality_configs=self.modalities,
+ 209 priors=_priors,
+ 210 progress_desc=f"Computing prevalences for scenario {i + 1}/{num_scens}",
+ 211 **prevalence_kwargs,
+ 212 )
+ 213
+ 214 portion = observe_prevalence(
+ 215 data=self.data.load(),
+ 216 scenario_config=scenario,
+ 217 mapping=self.data.mapping,
+ 218 )
+ 219 self.prevalences.save(values=prevalences, dataset=f"{i:03d}")
+ 220 self.prevalences.set_attrs(attrs=prior_kwargs, dataset=f"{i:03d}")
+ 221 self.prevalences.set_attrs(attrs=prevalence_kwargs, dataset=f"{i:03d}")
+ 222 self.prevalences.set_attrs(
+ 223 attrs={
+ 224 "num_match": portion.match,
+ 225 "num_total": portion.total,
+ 226 },
+ 227 dataset=f"{i:03d}",
+ 228 )
+ 229
+ 230
+ 231if __name__ == "__main__":
+ 232 main = assemble_main(settings_cls=PrevalencesCLI, prog_name="compute prevalences")
+ 233 main()
+
+
+
+
diff --git a/htmlcov/z_055061514423972c_priors_py.html b/htmlcov/z_055061514423972c_priors_py.html
new file mode 100644
index 0000000..0ed42d7
--- /dev/null
+++ b/htmlcov/z_055061514423972c_priors_py.html
@@ -0,0 +1,208 @@
+
+
+
+
+ Coverage for src/lyscripts/compute/priors.py: 94%
+
+
+
+
+
+
+
+ 1"""Given samples drawn during an MCMC round, compute the (prior) state distributions.
+ 2
+ 3This is done for each sample and for a list of specified scenarios. The computation is
+ 4cached at a location specified by the ``--cache_dir`` argument using ``joblib``.
+ 5"""
+ 6
+ 7from typing import Literal
+ 8
+ 9import numpy as np
+ 10from loguru import logger
+ 11from pydantic import Field
+ 12from rich import progress
+ 13
+ 14from lyscripts.cli import assemble_main
+ 15from lyscripts.compute.utils import BaseComputeCLI, HDF5FileStorage, get_cached
+ 16from lyscripts.configs import (
+ 17 DistributionConfig,
+ 18 GraphConfig,
+ 19 ModelConfig,
+ 20 add_distributions,
+ 21 construct_model,
+ 22)
+ 23from lyscripts.utils import console
+ 24
+ 25
+ 26def compute_priors(
+ 27 model_config: ModelConfig,
+ 28 graph_config: GraphConfig,
+ 29 dist_configs: dict[str, DistributionConfig],
+ 30 samples: np.ndarray,
+ 31 t_stages: list[int | str],
+ 32 t_stages_dist: list[float],
+ 33 mode: Literal["HMM", "BN"] = "HMM",
+ 34 progress_desc: str = "Computing priors from samples",
+ 35) -> np.ndarray:
+ 36 """Compute prior state distributions from the ``samples`` for the ``model``.
+ 37
+ 38 This will call the ``model`` method :py:meth:`~lymph.types.Model.state_dist`
+ 39 for each of the ``samples``. The prior state distributions are computed for
+ 40 each of the ``t_stages`` and marginalized over using the ``t_stages_dist``.
+ 41 """
+ 42 model = construct_model(model_config, graph_config)
+ 43 model = add_distributions(model, dist_configs)
+ 44 priors = []
+ 45
+ 46 for sample in progress.track(
+ 47 sequence=samples,
+ 48 description=progress_desc,
+ 49 total=len(samples),
+ 50 console=console,
+ 51 ):
+ 52 model.set_params(*sample)
+ 53 priors.append(
+ 54 sum(
+ 55 model.state_dist(t_stage=t, mode=mode) * p
+ 56 for t, p in zip(t_stages, t_stages_dist, strict=False)
+ 57 ),
+ 58 )
+ 59
+ 60 return np.stack(priors)
+ 61
+ 62
+ 63class PriorsCLI(BaseComputeCLI):
+ 64 """Compute the prior state distributions from MCMC samples."""
+ 65
+ 66 priors: HDF5FileStorage = Field(description="Storage for the computed priors.")
+ 67
+ 68 def cli_cmd(self) -> None:
+ 69 """Start the ``priors`` subcommand.
+ 70
+ 71 Given a ``graph``, ``model``, ``distributions`` over diagnosis times, and
+ 72 MCMC samples loaded from the ``sampling`` argument, this command computes the
+ 73 prior state distributions for each of the specified ``scenarios``.
+ 74
+ 75 Precomputing these state distributions is useful, because they largely only
+ 76 depend on T-stage and not on the diagnosis or involvement of interest. Hence,
+ 77 computing the :py:mod:`~lyscripts.compute.posteriors` and
+ 78 :py:mod:`~lyscripts.compute.risks` can be sped up.
+ 79
+ 80 Note that this command will use `joblib`_ to cache its computations.
+ 81
+ 82 .. _joblib: https://joblib.readthedocs.io/
+ 83 """
+ 84 logger.debug(self.model_dump_json(indent=2))
+ 85 global_attrs = self.model_dump(include={"model", "graph", "distributions"})
+ 86 self.priors.set_attrs(attrs=global_attrs, dataset="/")
+ 87
+ 88 samples = self.sampling.load()
+ 89 cached_compute_priors = get_cached(compute_priors, self.cache_dir)
+ 90 num_scenarios = len(self.scenarios)
+ 91
+ 92 for i, scenario in enumerate(self.scenarios):
+ 93 _fields = {"t_stages", "t_stages_dist", "mode"}
+ 94 prior_kwargs = scenario.model_dump(include=_fields)
+ 95
+ 96 priors = cached_compute_priors(
+ 97 model_config=self.model,
+ 98 graph_config=self.graph,
+ 99 dist_configs=self.distributions,
+ 100 samples=samples,
+ 101 progress_desc=f"Computing priors for scenario {i + 1}/{num_scenarios}",
+ 102 **prior_kwargs,
+ 103 )
+ 104
+ 105 self.priors.save(values=priors, dataset=f"{i:03d}")
+ 106 self.priors.set_attrs(attrs=prior_kwargs, dataset=f"{i:03d}")
+ 107
+ 108
+ 109if __name__ == "__main__":
+ 110 main = assemble_main(settings_cls=PriorsCLI, prog_name="compute priors")
+ 111 main()
+
+
+
+
diff --git a/htmlcov/z_055061514423972c_risks_py.html b/htmlcov/z_055061514423972c_risks_py.html
new file mode 100644
index 0000000..48b0011
--- /dev/null
+++ b/htmlcov/z_055061514423972c_risks_py.html
@@ -0,0 +1,237 @@
+
+
+
+
+ Coverage for src/lyscripts/compute/risks.py: 35%
+
+
+
+
+
+
+
+ 1"""Predict risks of involvements for scenarios using drawn MCMC samples.
+ 2
+ 3As the priors and posteriors, this computation, too, uses caching and may skip the
+ 4computation of these two initial steps if the cache directory is the same as during
+ 5their computation.
+ 6"""
+ 7
+ 8from typing import Literal
+ 9
+ 10import numpy as np
+ 11from loguru import logger
+ 12from lymph import models
+ 13from pydantic import Field
+ 14from rich import progress
+ 15
+ 16from lyscripts.cli import assemble_main
+ 17from lyscripts.compute.posteriors import compute_posteriors
+ 18from lyscripts.compute.priors import compute_priors
+ 19from lyscripts.compute.utils import BaseComputeCLI, HDF5FileStorage, get_cached
+ 20from lyscripts.configs import (
+ 21 DistributionConfig,
+ 22 GraphConfig,
+ 23 ModalityConfig,
+ 24 ModelConfig,
+ 25 add_distributions,
+ 26 add_modalities,
+ 27 construct_model,
+ 28)
+ 29from lyscripts.utils import console
+ 30
+ 31
+ 32def compute_risks(
+ 33 model_config: ModelConfig,
+ 34 graph_config: GraphConfig,
+ 35 dist_configs: dict[str, DistributionConfig],
+ 36 modality_configs: dict[str, ModalityConfig],
+ 37 posteriors: np.ndarray,
+ 38 involvement: dict[Literal["ipsi", "contra"], dict],
+ 39 progress_desc: str = "Computing risks from posteriors",
+ 40) -> np.ndarray:
+ 41 """Compute the risk of ``involvement`` from each of the ``posteriors``.
+ 42
+ 43 Essentially, this only calls the model's :py:meth:`lymph.models.Model.marginalize`
+ 44 method, as nothing more is necessary than to marginalize the full posterior state
+ 45 distribution over the states that correspond to the involvement of interest.
+ 46 """
+ 47 model = construct_model(model_config, graph_config)
+ 48 model = add_distributions(model, dist_configs)
+ 49 model = add_modalities(model, modality_configs)
+ 50 risks = []
+ 51
+ 52 if isinstance(model, models.Unilateral | models.HPVUnilateral):
+ 53 involvement = involvement.get("ipsi")
+ 54
+ 55 for posterior in progress.track(
+ 56 sequence=posteriors,
+ 57 description=progress_desc,
+ 58 total=len(posteriors),
+ 59 console=console,
+ 60 ):
+ 61 risks.append(
+ 62 model.marginalize(involvement=involvement, given_state_dist=posterior),
+ 63 )
+ 64
+ 65 return np.stack(risks)
+ 66
+ 67
+ 68class RisksCLI(BaseComputeCLI):
+ 69 """Predict the risk of involvement scenarios from model samples given diagnoses."""
+ 70
+ 71 modalities: dict[str, ModalityConfig] = Field(
+ 72 default={},
+ 73 description=(
+ 74 "Maps names of diagnostic modalities to their specificity/sensitivity."
+ 75 ),
+ 76 )
+ 77 risks: HDF5FileStorage = Field(description="Storage for the computed risks.")
+ 78
+ 79 def cli_cmd(self) -> None:
+ 80 """Start the ``risks`` subcommand."""
+ 81 logger.debug(self.model_dump_json(indent=2))
+ 82 global_attrs = self.model_dump(
+ 83 include={"model", "graph", "distributions", "modalities"},
+ 84 )
+ 85 self.risks.set_attrs(attrs=global_attrs, dataset="/")
+ 86
+ 87 samples = self.sampling.load()
+ 88 cached_compute_priors = get_cached(compute_priors, self.cache_dir)
+ 89 cached_compute_posteriors = get_cached(compute_posteriors, self.cache_dir)
+ 90 cached_compute_risks = get_cached(compute_risks, self.cache_dir)
+ 91 num_scens = len(self.scenarios)
+ 92
+ 93 for i, scenario in enumerate(self.scenarios):
+ 94 _fields = {"t_stages", "t_stages_dist", "mode"}
+ 95 prior_kwargs = scenario.model_dump(include=_fields)
+ 96
+ 97 _priors = cached_compute_priors(
+ 98 model_config=self.model,
+ 99 graph_config=self.graph,
+ 100 dist_configs=self.distributions,
+ 101 samples=samples,
+ 102 progress_desc=f"Computing priors for scenario {i + 1}/{num_scens}",
+ 103 **prior_kwargs,
+ 104 )
+ 105
+ 106 _fields = {"diagnosis", "midext", "mode"}
+ 107 posterior_kwargs = scenario.model_dump(include=_fields)
+ 108
+ 109 _posteriors = cached_compute_posteriors(
+ 110 model_config=self.model,
+ 111 graph_config=self.graph,
+ 112 dist_configs=self.distributions,
+ 113 modality_configs=self.modalities,
+ 114 priors=_priors,
+ 115 progress_desc=f"Computing posteriors for scenario {i + 1}/{num_scens}",
+ 116 **posterior_kwargs,
+ 117 )
+ 118
+ 119 _fields = {"involvement"}
+ 120 risk_kwargs = scenario.model_dump(include=_fields)
+ 121
+ 122 risks = cached_compute_risks(
+ 123 model_config=self.model,
+ 124 graph_config=self.graph,
+ 125 dist_configs=self.distributions,
+ 126 modality_configs=self.modalities,
+ 127 posteriors=_posteriors,
+ 128 progress_desc=f"Computing risks for scenario {i + 1}/{num_scens}",
+ 129 **risk_kwargs,
+ 130 )
+ 131
+ 132 self.risks.save(values=risks, dataset=f"{i:03d}")
+ 133 self.risks.set_attrs(attrs=prior_kwargs, dataset=f"{i:03d}")
+ 134 self.risks.set_attrs(attrs=posterior_kwargs, dataset=f"{i:03d}")
+ 135 self.risks.set_attrs(attrs=risk_kwargs, dataset=f"{i:03d}")
+ 136
+ 137
+ 138if __name__ == "__main__":
+ 139 main = assemble_main(settings_cls=RisksCLI, prog_name="compute risks")
+ 140 main()
+
+
+
+
diff --git a/htmlcov/z_055061514423972c_utils_py.html b/htmlcov/z_055061514423972c_utils_py.html
new file mode 100644
index 0000000..32e8efb
--- /dev/null
+++ b/htmlcov/z_055061514423972c_utils_py.html
@@ -0,0 +1,373 @@
+
+
+
+
+ Coverage for src/lyscripts/compute/utils.py: 95%
+
+
+
+
+
+
+
+ 1"""Utilities for precomputing the priors and posteriors."""
+ 2
+ 3import ast
+ 4import functools
+ 5from pathlib import Path
+ 6from typing import Annotated, Any
+ 7
+ 8import h5py
+ 9import numpy as np
+ 10from joblib import Memory
+ 11from loguru import logger
+ 12from pydantic import AfterValidator, BaseModel, Field
+ 13
+ 14from lyscripts.configs import (
+ 15 BaseCLI,
+ 16 DistributionConfig,
+ 17 GraphConfig,
+ 18 ModelConfig,
+ 19 SamplingConfig,
+ 20 ScenarioConfig,
+ 21)
+ 22
+ 23
+ 24class BaseComputeCLI(BaseCLI):
+ 25 """Common command line settings for the submodule ``compute``."""
+ 26
+ 27 graph: GraphConfig
+ 28 model: ModelConfig = ModelConfig()
+ 29 distributions: dict[str, DistributionConfig] = Field(
+ 30 default={},
+ 31 description=(
+ 32 "Mapping of model T-categories to predefined distributions over "
+ 33 "diagnose times."
+ 34 ),
+ 35 )
+ 36 cache_dir: Path = Field(
+ 37 default=Path.cwd() / ".cache",
+ 38 description="Cache directory for storing function calls.",
+ 39 )
+ 40 scenarios: list[ScenarioConfig] = Field(
+ 41 default=[],
+ 42 description="List of scenarios to compute risks for.",
+ 43 )
+ 44 sampling: SamplingConfig
+ 45
+ 46
+ 47def is_hdf5_compatible(value: Any) -> bool:
+ 48 """Check if the given ``value`` can be stored in an HDF5 file."""
+ 49 return isinstance(
+ 50 value,
+ 51 bool | str | bytes | int | float | np.ndarray | list | tuple,
+ 52 )
+ 53
+ 54
+ 55def to_hdf5_attrs(mapping: dict[str, Any]) -> dict[str, str]:
+ 56 """Convert ``attrs`` to a dictionary of HDF5 compatible attributes or strings."""
+ 57 res = {}
+ 58 for key, val in mapping.items():
+ 59 if is_hdf5_compatible(val):
+ 60 res[key] = val
+ 61 else:
+ 62 res[key] = str(val)
+ 63 return res
+ 64
+ 65
+ 66def from_hdf5_attrs(mapping: h5py.AttributeManager) -> dict[str, Any]:
+ 67 """Convert the HDF5 attributes to a dictionary of Python objects."""
+ 68 attrs = {}
+ 69 for key, value in mapping.items():
+ 70 try:
+ 71 attrs[key] = ast.literal_eval(value)
+ 72 except ValueError:
+ 73 attrs[key] = value
+ 74 return attrs
+ 75
+ 76
+ 77def extract_modalities(diagnosis: dict[str, Any]) -> set[str]:
+ 78 """Get the set of modalities used in the ``diagnosis``.
+ 79
+ 80 This is not used in the main apps anymore, but since it may be useful, I keep it.
+ 81
+ 82 >>> diagnosis = {
+ 83 ... "ipsi": {
+ 84 ... "MRI": {"II": True, "III": False},
+ 85 ... "PET": {"II": False, "III": True},
+ 86 ... },
+ 87 ... "contra": {"MRI": {"II": False, "III": None}},
+ 88 ... }
+ 89 >>> sorted(extract_modalities(diagnosis))
+ 90 ['MRI', 'PET']
+ 91 """
+ 92 modality_set = set()
+ 93
+ 94 if "ipsi" not in diagnosis and "contra" not in diagnosis:
+ 95 return modality_set | set(diagnosis.keys())
+ 96
+ 97 for side in ["ipsi", "contra"]:
+ 98 if side in diagnosis:
+ 99 modality_set |= set(diagnosis[side].keys())
+ 100
+ 101 return modality_set
+ 102
+ 103
+ 104def ensure_parent_dir(path: Path) -> Path:
+ 105 """Create the parent directory of the given ``path``."""
+ 106 path.parent.mkdir(parents=True, exist_ok=True)
+ 107 logger.debug(f"Ensured parent directory of {path}")
+ 108 return path
+ 109
+ 110
+ 111HasParentPath = Annotated[Path, AfterValidator(ensure_parent_dir)]
+ 112"""Type hint for path whose parent dir is created if it doesn't exist."""
+ 113
+ 114
+ 115class HDF5FileStorage(BaseModel):
+ 116 """HDF5 file storage for in- and outputs of computations."""
+ 117
+ 118 file: HasParentPath = Field(
+ 119 description="Path to the HDF5 file. Parent directories are created if needed.",
+ 120 )
+ 121 dataset: str | None = Field(
+ 122 default=None,
+ 123 description=(
+ 124 "Name of the dataset in the HDF5 file. Save/load methods can override this."
+ 125 ),
+ 126 )
+ 127
+ 128 def _get_dataset(self) -> str:
+ 129 """Get attribute ``dataset`` or the first dataset in the file.
+ 130
+ 131 >>> from tempfile import TemporaryDirectory
+ 132 >>> tmp_path = Path(TemporaryDirectory().name) / "test.hdf5"
+ 133 >>> storage = HDF5FileStorage(file=tmp_path)
+ 134 >>> rand_data = np.random.rand(100, 100)
+ 135 >>> storage.save(values=rand_data, dataset="test")
+ 136 >>> np.all(storage.load(dataset="test") == rand_data)
+ 137 np.True_
+ 138 >>> np.all(storage.load() == rand_data) # loads first dataset
+ 139 np.True_
+ 140 >>> some_attrs = {"key": "value"}
+ 141 >>> storage.set_attrs(attrs=some_attrs, dataset="test")
+ 142 >>> storage.get_attrs(dataset="test")
+ 143 {'key': 'value'}
+ 144 """
+ 145 if self.dataset is not None:
+ 146 return self.dataset
+ 147
+ 148 with h5py.File(self.file, "r") as file:
+ 149 return next(iter(file.keys()))
+ 150
+ 151 def load(self, dataset: str | None = None) -> np.ndarray:
+ 152 """Load the dataset with the name ``dataset``."""
+ 153 dataset = dataset or self._get_dataset()
+ 154
+ 155 with h5py.File(self.file, "r") as file:
+ 156 array = file[dataset][()]
+ 157
+ 158 logger.debug(f"Loaded dataset {dataset} from {self.file}")
+ 159 return array
+ 160
+ 161 def get_attrs(self, dataset: str | None = None) -> dict[str, Any]:
+ 162 """Get the attributes of the dataset ``dataset``."""
+ 163 dataset = dataset or self._get_dataset()
+ 164
+ 165 with h5py.File(self.file, "r") as file:
+ 166 attrs = from_hdf5_attrs(file[dataset].attrs)
+ 167
+ 168 logger.debug(f"Loaded attrs for dataset '{dataset}' from {self.file}")
+ 169 return attrs
+ 170
+ 171 def save(self, values: np.ndarray, dataset: str | None = None) -> None:
+ 172 """Set the ``values`` for the ``dataset`` dataset."""
+ 173 dataset = dataset or self._get_dataset()
+ 174
+ 175 with h5py.File(self.file, "a") as file:
+ 176 if dataset in file:
+ 177 del file[dataset]
+ 178 file[dataset] = values
+ 179
+ 180 logger.debug(f"Stored dataset {dataset} in {self.file}")
+ 181
+ 182 def set_attrs(self, attrs: dict[str, Any], dataset: str | None = None) -> None:
+ 183 """Update the ``attrs`` for the ``dataset`` dataset."""
+ 184 dataset = dataset or self._get_dataset()
+ 185
+ 186 with h5py.File(self.file, "a") as file:
+ 187 if dataset not in file:
+ 188 raise ValueError(f"Dataset '{dataset}' not found in {self.file}")
+ 189 file[dataset].attrs.update(to_hdf5_attrs(attrs))
+ 190
+ 191 logger.debug(f"Stored attrs {attrs} for dataset '{dataset}' in {self.file}")
+ 192
+ 193
+ 194def reduce_pattern(pattern: dict[str, dict[str, bool]]) -> dict[str, dict[str, bool]]:
+ 195 """Reduce a ``pattern`` by removing all entries that are ``None``.
+ 196
+ 197 This way, it should be completely recoverable by the ``complete_pattern`` function
+ 198 but be shorter to store.
+ 199
+ 200 Unused but maybe useful for some cases. Keeping it in here for now.
+ 201
+ 202 >>> full = {
+ 203 ... "ipsi": {"I": None, "II": True, "III": None},
+ 204 ... "contra": {"I": None, "II": None, "III": None},
+ 205 ... }
+ 206 >>> reduce_pattern(full)
+ 207 {'ipsi': {'II': True}}
+ 208
+ 209 """
+ 210 tmp_pattern = pattern.copy()
+ 211 reduced_pattern = {}
+ 212 for side in ["ipsi", "contra"]:
+ 213 if not all(v is None for v in tmp_pattern[side].values()):
+ 214 reduced_pattern[side] = {}
+ 215 for lnl, val in tmp_pattern[side].items():
+ 216 if val is not None:
+ 217 reduced_pattern[side][lnl] = val
+ 218
+ 219 return reduced_pattern
+ 220
+ 221
+ 222def complete_pattern(
+ 223 pattern: dict[str, dict[str, bool]] | None,
+ 224 lnls: list[str],
+ 225) -> dict[str, dict[str, bool]]:
+ 226 """Make sure the provided involvement ``pattern`` is correct.
+ 227
+ 228 For each side of the neck, and for each of the ``lnls`` this should in the end
+ 229 contain ``True``, ``False`` or ``None``.
+ 230
+ 231 Unused but maybe useful for some cases. Keeping it in here for now.
+ 232
+ 233 >>> pattern = {"ipsi": {"II": True}}
+ 234 >>> lnls = ["II", "III"]
+ 235 >>> complete_pattern(pattern, lnls)
+ 236 {'ipsi': {'II': True, 'III': None}, 'contra': {'II': None, 'III': None}}
+ 237
+ 238 """
+ 239 if pattern is None:
+ 240 pattern = {}
+ 241
+ 242 for side in ["ipsi", "contra"]:
+ 243 if side not in pattern:
+ 244 pattern[side] = {}
+ 245
+ 246 for lnl in lnls:
+ 247 if lnl not in pattern[side]:
+ 248 pattern[side][lnl] = None
+ 249 elif pattern[side][lnl] is None:
+ 250 continue
+ 251 else:
+ 252 pattern[side][lnl] = bool(pattern[side][lnl])
+ 253
+ 254 return pattern
+ 255
+ 256
+ 257def get_cached(func: callable, cache_dir: Path) -> callable:
+ 258 """Return cached ``func`` with a cache at ``cache_dir``."""
+ 259 memory = Memory(location=cache_dir, verbose=0)
+ 260 cached_func = memory.cache(func, ignore=["progress_desc"])
+ 261 logger.info(f"Initialized cache for {func.__name__} at {cache_dir}")
+ 262
+ 263 @functools.wraps(func)
+ 264 def log_cache_info_wrapper(*args, **kwargs):
+ 265 logger.debug(f"Calling {func.__name__}({args}, {kwargs})")
+ 266 if cached_func.check_call_in_cache(*args, **kwargs):
+ 267 logger.info(f"Cache hit for {func.__name__}, returning stored result")
+ 268 else:
+ 269 logger.info(f"Cache miss for {func.__name__}, computing result")
+ 270
+ 271 result = cached_func(*args, **kwargs)
+ 272 logger.debug(f"Computed {result = }")
+ 273 return result
+ 274
+ 275 log_cache_info_wrapper._cached_func = cached_func
+ 276 return log_cache_info_wrapper
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc___init___py.html b/htmlcov/z_5bf5c588c698c6cc___init___py.html
new file mode 100644
index 0000000..fe6eb02
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc___init___py.html
@@ -0,0 +1,172 @@
+
+
+
+
+ Coverage for src/lyscripts/__init__.py: 79%
+
+
+
+
+
+
+
+ 1"""Initial entry point for the lyscripts package and CLIs.
+ 2
+ 3This top-level module configures and provides the top-level CLI through which all
+ 4subcommands can be accessed.
+ 5"""
+ 6
+ 7import sys
+ 8from typing import Literal
+ 9
+ 10import pandas as pd
+ 11from loguru import logger
+ 12from pydantic import Field
+ 13from pydantic_settings import (
+ 14 BaseSettings,
+ 15 CliApp,
+ 16 CliImplicitFlag,
+ 17 CliSubCommand,
+ 18)
+ 19
+ 20from lyscripts import compute, data, sample, schedule # noqa: F401
+ 21from lyscripts._version import version
+ 22from lyscripts.cli import assemble_main, configure_logging
+ 23from lyscripts.utils import console
+ 24
+ 25__version__ = version
+ 26__description__ = "Package to interact with lymphatic progression data and models."
+ 27__author__ = "Roman Ludwig"
+ 28__email__ = "roman.ludwig@usz.ch"
+ 29__uri__ = "https://github.com/lycosystem/lyscripts"
+ 30
+ 31# activate copy on write in pandas.
+ 32# See https://pandas.pydata.org/docs/user_guide/copy_on_write.html
+ 33pd.options.mode.copy_on_write = True
+ 34
+ 35logger.disable("lyscripts")
+ 36
+ 37
+ 38class LyscriptsCLI(BaseSettings):
+ 39 """A CLI to interact with lymphatic progression data and models."""
+ 40
+ 41 version: CliImplicitFlag[bool] = Field(
+ 42 default=False,
+ 43 description="Display the version of lyscripts and exit.",
+ 44 )
+ 45 log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field(
+ 46 default="INFO",
+ 47 description="Set the log level of the lyscripts CLI.",
+ 48 )
+ 49
+ 50 data: CliSubCommand[data.DataCLI]
+ 51 sample: CliSubCommand[sample.SampleCLI]
+ 52 compute: CliSubCommand[compute.ComputeCLI]
+ 53 schedule: CliSubCommand[schedule.ScheduleCLI]
+ 54
+ 55 def __init__(self, **kwargs):
+ 56 """Add logging configuration to the lyscripts CLI."""
+ 57 configure_logging(argv=sys.argv, console=console)
+ 58 super().__init__(**kwargs)
+ 59
+ 60 def cli_cmd(self) -> None:
+ 61 """Start the main lyscripts CLI.
+ 62
+ 63 If the ``version`` flag is set, the version of lyscripts is displayed and the
+ 64 program exits. Otherwise, the lyscripts CLI runs one of the subcommands.
+ 65 """
+ 66 logger.debug("Starting lyscripts CLI.")
+ 67
+ 68 if self.version:
+ 69 logger.info(f"lyscripts {__version__}")
+ 70 return
+ 71
+ 72 CliApp.run_subcommand(self)
+ 73
+ 74
+ 75main = assemble_main(settings_cls=LyscriptsCLI, prog_name="lyscripts")
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc___main___py.html b/htmlcov/z_5bf5c588c698c6cc___main___py.html
new file mode 100644
index 0000000..2ec7cf5
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc___main___py.html
@@ -0,0 +1,103 @@
+
+
+
+
+ Coverage for src/lyscripts/__main__.py: 0%
+
+
+
+
+
+
+
+ 1"""Utility for common tasks w.r.t. inference & prediction using `lymph` package."""
+ 2
+ 3from lyscripts import main
+ 4
+ 5if __name__ == "__main__":
+ 6 main()
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc__version_py.html b/htmlcov/z_5bf5c588c698c6cc__version_py.html
new file mode 100644
index 0000000..de0995a
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc__version_py.html
@@ -0,0 +1,118 @@
+
+
+
+
+ Coverage for src/lyscripts/_version.py: 77%
+
+
+
+
+
+
+
+ 1# file generated by setuptools-scm
+ 2# don't change, don't track in version control
+ 3
+ 4__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
+ 5
+ 6TYPE_CHECKING = False
+ 7if TYPE_CHECKING:
+ 8 from typing import Tuple
+ 9 from typing import Union
+ 10
+ 11 VERSION_TUPLE = Tuple[Union[int, str], ...]
+ 12else:
+ 13 VERSION_TUPLE = object
+ 14
+ 15version: str
+ 16__version__: str
+ 17__version_tuple__: VERSION_TUPLE
+ 18version_tuple: VERSION_TUPLE
+ 19
+ 20__version__ = version = '0.1.dev1'
+ 21__version_tuple__ = version_tuple = (0, 1, 'dev1')
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_cli_py.html b/htmlcov/z_5bf5c588c698c6cc_cli_py.html
new file mode 100644
index 0000000..d9f8fd6
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_cli_py.html
@@ -0,0 +1,187 @@
+
+
+
+
+ Coverage for src/lyscripts/cli.py: 54%
+
+
+
+
+
+
+
+ 1"""Utilities for configuring and running CLIs app.
+ 2
+ 3In this module, we define and configure a :py:class:`RichDefaultHelpFormatter` that
+ 4nicely displays the CLI's ``--help`` text. We also provide a function to
+ 5:py:func:`assemble a main function <assemble_main>` for the different CLI apps to save
+ 6some boilerplate code. Lastly, we have two functions related to the `loguru`_ setup.
+ 7
+ 8.. _loguru: https://loguru.readthedocs.io/en/stable
+ 9"""
+ 10
+ 11from collections.abc import Callable
+ 12from typing import Literal
+ 13
+ 14from loguru import logger
+ 15from pydantic_settings import BaseSettings, CliApp, CliSettingsSource
+ 16from rich.console import Console
+ 17from rich.logging import RichHandler
+ 18from rich_argparse import ArgumentDefaultsRichHelpFormatter
+ 19
+ 20
+ 21def assemble_main(
+ 22 settings_cls: type[BaseSettings],
+ 23 prog_name: str,
+ 24) -> Callable[[], None]:
+ 25 """Assemble a ``main()`` function for a CLI app.
+ 26
+ 27 It creates a :py:class:`~pydantic_settings.CliSettingsSource` object with the
+ 28 provided ``settings_cls`` and ``prog_name``. Then, it fills in some default
+ 29 settings for the CLI configuration and runs the CLI app.
+ 30
+ 31 Assembling a ``main()`` function for all subcommands like this saves some
+ 32 boilerplate code.
+ 33 """
+ 34
+ 35 def main() -> None:
+ 36 """Start the main CLI app."""
+ 37 cli_settings_source = CliSettingsSource(
+ 38 settings_cls=settings_cls,
+ 39 cli_prog_name=prog_name,
+ 40 cli_kebab_case=True,
+ 41 cli_use_class_docs_for_groups=True,
+ 42 formatter_class=ArgumentDefaultsRichHelpFormatter,
+ 43 )
+ 44 CliApp.run(settings_cls, cli_settings_source=cli_settings_source)
+ 45
+ 46 return main
+ 47
+ 48
+ 49def somewhat_safely_get_loglevel(
+ 50 argv: list[str],
+ 51) -> Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
+ 52 """Set the log level of the lyscripts CLI.
+ 53
+ 54 This is a bit of a hack, since the :py:class:`~lyscripts.LyscriptsCLI` class is not
+ 55 yet initialized when we need to set the log level. In case the provided log-level is
+ 56 not valid, :py:class:`~lyscripts.LyscriptsCLI` will raise an exception at a later
+ 57 point.
+ 58
+ 59 Return ``"INFO"`` by default.
+ 60 """
+ 61 args_str = " ".join(argv)
+ 62 if "--log-level" in args_str:
+ 63 for log_level in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
+ 64 if log_level in args_str:
+ 65 return log_level
+ 66
+ 67 return "INFO"
+ 68
+ 69
+ 70def configure_logging(
+ 71 argv: list[str],
+ 72 console: Console,
+ 73) -> None:
+ 74 """Configure the `loguru`_ logging system of the lyscripts CLI.
+ 75
+ 76 This function sets the log level and format of the lyscripts CLI. Notably, for
+ 77 a log-level of `DEBUG` the output will contain more information.
+ 78
+ 79 .. _loguru: https://loguru.readthedocs.io/en/stable
+ 80 """
+ 81 logger.enable("lyscripts")
+ 82 logger.enable("lydata")
+ 83 log_level = somewhat_safely_get_loglevel(argv=argv)
+ 84 logger.remove()
+ 85 handler = RichHandler(console=console)
+ 86 logger.add(
+ 87 sink=handler,
+ 88 level=log_level,
+ 89 format="<lvl>{message}</>",
+ 90 )
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_configs_py.html b/htmlcov/z_5bf5c588c698c6cc_configs_py.html
new file mode 100644
index 0000000..44807f3
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_configs_py.html
@@ -0,0 +1,847 @@
+
+
+
+
+ Coverage for src/lyscripts/configs.py: 89%
+
+
+
+
+
+
+
+ 1"""Using `pydantic`_, we define configurations for the package.
+ 2
+ 3Most importantly, these configurations are part of the CLIs that the package provides.
+ 4but they also help with programmatically validating and constructing various objects.
+ 5Maybe most importantly, the :py:class:`GraphConfig` and :py:class:`ModelConfig` may be
+ 6used to precisely and reproducibly define how the function :py:func:`construct_model`
+ 7should create lymphatic progression :py:mod:`~lymph.models`.
+ 8
+ 9.. _pydantic: https://docs.pydantic.dev/latest/
+ 10"""
+ 11
+ 12from __future__ import annotations
+ 13
+ 14import importlib
+ 15import importlib.util
+ 16import os
+ 17import warnings
+ 18from collections.abc import Callable, Sequence
+ 19from copy import deepcopy
+ 20from pathlib import Path
+ 21from typing import Annotated, Any, Literal
+ 22
+ 23import numpy as np
+ 24import pandas as pd
+ 25import yaml
+ 26from loguru import logger
+ 27from lydata.loader import LyDataset
+ 28from lydata.utils import ModalityConfig
+ 29from lymph import graph, models
+ 30from lymph.modalities import Pathological
+ 31from lymph.types import Model, PatternType
+ 32from pydantic import (
+ 33 AfterValidator,
+ 34 BaseModel,
+ 35 ConfigDict,
+ 36 Field,
+ 37 FilePath,
+ 38)
+ 39from pydantic_settings import (
+ 40 BaseSettings,
+ 41 PydanticBaseSettingsSource,
+ 42 YamlConfigSettingsSource,
+ 43)
+ 44from pydantic_settings.sources import DEFAULT_PATH
+ 45
+ 46from lyscripts.utils import binom_pmf, flatten, load_model_samples, load_patient_data
+ 47
+ 48FuncNameType = Literal["binomial"]
+ 49
+ 50
+ 51DIST_MAP: dict[FuncNameType, Callable] = {
+ 52 "binomial": binom_pmf,
+ 53}
+ 54
+ 55
+ 56class CrossValidationConfig(BaseModel):
+ 57 """Configs for splitting a dataset into cross-validation folds."""
+ 58
+ 59 seed: int = Field(
+ 60 default=42,
+ 61 description="Seed for the random number generator.",
+ 62 )
+ 63 folds: int = Field(
+ 64 default=5,
+ 65 description="Number of folds to split the dataset into.",
+ 66 )
+ 67
+ 68
+ 69class DataConfig(BaseModel):
+ 70 """Where to load lymphatic progression data from and how to feed it into a model."""
+ 71
+ 72 source: FilePath | LyDataset = Field(
+ 73 description=(
+ 74 "Either a path to a CSV file or a config that specifies how and where "
+ 75 "to fetch the data from."
+ 76 ),
+ 77 )
+ 78 side: Literal["ipsi", "contra"] | None = Field(
+ 79 default=None,
+ 80 description="Side of the neck to load data for. Only for Unilateral models.",
+ 81 )
+ 82 mapping: dict[Literal[0, 1, 2, 3, 4] | str, int | str] = Field(
+ 83 default_factory=lambda: {i: "early" if i <= 2 else "late" for i in range(5)},
+ 84 description="Optional mapping of numeric T-stages to model T-stages.",
+ 85 )
+ 86
+ 87 def load(self, **get_dataframe_kwargs) -> pd.DataFrame:
+ 88 """Load data from path or the :py:class:`~lydata.loader.LyDataset`."""
+ 89 if isinstance(self.source, LyDataset):
+ 90 return self.source.get_dataframe(**get_dataframe_kwargs)
+ 91
+ 92 return load_patient_data(self.source, **get_dataframe_kwargs)
+ 93
+ 94 def get_load_kwargs(self, **read_csv_kwargs: dict[str, Any]) -> dict[str, Any]:
+ 95 """Get kwargs for :py:meth:`~lymph.types.Model.load_patient_data`."""
+ 96 return {
+ 97 "patient_data": self.load(**(read_csv_kwargs or {})),
+ 98 **self.model_dump(exclude={"source"}, exclude_none=True),
+ 99 }
+ 100
+ 101
+ 102def check_pattern(value: PatternType) -> Any:
+ 103 """Check if the value can be converted to a boolean value."""
+ 104 return {lnl: map_to_optional_bool(v) for lnl, v in value.items()}
+ 105
+ 106
+ 107class DiagnosisConfig(BaseModel):
+ 108 """Defines an ipsi- and contralateral diagnosis pattern."""
+ 109
+ 110 ipsi: dict[str, Annotated[PatternType, AfterValidator(check_pattern)]] = Field(
+ 111 default={},
+ 112 description="Observed diagnoses by different modalities on the ipsi neck.",
+ 113 examples=[{"CT": {"II": True, "III": False}}],
+ 114 )
+ 115 contra: dict[str, Annotated[PatternType, AfterValidator(check_pattern)]] = Field(
+ 116 default={},
+ 117 description="Observed diagnoses by different modalities on the contra neck.",
+ 118 )
+ 119
+ 120 def to_involvement(self, modality: str) -> InvolvementConfig:
+ 121 """Convert the diagnosis pattern to an involvement pattern for ``modality``."""
+ 122 return InvolvementConfig(
+ 123 ipsi=self.ipsi.get(modality, {}),
+ 124 contra=self.contra.get(modality, {}),
+ 125 )
+ 126
+ 127
+ 128class DistributionConfig(BaseModel):
+ 129 """Configuration defining a distribution over diagnose times."""
+ 130
+ 131 kind: Literal["frozen", "parametric"] = Field(
+ 132 default="frozen",
+ 133 description="Parametric distributions may be updated.",
+ 134 )
+ 135 func: FuncNameType = Field(
+ 136 default="binomial",
+ 137 description="Name of predefined function to use as distribution.",
+ 138 )
+ 139 params: dict[str, int | float] = Field(
+ 140 default={},
+ 141 description="Parameters to pass to the predefined function.",
+ 142 )
+ 143
+ 144
+ 145class InvolvementConfig(BaseModel):
+ 146 """Config that defines an ipsi- and contralateral involvement pattern."""
+ 147
+ 148 ipsi: Annotated[PatternType, AfterValidator(check_pattern)] = Field(
+ 149 default={},
+ 150 description="Involvement pattern for the ipsilateral side of the neck.",
+ 151 examples=[{"II": True, "III": False}],
+ 152 )
+ 153 contra: Annotated[PatternType, AfterValidator(check_pattern)] = Field(
+ 154 default={},
+ 155 description="Involvement pattern for the contralateral side of the neck.",
+ 156 )
+ 157
+ 158
+ 159def retrieve_graph_representation(model: Model) -> graph.Representation:
+ 160 """Retrieve the graph representation from a model."""
+ 161 if hasattr(model, "graph"):
+ 162 return model.graph
+ 163
+ 164 if hasattr(model, "hpv"):
+ 165 return retrieve_graph_representation(model.hpv)
+ 166
+ 167 if hasattr(model, "ipsi"):
+ 168 return retrieve_graph_representation(model.ipsi)
+ 169
+ 170 if hasattr(model, "ext"):
+ 171 return retrieve_graph_representation(model.ext)
+ 172
+ 173 raise ValueError("Model does not have a graph representation.")
+ 174
+ 175
+ 176class GraphConfig(BaseModel):
+ 177 """Specifies how the tumor(s) and LNLs are connected in a DAG."""
+ 178
+ 179 tumor: dict[str, list[str]] = Field(
+ 180 description="Define the name of the tumor(s) and which LNLs it/they drain to.",
+ 181 )
+ 182 lnl: dict[str, list[str]] = Field(
+ 183 description="Define the name of the LNL(s) and which LNLs it/they drain to.",
+ 184 )
+ 185
+ 186 @classmethod
+ 187 def from_model(cls: type, model: Model) -> GraphConfig:
+ 188 """Create a ``GraphConfig`` from a ``Model``."""
+ 189 graph = retrieve_graph_representation(model)
+ 190 return cls(
+ 191 tumor={
+ 192 name: [edge.child.name for edge in tumor.out]
+ 193 for name, tumor in graph.tumors.items()
+ 194 },
+ 195 lnl={
+ 196 name: [edge.child.name for edge in lnl.out] # noqa
+ 197 for name, lnl in graph.lnls.items()
+ 198 },
+ 199 )
+ 200
+ 201
+ 202def has_model_symbol(path: Path) -> Path:
+ 203 """Check if the Python file at ``path`` defines a symbol named ``model``."""
+ 204 spec = importlib.util.spec_from_file_location(path.stem, path)
+ 205 module = importlib.util.module_from_spec(spec)
+ 206 spec.loader.exec_module(module)
+ 207
+ 208 if not hasattr(module, "model"):
+ 209 raise ValueError(f"Python file at {path} does not define a symbol 'model'.")
+ 210
+ 211 return path
+ 212
+ 213
+ 214def get_symmetry_kwargs(model: Model) -> dict[str, Any]:
+ 215 """Get the symmetry kwargs from a model."""
+ 216 if isinstance(model, models.Unilateral | models.HPVUnilateral):
+ 217 raise TypeError("Unilateral models do not have symmetry kwargs.")
+ 218
+ 219 if hasattr(model, "ext"):
+ 220 return get_symmetry_kwargs(model.ext)
+ 221
+ 222 return getattr(model, "is_symmetric", {})
+ 223
+ 224
+ 225class ModelConfig(BaseModel):
+ 226 """Define which of the ``lymph`` models to use and how to set them up."""
+ 227
+ 228 external_file: Annotated[FilePath, AfterValidator(has_model_symbol)] | None = Field(
+ 229 default=None,
+ 230 description="Path to a Python file that defines a model.",
+ 231 )
+ 232 class_name: Literal["Unilateral", "Bilateral", "Midline"] = Field(
+ 233 default="Unilateral",
+ 234 description="Name of the model class to use.",
+ 235 )
+ 236 constructor: Literal["binary", "trinary"] = Field(
+ 237 default="binary",
+ 238 description="Trinary models differentiate btw. micro- and macroscopic disease.",
+ 239 )
+ 240 max_time: int = Field(
+ 241 default=10,
+ 242 description="Max. number of time-steps to evolve the model over.",
+ 243 )
+ 244 named_params: Sequence[str] = Field(
+ 245 default=None,
+ 246 description=(
+ 247 "Subset of valid model parameters a sampler may provide in the form of a "
+ 248 "dictionary to the model instead of as an array. Or, after sampling, with "
+ 249 "this list, one may safely recover which parameter corresponds to which "
+ 250 "index in the sample."
+ 251 ),
+ 252 )
+ 253 kwargs: dict[str, Any] = Field(
+ 254 default={},
+ 255 description="Additional keyword arguments to pass to the model constructor.",
+ 256 )
+ 257
+ 258 @classmethod
+ 259 def from_model(cls: type, model: Model) -> ModelConfig:
+ 260 """Create a ``ModelConfig`` from a ``Model``."""
+ 261 warnings.warn(
+ 262 message=(
+ 263 "Not all kwargs passed at initialization can be recovered into a "
+ 264 "config. Make sure to manually double-check the config."
+ 265 ),
+ 266 category=UserWarning,
+ 267 stacklevel=2,
+ 268 )
+ 269
+ 270 if getattr(model, "_named_params", None):
+ 271 additional_kwargs = {"named_params": list(model.named_params)}
+ 272 else:
+ 273 additional_kwargs = {}
+ 274
+ 275 try:
+ 276 additional_kwargs["is_symmetric"] = get_symmetry_kwargs(model)
+ 277 except TypeError:
+ 278 pass
+ 279
+ 280 if isinstance(model, models.Midline):
+ 281 additional_kwargs["use_midext_evo"] = model.use_midext_evo
+ 282 additional_kwargs["use_central"] = hasattr(model, "_central")
+ 283 additional_kwargs["use_mixing"] = hasattr(model, "mixing_param")
+ 284
+ 285 if not hasattr(model, "_unknown"):
+ 286 additional_kwargs["marginalize_unknown"] = False
+ 287
+ 288 return cls(
+ 289 class_name=model.__class__.__name__,
+ 290 constructor="trinary" if model.is_trinary else "binary",
+ 291 max_time=model.max_time,
+ 292 kwargs=additional_kwargs,
+ 293 )
+ 294
+ 295
+ 296def modalityconfig_from_model(model: Model, modality_name: str) -> ModalityConfig:
+ 297 """Create a ``ModalityConfig`` from a ``Model``."""
+ 298 modality = model.get_modality(modality_name)
+ 299 return ModalityConfig(
+ 300 spec=modality.spec,
+ 301 sens=modality.sens,
+ 302 kind="pathological" if isinstance(modality, Pathological) else "clinical",
+ 303 )
+ 304
+ 305
+ 306class DeprecatedModelConfig(BaseModel):
+ 307 """Model configuration prior to ``lyscripts`` major version 1.
+ 308
+ 309 This is implemented for backwards compatibility. Its sole job is to translate
+ 310 the outdated settings format into the new one. Note that the only stuff that needs
+ 311 to be translated is the model configuration itself and the distributions for
+ 312 marginalization over diagnosis times. The :py:class:`~GraphConfig` is still
+ 313 compatible.
+ 314 """
+ 315
+ 316 first_binom_prob: float = Field(
+ 317 description="Fixed parameter for first binomial dist over diagnosis times.",
+ 318 ge=0.0,
+ 319 le=1.0,
+ 320 )
+ 321 max_t: int = Field(
+ 322 description="Max. number of time-steps to evolve the model over.",
+ 323 gt=0,
+ 324 )
+ 325 t_stages: list[int | str] = Field(
+ 326 description=(
+ 327 "List of T-stages to marginalize over in the scenario. The old format "
+ 328 "assumed all T-stages except the first one to be parametric. Only binomial "
+ 329 "distributions are supported."
+ 330 ),
+ 331 )
+ 332 class_: Literal["Unilateral", "Bilateral", "Midline", "MidlineBilateral"] = Field(
+ 333 description="Name of the model class. Only binary models are supported.",
+ 334 alias="class",
+ 335 )
+ 336 kwargs: dict[str, Any] = Field(
+ 337 default={},
+ 338 description="Additional keyword arguments to pass to the model constructor.",
+ 339 )
+ 340
+ 341 def model_post_init(self, __context):
+ 342 """Issue a deprecation warning."""
+ 343 warnings.warn(
+ 344 message="The 'DeprecatedModelConfig' is deprecated.",
+ 345 category=DeprecationWarning,
+ 346 stacklevel=2,
+ 347 )
+ 348 if "Midline" in self.class_:
+ 349 self.class_ = "Midline"
+ 350 warnings.warn(
+ 351 "Model may not be recreated as expected due to extra parameter "
+ 352 "`midext_prob`. Make sure to manually handle edge cases.",
+ 353 stacklevel=2,
+ 354 )
+ 355 return super().model_post_init(__context)
+ 356
+ 357 def translate(self) -> tuple[ModelConfig, dict[int | str, DistributionConfig]]:
+ 358 """Translate the deprecated model config to the new format."""
+ 359 old_kwargs = self.kwargs.copy()
+ 360 new_kwargs = {"use_midext_evo": False} if "Midline" in self.class_ else {}
+ 361
+ 362 if (tumor_spread := old_kwargs.pop("base_symmetric")) is not None:
+ 363 new_kwargs["is_symmetric"] = new_kwargs.get("is_symmetric", {})
+ 364 new_kwargs["is_symmetric"]["tumor_spread"] = tumor_spread
+ 365
+ 366 if (lnl_spread := old_kwargs.pop("trans_symmetric")) is not None:
+ 367 new_kwargs["is_symmetric"] = new_kwargs.get("is_symmetric", {})
+ 368 new_kwargs["is_symmetric"]["lnl_spread"] = lnl_spread
+ 369
+ 370 new_kwargs.update(old_kwargs)
+ 371
+ 372 model_config = ModelConfig(
+ 373 class_name=self.class_,
+ 374 constructor="binary",
+ 375 max_time=self.max_t,
+ 376 kwargs=new_kwargs,
+ 377 )
+ 378
+ 379 distribution_configs = {}
+ 380 for i, t_stage in enumerate(self.t_stages):
+ 381 distribution_configs[t_stage] = DistributionConfig(
+ 382 kind="frozen" if i == 0 else "parametric",
+ 383 func="binomial",
+ 384 params={"p": self.first_binom_prob},
+ 385 )
+ 386
+ 387 return model_config, distribution_configs
+ 388
+ 389
+ 390class SamplingConfig(BaseModel):
+ 391 """Settings to configure the MCMC sampling."""
+ 392
+ 393 storage_file: Path = Field(
+ 394 description="Path to HDF5 file store results or load last state.",
+ 395 )
+ 396 history_file: Path | None = Field(
+ 397 default=None,
+ 398 description="Path to store the burn-in metrics (as CSV file).",
+ 399 )
+ 400 dataset: str = Field(
+ 401 default="mcmc",
+ 402 description="Name of the dataset in the HDF5 file.",
+ 403 )
+ 404 cores: int | None = Field(
+ 405 gt=0,
+ 406 default=os.cpu_count(),
+ 407 description=(
+ 408 "Number of cores to use for parallel sampling. If `None`, no parallel "
+ 409 "processing is used."
+ 410 ),
+ 411 )
+ 412 seed: int = Field(
+ 413 default=42,
+ 414 description="Seed for the random number generator.",
+ 415 )
+ 416 walkers_per_dim: int = Field(
+ 417 default=20,
+ 418 description="Number of walkers per parameter space dimension.",
+ 419 )
+ 420 check_interval: int = Field(
+ 421 default=50,
+ 422 description="Check for convergence each time after this many steps.",
+ 423 )
+ 424 trust_factor: float = Field(
+ 425 default=50.0,
+ 426 description=(
+ 427 "Trust the autocorrelation time only when it's smaller than this factor "
+ 428 "times the length of the chain."
+ 429 ),
+ 430 )
+ 431 relative_thresh: float = Field(
+ 432 default=0.05,
+ 433 description="Relative threshold for convergence.",
+ 434 )
+ 435 num_steps: int | None = Field(
+ 436 default=100,
+ 437 description=("Number of steps to take in the MCMC sampling."),
+ 438 )
+ 439 thin_by: int = Field(
+ 440 default=10,
+ 441 description="How many samples to draw before for saving one.",
+ 442 )
+ 443 inverse_temp: float = Field(
+ 444 default=1.0,
+ 445 description=(
+ 446 "Inverse temperature for thermodynamic integration. Note that this is not "
+ 447 "yet fully implemented."
+ 448 ),
+ 449 )
+ 450
+ 451 def load(self, thin: int = 1) -> np.ndarray:
+ 452 """Load the samples from the HDF5 file.
+ 453
+ 454 Note that the ``thin`` represents another round of thinning and is usually
+ 455 not necessary if the samples were already thinned during the sampling process.
+ 456 """
+ 457 return load_model_samples(
+ 458 file_path=self.storage_file,
+ 459 name=self.dataset,
+ 460 thin=thin,
+ 461 )
+ 462
+ 463
+ 464def map_to_optional_bool(value: Any) -> Any:
+ 465 """Try to convert the options in the `PatternType` to a boolean value."""
+ 466 if value in [True, "involved", 1]:
+ 467 return True
+ 468
+ 469 if value in [False, "healthy", 0]:
+ 470 return False
+ 471
+ 472 return value
+ 473
+ 474
+ 475class ScenarioConfig(BaseModel):
+ 476 """Define a scenario for which e.g. prevalences and risks may be computed."""
+ 477
+ 478 t_stages: list[int | str] = Field(
+ 479 description="List of T-stages to marginalize over in the scenario.",
+ 480 examples=[["early"], [3, 4]],
+ 481 )
+ 482 t_stages_dist: list[float] = Field(
+ 483 default=[1.0],
+ 484 description="Distribution over T-stages to use for marginalization.",
+ 485 examples=[[1.0], [0.6, 0.4]],
+ 486 )
+ 487 midext: bool | None = Field(
+ 488 default=None,
+ 489 description="Whether the patient's tumor extends over the midline.",
+ 490 )
+ 491 mode: Literal["HMM", "BN"] = Field(
+ 492 default="HMM",
+ 493 description="Which underlying model architecture to use.",
+ 494 )
+ 495 involvement: InvolvementConfig = InvolvementConfig()
+ 496 diagnosis: DiagnosisConfig = DiagnosisConfig()
+ 497
+ 498 def model_post_init(self, __context: Any) -> None:
+ 499 """Interpolate and normalize the distribution."""
+ 500 self.interpolate()
+ 501 self.normalize()
+ 502
+ 503 def interpolate(self):
+ 504 """Interpolate the distribution to the number of ``t_stages``."""
+ 505 if len(self.t_stages) != len(self.t_stages_dist):
+ 506 new_x = np.linspace(0.0, 1.0, len(self.t_stages))
+ 507 old_x = np.linspace(0.0, 1.0, len(self.t_stages_dist))
+ 508 # cast to list to make ``__eq__`` work
+ 509 self.t_stages_dist = np.interp(new_x, old_x, self.t_stages_dist).tolist()
+ 510
+ 511 def normalize(self):
+ 512 """Normalize the distribution to sum to 1."""
+ 513 if not np.isclose(np.sum(self.t_stages_dist), 1.0):
+ 514 self.t_stages_dist = (
+ 515 np.array(self.t_stages_dist) / np.sum(self.t_stages_dist)
+ 516 ).tolist() # cast to list to make ``__eq__`` work
+ 517
+ 518
+ 519def _construct_model_from_external(path: Path) -> Model:
+ 520 """Construct a model from a Python file."""
+ 521 module_name = path.stem
+ 522 spec = importlib.util.spec_from_file_location(module_name, path)
+ 523 module = importlib.util.module_from_spec(spec)
+ 524 spec.loader.exec_module(module)
+ 525 logger.info(f"Loaded model from {path}. This ignores model and graph configs.")
+ 526 return module.model
+ 527
+ 528
+ 529def construct_model(
+ 530 model_config: ModelConfig,
+ 531 graph_config: GraphConfig,
+ 532) -> Model:
+ 533 """Construct a model from a ``model_config``.
+ 534
+ 535 The default/expected use of this is to specify a model class from the
+ 536 `lymph`_ package and pass the necessary arguments to its constructor.
+ 537 However, it is also possible to load a model from an external Python file via the
+ 538 ``external`` attribute of the ``model_config`` argument. In this case, a symbol
+ 539 with name ``model`` must be defined in the file that is to be loaded.
+ 540
+ 541 .. note::
+ 542
+ 543 No check is performed on the model's compatibility with the command/pipeline
+ 544 it is used in. It is assumed the model complies with the
+ 545 :py:class:`model type <lymph.types.Model>` specifications of the `lymph`_
+ 546 package.
+ 547
+ 548 .. _lymph: https://lymph-model.readthedocs.io/stable/
+ 549 """
+ 550 if model_config.external_file is not None:
+ 551 return _construct_model_from_external(model_config.external_file)
+ 552
+ 553 cls = getattr(models, model_config.class_name)
+ 554 constructor = getattr(cls, model_config.constructor)
+ 555 model = constructor(
+ 556 graph_dict=flatten(graph_config.model_dump()),
+ 557 max_time=model_config.max_time,
+ 558 named_params=model_config.named_params,
+ 559 **model_config.kwargs,
+ 560 )
+ 561 logger.info(f"Constructed model: {model}")
+ 562 return model
+ 563
+ 564
+ 565def add_distributions(
+ 566 model: Model,
+ 567 configs: dict[str | int, DistributionConfig],
+ 568 mapping: dict[FuncNameType, Callable] | None = None,
+ 569 inplace: bool = False,
+ 570) -> Model:
+ 571 """Construct and add distributions over diagnose times to a ``model``."""
+ 572 if not inplace:
+ 573 model = deepcopy(model)
+ 574 logger.debug("Created deepcopy of model.")
+ 575
+ 576 mapping = mapping or DIST_MAP
+ 577
+ 578 for t_stage, dist_config in configs.items():
+ 579 if dist_config.kind == "frozen":
+ 580 support = np.arange(model.max_time + 1)
+ 581 dist = mapping[dist_config.func](support, **dist_config.params)
+ 582 elif dist_config.kind == "parametric":
+ 583 dist = mapping[dist_config.func]
+ 584 else:
+ 585 raise ValueError(f"Unknown distribution kind: {dist_config.kind}")
+ 586
+ 587 model.set_distribution(t_stage, dist)
+ 588 if dist_config.kind == "parametric" and dist_config.params:
+ 589 params = {f"{t_stage}_{k}": v for k, v in dist_config.params.items()}
+ 590 model.set_params(**params)
+ 591
+ 592 logger.debug(f"Set {dist_config.kind} distribution for '{t_stage}': {dist}")
+ 593
+ 594 logger.info(f"Added {len(configs)} distributions to model: {model}")
+ 595 return model
+ 596
+ 597
+ 598def add_modalities(
+ 599 model: Model,
+ 600 modalities: dict[str, ModalityConfig],
+ 601 inplace: bool = False,
+ 602) -> Model:
+ 603 """Add ``modalities`` to a ``model``."""
+ 604 if not inplace:
+ 605 model = deepcopy(model)
+ 606 logger.debug("Created deepcopy of model.")
+ 607
+ 608 for modality, modality_config in modalities.items():
+ 609 model.set_modality(modality, **modality_config.model_dump())
+ 610 logger.debug(f"Added modality {modality} to model: {modality_config}")
+ 611
+ 612 logger.info(f"Added {len(modalities)} modalities to model: {model}")
+ 613 return model
+ 614
+ 615
+ 616def add_data(
+ 617 model: Model,
+ 618 path: Path,
+ 619 side: Literal["ipsi", "contra"],
+ 620 mapping: dict[Literal[0, 1, 2, 3, 4], int | str] | None = None,
+ 621 inplace: bool = False,
+ 622) -> Model:
+ 623 """Add data to a ``model``."""
+ 624 data = pd.read_csv(path, header=[0, 1, 2])
+ 625 logger.debug(f"Loaded data from {path}: Shape: {data.shape}")
+ 626
+ 627 kwargs = {"patient_data": data, "mapping": mapping}
+ 628 if isinstance(model, models.Unilateral):
+ 629 kwargs["side"] = side
+ 630
+ 631 if not inplace:
+ 632 model = deepcopy(model)
+ 633 logger.debug("Created deepcopy of model.")
+ 634
+ 635 model.load_patient_data(**kwargs)
+ 636 logger.info(f"Added data to model: {model}")
+ 637 return model
+ 638
+ 639
+ 640PathType = Path | str | Sequence[Path | str]
+ 641
+ 642
+ 643class DynamicYamlConfigSettingsSource(YamlConfigSettingsSource):
+ 644 """YAML config source that allows dynamic file path specification.
+ 645
+ 646 This is heavily inspired by `this comment`_ in the discussion on a related issue
+ 647 of the `pydantic-settings`_ GitHub repository.
+ 648
+ 649 Essentially, this little hack allows a user to specify a one or multiple YAML files
+ 650 from which the CLI should read configurations. Normally, `pydantic-settings` only
+ 651 allows hard-coding the location of these config files.
+ 652
+ 653 .. _this comment: https://github.com/pydantic/pydantic-settings/issues/259#issuecomment-2549444286
+ 654 .. _pydantic-settings: https://github.com/pydantic/pydantic-settings
+ 655 """
+ 656
+ 657 def __init__(
+ 658 self,
+ 659 settings_cls,
+ 660 yaml_file: PathType | None = DEFAULT_PATH,
+ 661 yaml_file_encoding: str | None = None,
+ 662 yaml_file_path_field: str = "configs",
+ 663 ) -> None:
+ 664 """Allow getting the YAML file path from any key in the current state.
+ 665
+ 666 The argument ``yaml_file_path_field`` should be the :py:class:`BaseSettings`
+ 667 field that contains the path(s) to the YAML file(s).
+ 668
+ 669 Note that all config files must have a ``version: 1`` key in them to be
+ 670 recognized as valid config files.
+ 671 """
+ 672 self.yaml_file_path_field = yaml_file_path_field
+ 673 super().__init__(settings_cls, yaml_file, yaml_file_encoding)
+ 674
+ 675 def _read_file(self, file_path: Path) -> dict[str, Any]:
+ 676 """Read the YAML and raise exception when ``version: 1`` not found."""
+ 677 with open(file_path, encoding=self.yaml_file_encoding) as yaml_file:
+ 678 data = yaml.safe_load(yaml_file) or {}
+ 679 if data.get("version") != 1:
+ 680 raise ValueError(
+ 681 f"Config file {file_path} does not have a 'version: 1' key. "
+ 682 "For compatibility reasons, all config files must have this key.",
+ 683 )
+ 684 return data
+ 685
+ 686 def __call__(self) -> dict[str, Any]:
+ 687 """Reload the config files from the paths in the current state."""
+ 688 yaml_file_to_reload = self.current_state.get(
+ 689 self.yaml_file_path_field,
+ 690 self.yaml_file_path,
+ 691 )
+ 692 logger.debug(f"Reloading YAML files from {yaml_file_to_reload} (if it exists).")
+ 693 self.__init__(
+ 694 settings_cls=self.settings_cls,
+ 695 yaml_file=yaml_file_to_reload,
+ 696 yaml_file_encoding=self.yaml_file_encoding,
+ 697 yaml_file_path_field=self.yaml_file_path_field,
+ 698 )
+ 699 return super().__call__()
+ 700
+ 701 def __repr__(self) -> str:
+ 702 """Return a string representation of the source."""
+ 703 return (
+ 704 self.__class__.__name__
+ 705 + "("
+ 706 + f"yaml_file={self.yaml_file_path!r}, "
+ 707 + f"yaml_file_encoding={self.yaml_file_encoding!r}, "
+ 708 + f"yaml_file_path_field={self.yaml_file_path_field!r}"
+ 709 + ")"
+ 710 )
+ 711
+ 712
+ 713class BaseCLI(BaseSettings):
+ 714 """Base settings class for all CLI scripts to inherit from."""
+ 715
+ 716 model_config = ConfigDict(yaml_file="config.yaml", extra="ignore")
+ 717
+ 718 configs: list[Path] = Field(
+ 719 default=["config.yaml"],
+ 720 description=(
+ 721 "Path to the YAML file(s) that contain the configuration(s). Configs from "
+ 722 "YAML files may be overwritten by command line arguments. When multiple "
+ 723 "files are specified, the configs are merged in the order they are given. "
+ 724 "Note that every config file must have a `version: 1` key in it."
+ 725 ),
+ 726 )
+ 727
+ 728 @classmethod
+ 729 def settings_customise_sources(
+ 730 cls,
+ 731 settings_cls: type[BaseSettings],
+ 732 init_settings: PydanticBaseSettingsSource,
+ 733 env_settings: PydanticBaseSettingsSource,
+ 734 dotenv_settings: PydanticBaseSettingsSource,
+ 735 file_secret_settings: PydanticBaseSettingsSource,
+ 736 ) -> tuple[PydanticBaseSettingsSource, ...]:
+ 737 """Add the dynamic YAML config source to the CLI settings."""
+ 738 dynamic_yaml_config_source = DynamicYamlConfigSettingsSource(
+ 739 settings_cls=settings_cls,
+ 740 yaml_file_path_field="configs",
+ 741 yaml_file_encoding="utf-8",
+ 742 )
+ 743 logger.debug(f"Created {dynamic_yaml_config_source = }")
+ 744 return (
+ 745 init_settings,
+ 746 env_settings,
+ 747 dotenv_settings,
+ 748 file_secret_settings,
+ 749 dynamic_yaml_config_source,
+ 750 )
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_decorators_py.html b/htmlcov/z_5bf5c588c698c6cc_decorators_py.html
new file mode 100644
index 0000000..35c5565
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_decorators_py.html
@@ -0,0 +1,185 @@
+
+
+
+
+ Coverage for src/lyscripts/decorators.py: 90%
+
+
+
+
+
+
+
+ 1"""Decorators to avoid repetitive snippets of code.
+ 2
+ 3E.g. safely opening files or logging the state of a function call.
+ 4
+ 5This is *not* a command line tool.
+ 6"""
+ 7
+ 8import functools
+ 9import logging
+ 10from collections.abc import Callable
+ 11from functools import wraps
+ 12from pathlib import Path
+ 13from typing import Any
+ 14
+ 15
+ 16def assemble_signature(*args, **kwargs) -> str:
+ 17 """Assemble the signature of the function call."""
+ 18 args_str = ", ".join(str(arg) for arg in args)
+ 19 kwargs_str = ", ".join(f"{key}={value}" for key, value in kwargs.items())
+ 20 return ", ".join([args_str, kwargs_str])
+ 21
+ 22
+ 23def log_state(log_level: int = logging.INFO) -> Callable:
+ 24 """Provide a decorator that logs the state of the function execution.
+ 25
+ 26 The log message will simply be the function name where underscores are replaced
+ 27 with spaces. The `log_level` can be set in the decorator call.
+ 28 """
+ 29
+ 30 def log_decorator(func: Callable):
+ 31 """Decorate function for which to add logs."""
+ 32
+ 33 @functools.wraps(func)
+ 34 def wrapper(*args, **kwargs):
+ 35 """Execute decorated function."""
+ 36 logger = logging.getLogger(func.__module__)
+ 37 signature = assemble_signature(*args, **kwargs)
+ 38 logger.debug(f"Executing {func.__name__}({signature}).")
+ 39 log_msg_from_func = func.__name__.replace("_", " ").capitalize() + "."
+ 40
+ 41 try:
+ 42 logger.log(
+ 43 log_level,
+ 44 log_msg_from_func,
+ 45 extra={
+ 46 "func_filepath": f"{func.__module__.replace('.', '/')}.py",
+ 47 "func_name": func.__name__,
+ 48 "module_name": func.__module__,
+ 49 },
+ 50 )
+ 51 return func(*args, **kwargs)
+ 52
+ 53 except Exception as exc:
+ 54 logger.error(f"Error calling {func.__name__}().", exc_info=exc)
+ 55 raise exc
+ 56
+ 57 return wrapper
+ 58
+ 59 return log_decorator
+ 60
+ 61
+ 62def check_input_file_exists(loading_func: Callable) -> Callable:
+ 63 """Check if the file path provided to the `loading_func` exists."""
+ 64
+ 65 @wraps(loading_func)
+ 66 def inner(file_path: str, *args, **kwargs) -> Any:
+ 67 """Execute wrapped loading function."""
+ 68 file_path = Path(file_path)
+ 69 if not file_path.is_file():
+ 70 raise FileNotFoundError(f"File {file_path} does not exist.")
+ 71
+ 72 return loading_func(file_path, *args, **kwargs)
+ 73
+ 74 return inner
+ 75
+ 76
+ 77def check_output_dir_exists(saving_func: Callable) -> Callable:
+ 78 """Make sure the parent directory of the saved file exists."""
+ 79
+ 80 @wraps(saving_func)
+ 81 def inner(file_path: str, *args, **kwargs) -> Any:
+ 82 """Execute wrapped saving function."""
+ 83 file_path = Path(file_path)
+ 84 file_path.parent.mkdir(parents=True, exist_ok=True)
+ 85
+ 86 return saving_func(file_path, *args, **kwargs)
+ 87
+ 88 return inner
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_evaluate_py.html b/htmlcov/z_5bf5c588c698c6cc_evaluate_py.html
new file mode 100644
index 0000000..85cd60a
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_evaluate_py.html
@@ -0,0 +1,309 @@
+
+
+
+
+ Coverage for src/lyscripts/evaluate.py: 24%
+
+
+
+
+
+
+
+ 1"""Evaluate the performance of the trained model.
+ 2
+ 3This is done by computing quantities like the Bayesian information criterion (BIC) or
+ 4(if thermodynamic integration was performed) the actual evidence (with error) of the
+ 5model.
+ 6"""
+ 7
+ 8import argparse
+ 9import json
+ 10from pathlib import Path
+ 11
+ 12import emcee
+ 13import h5py
+ 14import numpy as np
+ 15import pandas as pd
+ 16from loguru import logger
+ 17from scipy.integrate import trapezoid
+ 18
+ 19from lyscripts.utils import load_patient_data, load_yaml_params
+ 20
+ 21RNG = np.random.default_rng()
+ 22
+ 23
+ 24def _add_parser(
+ 25 subparsers: argparse._SubParsersAction,
+ 26 help_formatter,
+ 27):
+ 28 """Add an ``ArgumentParser`` to the subparsers action."""
+ 29 parser = subparsers.add_parser(
+ 30 Path(__file__).name.replace(".py", ""),
+ 31 description=__doc__,
+ 32 help=__doc__,
+ 33 formatter_class=help_formatter,
+ 34 )
+ 35 _add_arguments(parser)
+ 36
+ 37
+ 38def _add_arguments(parser: argparse.ArgumentParser):
+ 39 """Add arguments to a ``subparsers`` instance and run its main function when chosen.
+ 40
+ 41 This is called by the parent module that is called via the command line.
+ 42 """
+ 43 parser.add_argument(
+ 44 "data",
+ 45 type=Path,
+ 46 help="Path to the tables of patient data (CSV).",
+ 47 )
+ 48 parser.add_argument("model", type=Path, help="Path to model output files (HDF5).")
+ 49
+ 50 parser.add_argument(
+ 51 "-p",
+ 52 "--params",
+ 53 default="./params.yaml",
+ 54 type=Path,
+ 55 help="Path to parameter file",
+ 56 )
+ 57 parser.add_argument(
+ 58 "--plots",
+ 59 default="./plots",
+ 60 type=Path,
+ 61 help="Directory for storing plots",
+ 62 )
+ 63 parser.add_argument(
+ 64 "--metrics",
+ 65 default="./metrics.json",
+ 66 type=Path,
+ 67 help="Path to metrics file",
+ 68 )
+ 69
+ 70 parser.set_defaults(run_main=main)
+ 71
+ 72
+ 73def comp_bic(log_probs: np.ndarray, num_params: int, num_data: int) -> float:
+ 74 r"""Compute the negative one half of the Bayesian Information Criterion (BIC).
+ 75
+ 76 The BIC is defined as [^1]
+ 77 $$ BIC = k \\ln{n} - 2 \\ln{\\hat{L}} $$
+ 78 where $k$ is the number of parameters ``num_params``, $n$ the number of datapoints
+ 79 ``num_data`` and $\\hat{L}$ the maximum likelihood estimate of the ``log_prob``.
+ 80 It is constructed such that the following is an
+ 81 approximation of the model evidence:
+ 82 $$ p(D \\mid m) \\approx \\exp{\\left( - BIC / 2 \\right)} $$
+ 83 which is why this function returns the negative one half of it.
+ 84
+ 85 [^1]: https://en.wikipedia.org/wiki/Bayesian_information_criterion
+ 86 """
+ 87 return np.max(log_probs) - num_params * np.log(num_data) / 2.0
+ 88
+ 89
+ 90def compute_evidence(
+ 91 temp_schedule: np.ndarray,
+ 92 log_probs: np.ndarray,
+ 93 num: int = 1000,
+ 94) -> tuple[float, float]:
+ 95 """Compute the evidence and its standard deviation.
+ 96
+ 97 Given a ``temp_schedule`` of inverse temperatures and corresponding sets of
+ 98 ``log_probs``, draw ``num`` "paths" of log-probabilities and compute the evidence
+ 99 for each using trapezoidal integration.
+ 100
+ 101 The evidence is then the mean of those ``num`` integrations, while the error is
+ 102 their standard deviation.
+ 103 """
+ 104 integrals = np.zeros(shape=num)
+ 105 for i in range(num):
+ 106 rand_idx = RNG.choice(log_probs.shape[1], size=log_probs.shape[0])
+ 107 drawn_accuracy = log_probs[np.arange(log_probs.shape[0]), rand_idx].copy()
+ 108 integrals[i] = trapezoid(y=drawn_accuracy, x=temp_schedule)
+ 109 return np.mean(integrals), np.std(integrals)
+ 110
+ 111
+ 112def compute_ti_results(
+ 113 metrics: dict,
+ 114 params: dict,
+ 115 ndim: int,
+ 116 h5_file: Path,
+ 117 model: Path,
+ 118) -> tuple[np.ndarray, np.ndarray]:
+ 119 """Compute the results in case of a thermodynamic integration run."""
+ 120 temp_schedule = params["sampling"]["temp_schedule"]
+ 121 num_temps = len(temp_schedule)
+ 122
+ 123 if num_temps != len(h5_file["ti"]):
+ 124 raise RuntimeError(
+ 125 f"Parameters suggest temp schedule of length {num_temps}, "
+ 126 f"but stored are {len(h5_file['ti'])}",
+ 127 )
+ 128
+ 129 nwalker = ndim * params["sampling"]["walkers_per_dim"]
+ 130 nsteps = params["sampling"]["nsteps"]
+ 131 ti_log_probs = np.zeros(shape=(num_temps, nsteps * nwalker))
+ 132
+ 133 for i, run in enumerate(h5_file["ti"]):
+ 134 reader = emcee.backends.HDFBackend(model, name=f"ti/{run}", read_only=True)
+ 135 ti_log_probs[i] = reader.get_blobs(flat=True)
+ 136
+ 137 evidence, evidence_std = compute_evidence(temp_schedule, ti_log_probs)
+ 138 metrics["evidence"] = evidence
+ 139 metrics["evidence_std"] = evidence_std
+ 140
+ 141 return temp_schedule, ti_log_probs
+ 142
+ 143
+ 144def main(args: argparse.Namespace):
+ 145 """Run main script."""
+ 146 metrics = {}
+ 147
+ 148 params = load_yaml_params(args.params)
+ 149 model = None # create_model(params)
+ 150 ndim = len(model.get_params())
+ 151 data = load_patient_data(args.data)
+ 152 h5_file = h5py.File(args.model, mode="r")
+ 153
+ 154 # if TI has been performed, compute the accuracy for every step
+ 155 if "ti" in h5_file:
+ 156 temp_schedule, ti_log_probs = compute_ti_results(
+ 157 metrics=metrics,
+ 158 params=params,
+ 159 ndim=ndim,
+ 160 h5_file=h5_file,
+ 161 model=args.model,
+ 162 )
+ 163 logger.info(
+ 164 "Computed results of thermodynamic integration with "
+ 165 f"{len(temp_schedule)} steps",
+ 166 )
+ 167
+ 168 # store inverse temperatures and log-probs in CSV file
+ 169 args.plots.parent.mkdir(exist_ok=True)
+ 170
+ 171 beta_vs_accuracy = pd.DataFrame(
+ 172 np.array(
+ 173 [
+ 174 temp_schedule,
+ 175 np.mean(ti_log_probs, axis=1),
+ 176 np.std(ti_log_probs, axis=1),
+ 177 ],
+ 178 ).T,
+ 179 columns=["β", "accuracy", "std"],
+ 180 )
+ 181 beta_vs_accuracy.to_csv(args.plots, index=False)
+ 182 logger.info(f"Plotted β vs accuracy at {args.plots}")
+ 183
+ 184 # use blobs, because also for TI, this is the unscaled log-prob
+ 185 backend = emcee.backends.HDFBackend(args.model, read_only=True, name="mcmc")
+ 186 final_log_probs = backend.get_blobs()
+ 187 logger.info(f"Opened samples from emcee backend from {args.model}")
+ 188
+ 189 # store metrics in JSON file
+ 190 args.metrics.parent.mkdir(parents=True, exist_ok=True)
+ 191 args.metrics.touch(exist_ok=True)
+ 192
+ 193 metrics["BIC"] = comp_bic(
+ 194 final_log_probs,
+ 195 ndim,
+ 196 len(data),
+ 197 )
+ 198 metrics["max_llh"] = np.max(final_log_probs)
+ 199 metrics["mean_llh"] = np.mean(final_log_probs)
+ 200
+ 201 with open(args.metrics, mode="w", encoding="utf-8") as metrics_file:
+ 202 json.dump(metrics, metrics_file)
+ 203
+ 204 logger.info(f"Wrote out metrics to {args.metrics}")
+ 205
+ 206
+ 207if __name__ == "__main__":
+ 208 parser = argparse.ArgumentParser(description=__doc__)
+ 209 _add_arguments(parser)
+ 210
+ 211 args = parser.parse_args()
+ 212 args.run_main(args)
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_plots_py.html b/htmlcov/z_5bf5c588c698c6cc_plots_py.html
new file mode 100644
index 0000000..5dce988
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_plots_py.html
@@ -0,0 +1,508 @@
+
+
+
+
+ Coverage for src/lyscripts/plots.py: 86%
+
+
+
+
+
+
+
+ 1"""Utility functions for the plotting commands."""
+ 2
+ 3from __future__ import annotations
+ 4
+ 5from abc import abstractmethod
+ 6from collections.abc import Mapping
+ 7from dataclasses import field
+ 8from itertools import cycle
+ 9from pathlib import Path
+ 10from typing import TYPE_CHECKING, Any, TypeVar
+ 11
+ 12import h5py
+ 13import matplotlib.pyplot as plt
+ 14import numpy as np
+ 15import scipy as sp
+ 16from numpydantic import NDArray, Shape
+ 17from pydantic import BaseModel
+ 18
+ 19from lyscripts.decorators import (
+ 20 check_input_file_exists,
+ 21 check_output_dir_exists,
+ 22 log_state,
+ 23)
+ 24
+ 25if TYPE_CHECKING:
+ 26 from matplotlib.axes._axes import Axes as MPLAxes
+ 27 from matplotlib.figure import Figure
+ 28
+ 29# define USZ colors
+ 30COLORS = {
+ 31 "blue": "#005ea8",
+ 32 "orange": "#f17900",
+ 33 "green": "#00afa5",
+ 34 "red": "#ae0060",
+ 35 "gray": "#c5d5db",
+ 36}
+ 37COLOR_CYCLE = cycle(COLORS.values())
+ 38CM_PER_INCH = 2.54
+ 39
+ 40
+ 41def floor_at_decimal(value: float, decimal: int) -> float:
+ 42 """Compute the floor of ``value`` for the specified ``decimal``.
+ 43
+ 44 Essentially the distance to the right of the decimal point. May be negative.
+ 45 """
+ 46 power = 10**decimal
+ 47 return np.floor(power * value) / power
+ 48
+ 49
+ 50def ceil_at_decimal(value: float, decimal: int) -> float:
+ 51 """Compute the ceiling of ``value`` for the specified ``decimal``.
+ 52
+ 53 Analog to :py:func:`.floor_at_decimal`, this is the distance to the right of the
+ 54 decimal point. May be negative.
+ 55 """
+ 56 return -floor_at_decimal(-value, decimal)
+ 57
+ 58
+ 59def floor_to_step(value: float, step: float) -> float:
+ 60 """Compute next value on ladder of stepsize ``step`` still below ``value``."""
+ 61 return (value // step) * step
+ 62
+ 63
+ 64def ceil_to_step(value: float, step: float) -> float:
+ 65 """Compute next value on ladder of stepsize ``step`` still above ``value``."""
+ 66 return floor_to_step(value, step) + step
+ 67
+ 68
+ 69def clean_and_check(filename: str | Path) -> Path:
+ 70 """Check if file with ``filename`` exists.
+ 71
+ 72 If not, raise error, otherwise return cleaned :py:class:`~pathlib.PosixPath`.
+ 73 """
+ 74 filepath = Path(filename)
+ 75 if not filepath.exists():
+ 76 msg = f"File with the name {filename} does not exist at {filepath.resolve()}"
+ 77 raise FileNotFoundError(msg)
+ 78 return filepath
+ 79
+ 80
+ 81AbstractDistributionT = TypeVar("AbstractDistributionT", bound="AbstractDistribution")
+ 82
+ 83
+ 84class AbstractDistribution(BaseModel):
+ 85 """Abstract class for distributions that should be plotted."""
+ 86
+ 87 scale: float = 100.0
+ 88 offset: float = 0.0
+ 89 kwargs: dict[str, Any] = field(default_factory=lambda: {})
+ 90
+ 91 @abstractmethod
+ 92 def draw(self, axes: MPLAxes) -> MPLAxes:
+ 93 """Draw the distribution into the provided ``axes``."""
+ 94 ...
+ 95
+ 96 @abstractmethod
+ 97 def left_percentile(self, percent: float) -> float:
+ 98 """Compute the point where ``percent`` of the values are to the left."""
+ 99 ...
+ 100
+ 101 @abstractmethod
+ 102 def right_percentile(self, percent: float) -> float:
+ 103 """Compute the point where ``percent`` of the values are to the right."""
+ 104 ...
+ 105
+ 106 def _get_label(self) -> str:
+ 107 """Compute label for when ``kwargs`` does not contain one."""
+ 108
+ 109 @property
+ 110 def label(self) -> str:
+ 111 """Return the label of the histogram."""
+ 112 return self.kwargs.get("label", self._get_label())
+ 113
+ 114
+ 115class Histogram(AbstractDistribution):
+ 116 """Class containing data for plotting a histogram."""
+ 117
+ 118 raw_values: NDArray[Shape["*"], float] # noqa: F722
+ 119
+ 120 @property
+ 121 def values(self) -> np.ndarray:
+ 122 """Return the values of the histogram scaled and offset."""
+ 123 return self.raw_values * self.scale + self.offset
+ 124
+ 125 @classmethod
+ 126 def from_hdf5(
+ 127 cls: type[Histogram],
+ 128 filename: str | Path,
+ 129 dataname: str,
+ 130 scale: float = 100.0,
+ 131 offset: float = 0.0,
+ 132 **kwargs,
+ 133 ) -> Histogram:
+ 134 """Create a histogram from an HDF5 file."""
+ 135 filename = clean_and_check(filename)
+ 136 with h5py.File(filename, mode="r") as h5file:
+ 137 dataset = h5file[dataname]
+ 138 if "label" not in kwargs:
+ 139 kwargs["label"] = get_label(dataset.attrs)
+ 140 return cls(raw_values=dataset[:], scale=scale, offset=offset, kwargs=kwargs)
+ 141
+ 142 def left_percentile(self, percent: float) -> float:
+ 143 """Compute the point where `percent` of the values are to the left."""
+ 144 return np.percentile(self.values, percent)
+ 145
+ 146 def right_percentile(self, percent: float) -> float:
+ 147 """Compute the point where `percent` of the values are to the right."""
+ 148 return np.percentile(self.values, 100.0 - percent)
+ 149
+ 150 def draw(self, axes: MPLAxes, **defaults) -> Any:
+ 151 """Draw the histogram into the provided ``axes``."""
+ 152 xlim = axes.get_xlim()
+ 153
+ 154 hist_kwargs = defaults.get("hist", {}).copy()
+ 155 hist_kwargs.update(self.kwargs)
+ 156
+ 157 if self.label is not None:
+ 158 hist_kwargs["label"] = self.label
+ 159
+ 160 return axes.hist(self.values, range=xlim, **hist_kwargs)
+ 161
+ 162
+ 163class BetaPosterior(AbstractDistribution):
+ 164 """Class for storing plot configs for a Beta posterior."""
+ 165
+ 166 num_success: int
+ 167 num_total: int
+ 168
+ 169 @classmethod
+ 170 def from_hdf5(
+ 171 cls: type[BetaPosterior],
+ 172 filename: str | Path,
+ 173 dataname: str,
+ 174 scale: float = 100.0,
+ 175 offset: float = 0.0,
+ 176 **kwargs,
+ 177 ) -> BetaPosterior:
+ 178 """Initialize data container for Beta posteriors from HDF5 file."""
+ 179 filename = clean_and_check(filename)
+ 180 with h5py.File(filename, mode="r") as h5file:
+ 181 dataset = h5file[dataname]
+ 182 try:
+ 183 num_success = int(dataset.attrs["num_match"])
+ 184 num_total = int(dataset.attrs["num_total"])
+ 185 except KeyError as key_err:
+ 186 raise KeyError(
+ 187 "Dataset does not contain observed prevalence data",
+ 188 ) from key_err
+ 189
+ 190 return cls(
+ 191 num_success=num_success,
+ 192 num_total=num_total,
+ 193 scale=scale,
+ 194 offset=offset,
+ 195 kwargs=kwargs,
+ 196 )
+ 197
+ 198 def _get_label(self) -> str:
+ 199 return f"data: {self.num_success} of {self.num_total}"
+ 200
+ 201 @property
+ 202 def num_fail(self):
+ 203 """Return the number of failures, i.e. the totals minus the successes."""
+ 204 return self.num_total - self.num_success
+ 205
+ 206 def pdf(self, x: np.ndarray) -> np.ndarray:
+ 207 """Compute the probability density function."""
+ 208 return sp.stats.beta.pdf(
+ 209 x,
+ 210 a=self.num_success + 1,
+ 211 b=self.num_fail + 1,
+ 212 loc=self.offset,
+ 213 scale=self.scale,
+ 214 )
+ 215
+ 216 def left_percentile(self, percent: float) -> float:
+ 217 """Return the point where the CDF reaches ``percent``."""
+ 218 return sp.stats.beta.ppf(
+ 219 percent / 100.0,
+ 220 a=self.num_success + 1,
+ 221 b=self.num_fail + 1,
+ 222 scale=self.scale,
+ 223 )
+ 224
+ 225 def right_percentile(self, percent: float) -> float:
+ 226 """Return the point where 100% minus the CDF equals ``percent``."""
+ 227 return sp.stats.beta.ppf(
+ 228 1.0 - (percent / 100.0),
+ 229 a=self.num_success + 1,
+ 230 b=self.num_fail + 1,
+ 231 scale=self.scale,
+ 232 )
+ 233
+ 234 def draw(self, axes: MPLAxes, resolution: int = 300, **defaults) -> Any:
+ 235 """Draw the Beta posterior into the provided ``axes``.
+ 236
+ 237 Returns a handle and a label for the legend.
+ 238 """
+ 239 left, right = axes.get_xlim()
+ 240 x = np.linspace(left, right, resolution)
+ 241 y = self.pdf(x)
+ 242
+ 243 plot_kwargs = defaults.get("plot", {}).copy()
+ 244 plot_kwargs.update(self.kwargs)
+ 245
+ 246 if self.label is not None:
+ 247 plot_kwargs["label"] = self.label
+ 248
+ 249 return axes.plot(x, y, **plot_kwargs)
+ 250
+ 251
+ 252def get_size(width="single", unit="cm", ratio="golden"):
+ 253 """Return a tuple of figure sizes in inches.
+ 254
+ 255 This is provided as the ``matplotlib`` keyword argument ``figsize`` expects it.
+ 256 This figure size is computed from a ``width``, in the ``unit`` of centimeters by
+ 257 default, and a ``ratio`` which is set to the golden ratio by default.
+ 258
+ 259 >>> get_size(width="single", ratio="golden")
+ 260 (3.937007874015748, 2.4332557935820445)
+ 261 >>> get_size(width="full", ratio=2.)
+ 262 (6.299212598425196, 3.149606299212598)
+ 263 >>> get_size(width=10., ratio=1.)
+ 264 (3.937007874015748, 3.937007874015748)
+ 265 >>> get_size(width=5, unit="inches", ratio=2./3.)
+ 266 (5, 7.5)
+ 267 """
+ 268 if width == "single":
+ 269 width = 10
+ 270 elif width == "full":
+ 271 width = 16
+ 272
+ 273 ratio = 1.618 if ratio == "golden" else ratio
+ 274 width = width / CM_PER_INCH if unit == "cm" else width
+ 275 height = width / ratio
+ 276 return (width, height)
+ 277
+ 278
+ 279def get_label(attrs: Mapping) -> str:
+ 280 """Extract label of a histogram from the HDF5 ``attrs`` object of the dataset."""
+ 281 label = []
+ 282 transforms = {
+ 283 "label": str,
+ 284 "modality": str,
+ 285 "t_stage": str,
+ 286 "midline_ext": lambda x: "ext" if x else "noext",
+ 287 }
+ 288 for key, func in transforms.items():
+ 289 if key in attrs and attrs[key] is not None:
+ 290 label.append(func(attrs[key]))
+ 291 return " | ".join(label)
+ 292
+ 293
+ 294def get_xlims(
+ 295 contents: AbstractDistributionT,
+ 296 percent_lims: tuple[float] = (10.0, 10.0),
+ 297) -> tuple[float]:
+ 298 """Get the x-axis limits for a plot containing multiple distribution.
+ 299
+ 300 Compute the ``xlims`` of a plot containing histograms and probability density
+ 301 functions by considering their smallest and largest percentiles.
+ 302 """
+ 303 left_percentiles = np.array(
+ 304 [c.left_percentile(percent_lims[0]) for c in contents],
+ 305 )
+ 306 left_lim = np.min(left_percentiles)
+ 307 right_percentiles = np.array(
+ 308 [c.right_percentile(percent_lims[0]) for c in contents],
+ 309 )
+ 310 right_lim = np.max(right_percentiles)
+ 311 return left_lim, right_lim
+ 312
+ 313
+ 314def draw(
+ 315 axes: MPLAxes,
+ 316 contents: list[AbstractDistribution],
+ 317 percent_lims: tuple[float, float] = (10.0, 10.0),
+ 318 xlims: tuple[float] | None = None,
+ 319 hist_kwargs: dict[str, Any] | None = None,
+ 320 plot_kwargs: dict[str, Any] | None = None,
+ 321) -> MPLAxes:
+ 322 """Draw histograms and Beta posterior from ``contents`` into ``axes``.
+ 323
+ 324 The limits of the x-axis is computed to be the smallest and largest left and right
+ 325 percentile of all provided ``contents`` respectively via the ``percent_lims`` tuple.
+ 326
+ 327 The ``hist_kwargs`` define general settings that will be applied to all histograms.
+ 328 One additional key ``'nbins'`` may be used to adjust only the numbers, not the
+ 329 spacing of the histogram bins.
+ 330 Similarly, ``plot_kwargs`` adjusts the default settings for the Beta posteriors.
+ 331
+ 332 Both these keyword arguments can be overwritten by what the individual ``contents``
+ 333 have defined.
+ 334 """
+ 335 if not all(isinstance(c, AbstractDistribution) for c in contents):
+ 336 raise TypeError("Contents must be subclasses of `AbstractDistribution`")
+ 337
+ 338 xlims = xlims or get_xlims(contents, percent_lims)
+ 339
+ 340 if len(xlims) != 2 or xlims[0] > xlims[-1]:
+ 341 raise ValueError("`xlims` must be tuple of two increasing values")
+ 342
+ 343 axes.set_xlim(*xlims)
+ 344
+ 345 default_kwargs = {
+ 346 "hist": {
+ 347 "density": True,
+ 348 "histtype": "stepfilled",
+ 349 "alpha": 0.7,
+ 350 "bins": 50,
+ 351 },
+ 352 "plot": {},
+ 353 }
+ 354 default_kwargs["hist"].update(hist_kwargs or {})
+ 355 default_kwargs["plot"].update(plot_kwargs or {})
+ 356
+ 357 for content in contents:
+ 358 content.draw(axes, **default_kwargs)
+ 359
+ 360 return axes
+ 361
+ 362
+ 363def split_legends(
+ 364 axes: MPLAxes,
+ 365 titles: list[str],
+ 366 locs: list[tuple[float, float]],
+ 367 **kwargs,
+ 368) -> None:
+ 369 """Separate labels in ``axes`` into separate legends with ``titles`` at ``locs``."""
+ 370 legend_kwargs = {
+ 371 "title_fontsize": "small",
+ 372 "labelspacing": 0.1,
+ 373 "loc": "upper left",
+ 374 }
+ 375 legend_kwargs.update(kwargs)
+ 376
+ 377 handles, labels = axes.get_legend_handles_labels()
+ 378 labels_per_legend = len(labels) // len(titles)
+ 379
+ 380 for i, (title, loc) in enumerate(zip(titles, locs, strict=True)):
+ 381 start = i * labels_per_legend
+ 382 stop = (i + 1) * labels_per_legend if i < len(titles) - 1 else None
+ 383 idx = slice(start, stop)
+ 384
+ 385 legend = axes.legend(
+ 386 handles[idx],
+ 387 labels[idx],
+ 388 bbox_to_anchor=loc,
+ 389 title=title,
+ 390 **legend_kwargs,
+ 391 )
+ 392 axes.add_artist(legend)
+ 393
+ 394
+ 395@log_state()
+ 396@check_input_file_exists
+ 397def use_mpl_stylesheet(file_path: str | Path):
+ 398 """Load a ``.mplstyle`` stylesheet from ``file_path``."""
+ 399 plt.style.use(file_path)
+ 400
+ 401
+ 402@log_state()
+ 403@check_output_dir_exists
+ 404def save_figure(
+ 405 output_path: str | Path,
+ 406 figure: Figure,
+ 407 formats: list[str] | None,
+ 408):
+ 409 """Save a ``figure`` to ``output_path`` in every one of the provided ``formats``."""
+ 410 for frmt in formats:
+ 411 figure.savefig(output_path.with_suffix(f".{frmt}"))
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_sample_py.html b/htmlcov/z_5bf5c588c698c6cc_sample_py.html
new file mode 100644
index 0000000..fc32eec
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_sample_py.html
@@ -0,0 +1,518 @@
+
+
+
+
+ Coverage for src/lyscripts/sample.py: 92%
+
+
+
+
+
+
+
+ 1"""Implementation of flexible MCMC sampling for lymphatic progression models.
+ 2
+ 3This module provides both helpful functions for programmatically building and running
+ 4sampling pipelines, as well a CLI interface for th most common sampling use cases.
+ 5
+ 6The core is the :py:func:`run_sampling` function. It has a flexible interface and
+ 7built-in convergence detection, as well as bookkeeping for monitoring and resuming
+ 8interrupted sampling runs. It can be used both during the burn-in phase and the actual
+ 9sampling phase.
+ 10
+ 11.. warning::
+ 12
+ 13 We strongly recommend to set the CLI's ``--cores`` argument to ``None`` (or ``null``
+ 14 in the YAML config file) if you are on MacOS or Windows. This is because we haven't
+ 15 yet figured out how we can safely and efficiently use the ``multiprocess(ing)``
+ 16 library on these two platforms.
+ 17"""
+ 18
+ 19from __future__ import annotations
+ 20
+ 21import os
+ 22import sys
+ 23from typing import Any
+ 24
+ 25from loguru import logger
+ 26
+ 27from lyscripts.cli import assemble_main
+ 28
+ 29try:
+ 30 import multiprocess as mp
+ 31except ModuleNotFoundError:
+ 32 import multiprocessing as mp
+ 33
+ 34if sys.platform == "darwin":
+ 35 logger.warning("Detected MacOS. Setting multiprocess(ing) start method to 'fork'.")
+ 36 mp.set_start_method("fork")
+ 37
+ 38from pathlib import Path
+ 39
+ 40import emcee
+ 41import numpy as np
+ 42import pandas as pd
+ 43from lydata.utils import ModalityConfig
+ 44from lymph.types import ParamsType
+ 45from pydantic import BaseModel, Field
+ 46from rich.progress import Progress, ProgressColumn, Task, TimeElapsedColumn
+ 47from rich.text import Text
+ 48
+ 49from lyscripts.configs import (
+ 50 BaseCLI,
+ 51 DataConfig,
+ 52 DistributionConfig,
+ 53 GraphConfig,
+ 54 ModelConfig,
+ 55 SamplingConfig,
+ 56 add_distributions,
+ 57 add_modalities,
+ 58 construct_model,
+ 59)
+ 60from lyscripts.utils import console, get_hdf5_backend
+ 61
+ 62
+ 63class CompletedItersColumn(ProgressColumn):
+ 64 """A column that displays the completed number of iterations."""
+ 65
+ 66 def __init__(self, table_column=None, it: int = 0):
+ 67 """Initialize the column with number of previous iterations."""
+ 68 super().__init__(table_column)
+ 69 self.it = it
+ 70
+ 71 def render(self, task: Task) -> Text:
+ 72 """Render total iterations."""
+ 73 if task.completed is None:
+ 74 return Text("? it", style="progress.data.steps")
+ 75 return Text(f"{task.completed + self.it} it", style="progress.data.steps")
+ 76
+ 77
+ 78class ItersPerSecondColumn(ProgressColumn):
+ 79 """A column that displays the number of iterations per second."""
+ 80
+ 81 def render(self, task: Task) -> Text:
+ 82 """Render iterations per second."""
+ 83 speed = task.finished_speed or task.speed
+ 84 if speed is None:
+ 85 return Text("? it/s", style="progress.data.speed")
+ 86 return Text(f"{speed:.2f} it/s", style="progress.data.speed")
+ 87
+ 88
+ 89class AcorTime(BaseModel, validate_assignment=True):
+ 90 """Storage for old and new autocorrelation times."""
+ 91
+ 92 old: float
+ 93 new: float
+ 94
+ 95 def update(self, new: float) -> None:
+ 96 """Update the autocorrelation time."""
+ 97 self.old = self.new
+ 98 self.new = new
+ 99
+ 100 @property
+ 101 def relative_diff(self) -> float:
+ 102 """Get the relative difference between new and old autocorrelation time."""
+ 103 return np.abs(self.new - self.old) / self.new
+ 104
+ 105
+ 106class NumAccepted(BaseModel, validate_assignment=True):
+ 107 """Storage for old and new number of accepted proposals."""
+ 108
+ 109 old: int
+ 110 new: int
+ 111
+ 112 def update(self, new: int) -> None:
+ 113 """Update the number of accepted proposals."""
+ 114 self.old = self.new
+ 115 self.new = new
+ 116
+ 117 @property
+ 118 def newly_accepted(self) -> int:
+ 119 """Get the number of newly accepted proposals."""
+ 120 return self.new - self.old
+ 121
+ 122
+ 123MODEL = None
+ 124
+ 125
+ 126def log_prob_fn(theta: ParamsType, inverse_temp: float = 1.0) -> tuple[float, float]:
+ 127 """Compute log-prob using global variables because of pickling.
+ 128
+ 129 An inverse temperature ``inverse_temp`` can be provided for thermodynamic
+ 130 integration.
+ 131 """
+ 132 return inverse_temp * MODEL.likelihood(given_params=theta), inverse_temp
+ 133
+ 134
+ 135def ensure_initial_state(sampler: emcee.EnsembleSampler) -> np.ndarray:
+ 136 """Try to extract a starting state from a ``sampler``.
+ 137
+ 138 Create a random starting state if no one was found.
+ 139 """
+ 140 try:
+ 141 state = sampler.backend.get_last_sample()
+ 142 logger.info(
+ 143 f"Resuming from {sampler.backend.filename} with {sampler.iteration} "
+ 144 "stored iterations.",
+ 145 )
+ 146 except AttributeError:
+ 147 state = np.random.uniform(size=(sampler.nwalkers, sampler.ndim)) # noqa: NPY002
+ 148 logger.debug(f"No stored samples found. Starting from random state {state}.")
+ 149
+ 150 return state
+ 151
+ 152
+ 153def ensure_history_table(file: Path | None) -> pd.DataFrame:
+ 154 """Return the history table from a file or an empty DataFrame.
+ 155
+ 156 It will try to load a history at the given ``file`` location, but with a ``.tmp``
+ 157 extension. This is the expected name and location of a history file that was
+ 158 stored during an interrupted sampling run.
+ 159
+ 160 If no file is found, an empty DataFrame is returned.
+ 161 """
+ 162 if file is None or not file.with_suffix(".tmp").exists():
+ 163 return pd.DataFrame(
+ 164 columns=[
+ 165 "steps",
+ 166 "acor_times",
+ 167 "accept_fracs",
+ 168 "max_log_probs",
+ 169 ],
+ 170 ).set_index("steps")
+ 171
+ 172 return pd.read_csv(file.with_suffix(".tmp"), index_col="steps")
+ 173
+ 174
+ 175def update_history_table(
+ 176 history: pd.DataFrame,
+ 177 history_file: Path | None,
+ 178 iteration: int,
+ 179 acor_time: float,
+ 180 accepted_frac: float,
+ 181 max_log_prob: float,
+ 182) -> pd.DataFrame:
+ 183 """Update the history table with the current iteration's information."""
+ 184 history.loc[iteration] = [acor_time, accepted_frac, max_log_prob]
+ 185 logger.debug(history.iloc[-1].to_dict())
+ 186
+ 187 if history_file is not None:
+ 188 history.to_csv(history_file.with_suffix(".tmp"))
+ 189
+ 190 return history
+ 191
+ 192
+ 193def is_converged(
+ 194 iteration: int,
+ 195 acor_time: AcorTime,
+ 196 trust_factor: float,
+ 197 relative_thresh: float,
+ 198) -> bool:
+ 199 """Check if the chain has converged based on the autocorrelation time.
+ 200
+ 201 The criterion is based on the relative change of the autocorrelation time and
+ 202 whether the autocorrelation extimate can be trusted. Essentially, we only trust
+ 203 the estimate if it is smaller than ``trust_factor`` times the current ``iteration``.
+ 204
+ 205 More details can be found in the `emcee documentation`_.
+ 206
+ 207 .. _emcee documentation: https://emcee.readthedocs.io/en/stable/tutorials/autocorr/
+ 208 """
+ 209 return (
+ 210 acor_time.new * trust_factor < iteration
+ 211 and acor_time.relative_diff < relative_thresh
+ 212 )
+ 213
+ 214
+ 215def _get_columns(it: int = 0) -> list[ProgressColumn]:
+ 216 """Get the default progress columns for the MCMC sampling."""
+ 217 return [
+ 218 *Progress.get_default_columns(),
+ 219 ItersPerSecondColumn(),
+ 220 CompletedItersColumn(it=it),
+ 221 TimeElapsedColumn(),
+ 222 ]
+ 223
+ 224
+ 225def run_sampling(
+ 226 sampler: emcee.EnsembleSampler,
+ 227 initial_state: np.ndarray | None = None,
+ 228 num_steps: int | None = None,
+ 229 thin_by: int = 1,
+ 230 check_interval: int = 100,
+ 231 trust_factor: float = 50.0,
+ 232 relative_thresh: float = 0.05,
+ 233 history_file: Path | None = None,
+ 234 reset_backend: bool = False,
+ 235 description: str = "Burn-in phase",
+ 236) -> None:
+ 237 """Run MCMC sampling.
+ 238
+ 239 This will run the ``sampler`` either for ``num_steps`` steps or - if it set to
+ 240 ``None`` - until convergence. Convergence is determined once within a
+ 241 ``check_interval`` of steps by the :py:func:`is_converged` function. The
+ 242 convergence criterion is based on a trustworthy estimate of the autocorrelation
+ 243 time. This is elaborated in the `emcee documentation`_.
+ 244
+ 245 Some bookkeeping parameters may be stored in a ``history_file``. During sampling,
+ 246 the history is stored in a temporary file with the suffix ``.tmp``. If the sampling
+ 247 is interrupted, the history and the last state of the ``sampler`` can be recovered
+ 248 and the sampling can be continued.
+ 249
+ 250 One may choose to ``reset_backend``, e.g. in case the previous sampling was run
+ 251 until convergence and now one wants to store a length of the converged chain. This
+ 252 may also be thinned by a factor of ``thin_by`` (directly passed to the
+ 253 :py:class:`emcee.EnsembleSampler` class).
+ 254
+ 255 .. _emcee documentation: https://emcee.readthedocs.io/en/stable/tutorials/autocorr/
+ 256 """
+ 257 state = initial_state or ensure_initial_state(sampler)
+ 258 history = ensure_history_table(history_file)
+ 259
+ 260 if reset_backend:
+ 261 logger.debug("Resetting backend of sampler.")
+ 262 sampler.backend.reset(sampler.nwalkers, sampler.ndim)
+ 263
+ 264 acor_time = AcorTime(old=np.inf, new=np.inf)
+ 265 accepted = NumAccepted(old=0, new=sampler.backend.accepted.sum())
+ 266
+ 267 with Progress(*_get_columns(it=sampler.iteration), console=console) as progress:
+ 268 task = progress.add_task(description=description, total=num_steps)
+ 269 while sampler.iteration < (num_steps or np.inf):
+ 270 for state in sampler.sample( # noqa: B007, B020
+ 271 initial_state=state,
+ 272 iterations=check_interval - sampler.iteration % check_interval,
+ 273 thin_by=thin_by,
+ 274 ):
+ 275 progress.update(task, advance=1)
+ 276
+ 277 acor_time.update(new=sampler.get_autocorr_time(tol=0).mean())
+ 278 accepted.update(new=sampler.backend.accepted.sum())
+ 279
+ 280 history = update_history_table(
+ 281 history=history,
+ 282 history_file=history_file,
+ 283 iteration=sampler.iteration,
+ 284 acor_time=acor_time.new,
+ 285 accepted_frac=(
+ 286 accepted.newly_accepted / (check_interval * sampler.nwalkers)
+ 287 ),
+ 288 max_log_prob=np.max(state.log_prob),
+ 289 )
+ 290
+ 291 if num_steps is None and is_converged(
+ 292 iteration=sampler.iteration,
+ 293 acor_time=acor_time,
+ 294 trust_factor=trust_factor,
+ 295 relative_thresh=relative_thresh,
+ 296 ):
+ 297 logger.info(f"Sampling converged after {sampler.iteration} steps.")
+ 298 break
+ 299
+ 300 if history_file is not None:
+ 301 history_file.with_suffix(".tmp").rename(history_file)
+ 302
+ 303
+ 304class DummyPool:
+ 305 """Dummy class to allow for no multiprocessing."""
+ 306
+ 307 def __enter__(self) -> None:
+ 308 """Enter the context manager."""
+ 309 ...
+ 310
+ 311 def __exit__(self, *args) -> None:
+ 312 """Exit the context manager."""
+ 313 ...
+ 314
+ 315
+ 316def get_pool(num_cores: int | None) -> Any | DummyPool: # type: ignore
+ 317 """Get a ``multiprocess(ing)`` pool or ``DummyPool``.
+ 318
+ 319 Returns a ``multiprocess(ing)`` pool with ``num_cores`` cores if ``num_cores`` is
+ 320 not ``None``. Otherwise, a ``DummyPool`` is returned.
+ 321 """
+ 322 return mp.Pool(num_cores) if num_cores is not None else DummyPool()
+ 323
+ 324
+ 325def init_sampler(settings: SampleCLI, ndim: int, pool: Any) -> emcee.EnsembleSampler:
+ 326 """Initialize the ``emcee.EnsembleSampler`` with the given ``settings``."""
+ 327 nwalkers = ndim * settings.sampling.walkers_per_dim
+ 328 backend = get_hdf5_backend(
+ 329 file_path=settings.sampling.storage_file,
+ 330 dataset=settings.sampling.dataset,
+ 331 nwalkers=nwalkers,
+ 332 ndim=ndim,
+ 333 )
+ 334 return emcee.EnsembleSampler(
+ 335 nwalkers=nwalkers,
+ 336 ndim=ndim,
+ 337 log_prob_fn=log_prob_fn,
+ 338 kwargs={"inverse_temp": settings.sampling.inverse_temp},
+ 339 moves=[(emcee.moves.DEMove(), 0.8), (emcee.moves.DESnookerMove(), 0.2)],
+ 340 backend=backend,
+ 341 pool=pool,
+ 342 blobs_dtype=[("inverse_temp", np.float64)],
+ 343 parameter_names=list(MODEL.get_named_params().keys()),
+ 344 )
+ 345
+ 346
+ 347class SampleCLI(BaseCLI):
+ 348 """Use MCMC to infer distributions over model parameters from data."""
+ 349
+ 350 graph: GraphConfig
+ 351 model: ModelConfig = ModelConfig()
+ 352 distributions: dict[str, DistributionConfig] = Field(
+ 353 default={},
+ 354 description=(
+ 355 "Mapping of model T-categories to predefined distributions over "
+ 356 "diagnose times."
+ 357 ),
+ 358 )
+ 359 modalities: dict[str, ModalityConfig] = Field(
+ 360 default={},
+ 361 description=(
+ 362 "Maps names of diagnostic modalities to their specificity/sensitivity."
+ 363 ),
+ 364 )
+ 365 data: DataConfig
+ 366 sampling: SamplingConfig
+ 367
+ 368 def cli_cmd(self) -> None:
+ 369 """Start the ``sample`` subcommand.
+ 370
+ 371 First, it will construct the model from the ``graph`` and ``model`` arguments.
+ 372 Then, it will add distributions over diagnose times via the dictionary from
+ 373 the ``distributions`` argument. It will also set sensitivity and specificity of
+ 374 diagnostic modalities via the dictionary provided through the ``modalities``
+ 375 argument. Finally, it will load the patient data as specified via the ``data``
+ 376 argument.
+ 377
+ 378 When the model is constructed, an :py:class:`emcee.EnsembleSampler` is
+ 379 initialized (see :py:func:`init_sampler`) and :py:func:`run_sampling` is
+ 380 executed twice: once for the burn-in phase and once for the actual sampling
+ 381 phase. The ``sampling`` argument provides all necessary settings for the
+ 382 sampling.
+ 383 """
+ 384 # as recommended in https://emcee.readthedocs.io/en/stable/tutorials/parallel/#
+ 385 os.environ["OMP_NUM_THREADS"] = "1"
+ 386
+ 387 logger.debug(self.model_dump_json(indent=2))
+ 388
+ 389 # ugly, but necessary for pickling
+ 390 global MODEL
+ 391 MODEL = construct_model(self.model, self.graph)
+ 392 MODEL = add_distributions(MODEL, self.distributions)
+ 393 MODEL = add_modalities(MODEL, self.modalities)
+ 394 MODEL.load_patient_data(**self.data.get_load_kwargs())
+ 395 ndim = MODEL.get_num_dims()
+ 396
+ 397 # emcee does not support numpy's new random number generator yet.
+ 398 np.random.seed(self.sampling.seed) # noqa: NPY002
+ 399
+ 400 with get_pool(self.sampling.cores) as pool:
+ 401 sampler = init_sampler(settings=self, ndim=ndim, pool=pool)
+ 402 run_sampling(
+ 403 description="Burn-in phase",
+ 404 sampler=sampler,
+ 405 check_interval=self.sampling.check_interval,
+ 406 trust_factor=self.sampling.trust_factor,
+ 407 relative_thresh=self.sampling.relative_thresh,
+ 408 history_file=self.sampling.history_file,
+ 409 )
+ 410 run_sampling(
+ 411 description="Sampling phase",
+ 412 sampler=sampler,
+ 413 num_steps=self.sampling.num_steps,
+ 414 reset_backend=True,
+ 415 thin_by=self.sampling.thin_by,
+ 416 )
+ 417
+ 418
+ 419if __name__ == "__main__":
+ 420 main = assemble_main(settings_cls=SampleCLI, prog_name="sample")
+ 421 main()
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_schedule_py.html b/htmlcov/z_5bf5c588c698c6cc_schedule_py.html
new file mode 100644
index 0000000..14f05ef
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_schedule_py.html
@@ -0,0 +1,182 @@
+
+
+
+
+ Coverage for src/lyscripts/schedule.py: 55%
+
+
+
+
+
+
+
+ 1r"""Generate inverse temperature schedules for thermodynamic integration.
+ 2
+ 3Thermodynamic integration is quite sensitive to the specific schedule which is used.
+ 4I noticed in my models, that within the interval :math:`[0, 0.1]`, the increase in the
+ 5expected log-likelihood is very steep. Hence, the inverse temperature :math:`\beta`
+ 6must be more densely spaced in the beginning.
+ 7
+ 8This can be achieved by using a power sequence: Generate :math:`n` linearly spaced
+ 9points in the interval :math:`[0, 1]` and then transform each point by computing
+ 10:math:`\beta_i^k` where :math:`k` could e.g. be 5.
+ 11"""
+ 12
+ 13from typing import Literal
+ 14
+ 15import numpy as np
+ 16from loguru import logger
+ 17from pydantic import Field
+ 18
+ 19from lyscripts.cli import assemble_main
+ 20from lyscripts.configs import BaseCLI
+ 21
+ 22
+ 23def geometric_schedule(num: int, *_a) -> np.ndarray:
+ 24 """Create a geometric sequence of ``num`` numbers from 0 to 1."""
+ 25 log_seq = np.logspace(0.0, 1.0, num)
+ 26 shifted_seq = log_seq - 1.0
+ 27 return shifted_seq / 9.0
+ 28
+ 29
+ 30def linear_schedule(num: int, *_a) -> np.ndarray:
+ 31 """Create a linear sequence of ``num`` numbers from 0 to 1.
+ 32
+ 33 Equivalent to the :py:func:`power_schedule` with ``power=1``.
+ 34 """
+ 35 return np.linspace(0.0, 1.0, num)
+ 36
+ 37
+ 38def power_schedule(num: int, power: float, *_a) -> np.ndarray:
+ 39 """Create a power sequence of ``num`` numbers from 0 to 1.
+ 40
+ 41 This is essentially a :py:func:`linear_schedule` of ``num`` numbers from 0 to 1,
+ 42 but each number is raised to the power of ``power``.
+ 43 """
+ 44 lin_seq = np.linspace(0.0, 1.0, num)
+ 45 return lin_seq**power
+ 46
+ 47
+ 48SCHEDULES = {
+ 49 "geometric": geometric_schedule,
+ 50 "linear": linear_schedule,
+ 51 "power": power_schedule,
+ 52}
+ 53
+ 54
+ 55class ScheduleCLI(BaseCLI):
+ 56 """Generate an inverse temperature schedule for thermodynamic integration."""
+ 57
+ 58 method: Literal["geometric", "linear", "power"] = Field(
+ 59 default="geometric",
+ 60 description="Choose the method to distribute the inverse temperatures.",
+ 61 )
+ 62 num: int = Field(
+ 63 default=32,
+ 64 description="Number of inverse temperatures in the schedule.",
+ 65 )
+ 66 power: float = Field(
+ 67 default=4,
+ 68 description="If a power schedule is chosen, use this as power.",
+ 69 )
+ 70
+ 71 def cli_cmd(self) -> None:
+ 72 """Start the ``schedule`` command."""
+ 73 logger.debug(self.model_dump_json(indent=2))
+ 74
+ 75 func = SCHEDULES[self.method]
+ 76 schedule = func(self.num, self.power)
+ 77
+ 78 for inv_temp in schedule:
+ 79 # print is necessary to allow piping the output
+ 80 print(inv_temp) # noqa: T201
+ 81
+ 82
+ 83if __name__ == "__main__":
+ 84 main = assemble_main(settings_cls=ScheduleCLI, prog_name="schedule")
+ 85 main()
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_schema_py.html b/htmlcov/z_5bf5c588c698c6cc_schema_py.html
new file mode 100644
index 0000000..315ac04
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_schema_py.html
@@ -0,0 +1,162 @@
+
+
+
+
+ Coverage for src/lyscripts/schema.py: 86%
+
+
+
+
+
+
+
+ 1"""A fusion of all :py:mod:`configs`, allowing the creation of a JSON schema.
+ 2
+ 3This command is not intended to be used by the end user. Rather, it exists such that
+ 4the developers and maintainers can create a JSON schema from all the defined
+ 5:py:mod:`configs` an store that in the `source code repository`_. Subsequently, the
+ 6end user can point their IDE to this schema, hosted on GitHub to provide them with
+ 7auto-completion and validation of their YAML configuration files that they feed into
+ 8the lyscripts CLIs when they build pipelines or scripts with it.
+ 9
+ 10The `URL for the schema`_ can for example be used in the settings of VS Code like this:
+ 11
+ 12.. code:: json
+ 13
+ 14 {
+ 15 "yaml.schemas": {
+ 16 "https://raw.githubusercontent.com/lycosystem/lyscripts/main/schemas/ly.json": "*.ly.yaml"
+ 17 },
+ 18 }
+ 19
+ 20Which would enable auto-completion and validation for all files with the extension
+ 21``.ly.yaml`` in the workspace.
+ 22
+ 23.. _source code repository: https://github.com/lycosystem/lyscripts
+ 24.. _URL for the schema: https://raw.githubusercontent.com/lycosystem/lyscripts/main/schemas/ly.json
+ 25""" # noqa: E501
+ 26
+ 27import json
+ 28
+ 29from lydata.utils import ModalityConfig
+ 30from pydantic import BaseModel, Field
+ 31
+ 32from lyscripts import configs
+ 33
+ 34
+ 35class SchemaSettings(BaseModel):
+ 36 """Settings for generating a JSON schema for lyscripts configuration files."""
+ 37
+ 38 version: int = Field(
+ 39 description=(
+ 40 "For future compatibility reasons, every config file must have a "
+ 41 "`version: 1` field at the top level."
+ 42 ),
+ 43 ge=1,
+ 44 le=1,
+ 45 )
+ 46 cross_validation: configs.CrossValidationConfig = None
+ 47 data: configs.DataConfig = None
+ 48 diagnosis: configs.DiagnosisConfig = None
+ 49 distributions: dict[str, configs.DistributionConfig] = {}
+ 50 graph: configs.GraphConfig = None
+ 51 involvement: configs.InvolvementConfig = None
+ 52 modalities: dict[str, ModalityConfig] = {}
+ 53 model: configs.ModelConfig = None
+ 54 sampling: configs.SamplingConfig = None
+ 55 scenarios: list[configs.ScenarioConfig] = []
+ 56
+ 57
+ 58def main() -> None:
+ 59 """Generate a JSON schema for lyscripts configuration files."""
+ 60 schema = SchemaSettings.model_json_schema()
+ 61 print(json.dumps(schema, indent=2)) # noqa: T201
+ 62
+ 63
+ 64if __name__ == "__main__":
+ 65 main()
+
+
+
+
diff --git a/htmlcov/z_5bf5c588c698c6cc_utils_py.html b/htmlcov/z_5bf5c588c698c6cc_utils_py.html
new file mode 100644
index 0000000..a95cac5
--- /dev/null
+++ b/htmlcov/z_5bf5c588c698c6cc_utils_py.html
@@ -0,0 +1,296 @@
+
+
+
+
+ Coverage for src/lyscripts/utils.py: 94%
+
+
+
+
+
+
+
+ 1"""General utility functions for the lyscripts package."""
+ 2
+ 3from pathlib import Path
+ 4
+ 5import numpy as np
+ 6import pandas as pd
+ 7import yaml
+ 8from emcee.backends import HDFBackend
+ 9from loguru import logger
+ 10from rich.console import Console
+ 11from scipy.special import factorial
+ 12
+ 13from lyscripts.decorators import (
+ 14 check_input_file_exists,
+ 15 check_output_dir_exists,
+ 16)
+ 17
+ 18console = Console()
+ 19
+ 20
+ 21def binom_pmf(support: list[int] | np.ndarray, p: float = 0.5):
+ 22 """Binomial PMF that is much faster than the one from scipy."""
+ 23 max_time = len(support) - 1
+ 24 if p > 1.0 or p < 0.0:
+ 25 raise ValueError("Binomial prob must be btw. 0 and 1")
+ 26 q = 1.0 - p
+ 27 binom_coeff = factorial(max_time) / (
+ 28 factorial(support) * factorial(max_time - support)
+ 29 )
+ 30 return binom_coeff * p**support * q ** (max_time - support)
+ 31
+ 32
+ 33def get_dict_depth(nested: dict) -> int:
+ 34 """Get the depth of a nested dictionary.
+ 35
+ 36 >>> get_dict_depth({"a": {"b": 1}})
+ 37 2
+ 38 >>> varying_depth = {"a": {"b": 1}, "c": {"d": {"e": 2}}}
+ 39 >>> get_dict_depth(varying_depth)
+ 40 3
+ 41 """
+ 42 if not isinstance(nested, dict):
+ 43 return 0
+ 44
+ 45 max_depth = None
+ 46 for _, value in nested.items():
+ 47 value_depth = get_dict_depth(value)
+ 48 max_depth = max(max_depth or value_depth, value_depth)
+ 49
+ 50 return 1 + (max_depth or 0)
+ 51
+ 52
+ 53def delete_private_keys(nested: dict) -> dict:
+ 54 """Delete private keys from a nested dictionary.
+ 55
+ 56 A 'private' key is a key whose name starts with an underscore. For example:
+ 57
+ 58 >>> delete_private_keys({"patient": {"__doc__": "some patient info", "age": 61}})
+ 59 {'patient': {'age': 61}}
+ 60 >>> delete_private_keys({"patient": {"age": 61}})
+ 61 {'patient': {'age': 61}}
+ 62 """
+ 63 cleaned = {}
+ 64
+ 65 if isinstance(nested, dict):
+ 66 for key, value in nested.items():
+ 67 if not (isinstance(key, str) and key.startswith("_")):
+ 68 cleaned[key] = delete_private_keys(value)
+ 69 else:
+ 70 cleaned = nested
+ 71
+ 72 return cleaned
+ 73
+ 74
+ 75def flatten(
+ 76 nested: dict,
+ 77 prev_key: tuple = (),
+ 78 max_depth: int | None = None,
+ 79) -> dict:
+ 80 """Flatten ``nested`` dict by creating key tuples for each value at ``max_depth``.
+ 81
+ 82 >>> nested = {"tumor": {"1": {"t_stage": 1, "size": 12.3}}}
+ 83 >>> flatten(nested)
+ 84 {('tumor', '1', 't_stage'): 1, ('tumor', '1', 'size'): 12.3}
+ 85 >>> mapping = {"patient": {"#": {"age": {"func": int, "columns": ["age"]}}}}
+ 86 >>> flatten(mapping, max_depth=3)
+ 87 {('patient', '#', 'age'): {'func': <class 'int'>, 'columns': ['age']}}
+ 88
+ 89 Note that flattening an already flat dictionary will yield some weird results.
+ 90 """
+ 91 result = {}
+ 92
+ 93 for key, value in nested.items():
+ 94 is_dict = isinstance(value, dict)
+ 95 has_reached_max_depth = max_depth is not None and len(prev_key) >= max_depth - 1
+ 96
+ 97 if is_dict and not has_reached_max_depth:
+ 98 result.update(flatten(value, (*prev_key, key), max_depth))
+ 99 else:
+ 100 result[(*prev_key, key)] = value
+ 101
+ 102 return result
+ 103
+ 104
+ 105def unflatten(flat: dict) -> dict:
+ 106 """Take a flat dictionary with tuples of keys and create nested dict from it.
+ 107
+ 108 >>> flat = {('tumor', '1', 't_stage'): 1, ('tumor', '1', 'size'): 12.3}
+ 109 >>> unflatten(flat)
+ 110 {'tumor': {'1': {'t_stage': 1, 'size': 12.3}}}
+ 111 >>> mapping = {('patient', '#', 'age'): {'func': int, 'columns': ['age']}}
+ 112 >>> unflatten(mapping)
+ 113 {'patient': {'#': {'age': {'func': <class 'int'>, 'columns': ['age']}}}}
+ 114 """
+ 115 result = {}
+ 116
+ 117 for keys, value in flat.items():
+ 118 current = result
+ 119 for key in keys[:-1]:
+ 120 current = current.setdefault(key, {})
+ 121
+ 122 current[keys[-1]] = value
+ 123
+ 124 return result
+ 125
+ 126
+ 127def get_modalities_subset(
+ 128 defined_modalities: dict[str, list[float]],
+ 129 selection: list[str],
+ 130) -> dict[str, list[float]]:
+ 131 """Of the ``defined_modalities`` return only those mentioned in the ``selection``.
+ 132
+ 133 >>> modalities = {"CT": [0.76, 0.81], "MRI": [0.63, 0.86]}
+ 134 >>> get_modalities_subset(modalities, ["CT"])
+ 135 {'CT': [0.76, 0.81]}
+ 136 """
+ 137 selected_modalities = {}
+ 138 for mod in selection:
+ 139 try:
+ 140 selected_modalities[mod] = defined_modalities[mod]
+ 141 except KeyError as key_err:
+ 142 raise KeyError(f"Modality {mod} has not been defined yet") from key_err
+ 143 return selected_modalities
+ 144
+ 145
+ 146def load_patient_data(
+ 147 file_path: Path,
+ 148 **read_csv_kwargs: dict,
+ 149) -> pd.DataFrame:
+ 150 """Load patient data from a CSV file stored at ``file``."""
+ 151 if "header" not in read_csv_kwargs:
+ 152 read_csv_kwargs["header"] = [0, 1, 2]
+ 153
+ 154 data = pd.read_csv(file_path, **read_csv_kwargs)
+ 155 logger.info(f"Loaded {len(data)} patient records from {file_path}")
+ 156 return data
+ 157
+ 158
+ 159@check_input_file_exists
+ 160def load_yaml_params(file_path: Path) -> dict:
+ 161 """Load parameters from a YAML ``file``."""
+ 162 with open(file_path, encoding="utf-8") as file:
+ 163 loaded_params = yaml.safe_load(file)
+ 164 logger.info(f"Loaded YAML parameters from {file_path}")
+ 165 return loaded_params
+ 166
+ 167
+ 168@check_input_file_exists
+ 169def load_model_samples(
+ 170 file_path: Path,
+ 171 name: str = "mcmc",
+ 172 flat: bool = True,
+ 173 discard: int = 0,
+ 174 thin: int = 1,
+ 175) -> np.ndarray:
+ 176 """Load MCMC samples stored in HDF5 file at ``file_path`` under a key ``name``."""
+ 177 backend = HDFBackend(file_path, name=name, read_only=True)
+ 178 samples = backend.get_chain(flat=flat, discard=discard, thin=thin)
+ 179 logger.info(f"Loaded samples with shape {samples.shape} from {file_path}")
+ 180 return samples
+ 181
+ 182
+ 183@check_output_dir_exists
+ 184def get_hdf5_backend(
+ 185 file_path: Path,
+ 186 dataset: str = "mcmc",
+ 187 nwalkers: int | None = None,
+ 188 ndim: int | None = None,
+ 189 reset: bool = False,
+ 190) -> HDFBackend:
+ 191 """Open an HDF5 file at ``file_path`` and return a backend."""
+ 192 backend = HDFBackend(file_path, name=dataset)
+ 193 logger.info(f"Opened HDF5 file at {file_path}")
+ 194
+ 195 if reset:
+ 196 logger.info(f"Resetting backend at {file_path} to {nwalkers=} and {ndim=}")
+ 197 backend.reset(nwalkers, ndim)
+ 198
+ 199 return backend
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a___init___py.html b/htmlcov/z_9b7bcb970ba14d6a___init___py.html
new file mode 100644
index 0000000..a2eebe1
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a___init___py.html
@@ -0,0 +1,140 @@
+
+
+
+
+ Coverage for src/lyscripts/data/__init__.py: 92%
+
+
+
+
+
+
+
+ 1"""Commands and functions for managing CSV data on patterns of lymphatic progression.
+ 2
+ 3This contains helpful CLI commands that allow building quick and reproducible workflows
+ 4even when using language-agnostic tools like `Make`_ or `DVC`_.
+ 5
+ 6Most of these commands can load `LyProX`_ style data from CSV files, but also from
+ 7the installed datasets provided by the `lydata`_ package and directly from the
+ 8associated `GitHub repository`_.
+ 9
+ 10.. _Make: https://www.gnu.org/software/make/
+ 11.. _DVC: https://dvc.org
+ 12.. _LyProX: https://lyprox.org
+ 13.. _lydata: https://lydata.readthedocs.io
+ 14.. _GitHub repository: https://github.com/rmnldwg/lydata
+ 15"""
+ 16
+ 17from pydantic_settings import BaseSettings, CliApp, CliSubCommand
+ 18
+ 19from lyscripts.data import ( # noqa: F401
+ 20 enhance,
+ 21 generate,
+ 22 join,
+ 23 lyproxify,
+ 24 split,
+ 25)
+ 26
+ 27# Avoid conflict with built-in `filter` function
+ 28from lyscripts.data import filter as filter_
+ 29
+ 30
+ 31class DataCLI(BaseSettings):
+ 32 """Work with lymphatic progression data through this CLI."""
+ 33
+ 34 lyproxify: CliSubCommand[lyproxify.LyproxifyCLI]
+ 35 join: CliSubCommand[join.JoinCLI]
+ 36 split: CliSubCommand[split.SplitCLI]
+ 37 filter: CliSubCommand[filter_.FilterCLI]
+ 38 enhance: CliSubCommand[enhance.EnhanceCLI]
+ 39 generate: CliSubCommand[generate.GenerateCLI]
+ 40
+ 41 def cli_cmd(self) -> None:
+ 42 """Run one of the ``data`` subcommands."""
+ 43 CliApp.run_subcommand(self)
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a___main___py.html b/htmlcov/z_9b7bcb970ba14d6a___main___py.html
new file mode 100644
index 0000000..968898f
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a___main___py.html
@@ -0,0 +1,133 @@
+
+
+
+
+ Coverage for src/lyscripts/data/__main__.py: 0%
+
+
+
+
+
+
+
+ 1"""Run the data module as a script."""
+ 2
+ 3import argparse
+ 4
+ 5from lyscripts import exit_cli
+ 6from lyscripts.cli import RichDefaultHelpFormatter
+ 7from lyscripts.data import enhance, generate, join, split
+ 8
+ 9# Avoid conflict with built-in `filter` function
+ 10from lyscripts.data import filter as filter_
+ 11
+ 12
+ 13def main(args: argparse.Namespace):
+ 14 """Run the main script."""
+ 15 parser = argparse.ArgumentParser(
+ 16 prog="lyscripts data",
+ 17 description=__doc__,
+ 18 formatter_class=RichDefaultHelpFormatter,
+ 19 )
+ 20 parser.set_defaults(run_main=exit_cli)
+ 21 subparsers = parser.add_subparsers()
+ 22
+ 23 # the individual scripts add `ArgumentParser` instances and their arguments to
+ 24 # this `subparsers` object
+ 25 enhance._add_parser(subparsers, help_formatter=parser.formatter_class)
+ 26 generate._add_parser(subparsers, help_formatter=parser.formatter_class)
+ 27 join._add_parser(subparsers, help_formatter=parser.formatter_class)
+ 28 split._add_parser(subparsers, help_formatter=parser.formatter_class)
+ 29 filter_._add_parser(subparsers, help_formatter=parser.formatter_class)
+ 30
+ 31 args = parser.parse_args()
+ 32 args.run_main(args, parser)
+ 33
+ 34
+ 35if __name__ == "__main__":
+ 36 main()
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a_enhance_py.html b/htmlcov/z_9b7bcb970ba14d6a_enhance_py.html
new file mode 100644
index 0000000..2f99823
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a_enhance_py.html
@@ -0,0 +1,160 @@
+
+
+
+
+ Coverage for src/lyscripts/data/enhance.py: 67%
+
+
+
+
+
+
+
+ 1"""Enhance the dataset by inferring additional columns from the data.
+ 2
+ 3This is a command-line interface to the
+ 4:py:func:`~lydata.utils.infer_and_combine_levels` function.
+ 5"""
+ 6
+ 7from typing import Literal
+ 8
+ 9from loguru import logger
+ 10from lydata import infer_and_combine_levels
+ 11from lydata.utils import ModalityConfig
+ 12
+ 13from lyscripts.cli import assemble_main
+ 14from lyscripts.configs import BaseCLI, DataConfig
+ 15from lyscripts.data.utils import save_table_to_csv
+ 16
+ 17
+ 18class EnhanceCLI(BaseCLI):
+ 19 """Enhance the dataset by inferring additional columns from the data."""
+ 20
+ 21 input: DataConfig
+ 22 modalities: dict[str, ModalityConfig] | None = None
+ 23 method: Literal["max_llh", "rank"] = "max_llh"
+ 24 sides: list[Literal["ipsi", "contra"]] = ["ipsi", "contra"]
+ 25 lnl_subdivisions: dict[str, list[str]] = {
+ 26 "I": ["a", "b"],
+ 27 "II": ["a", "b"],
+ 28 "V": ["a", "b"],
+ 29 }
+ 30 output_file: str
+ 31
+ 32 def cli_cmd(self) -> None:
+ 33 """Infer additional columns from the data and save the enhanced dataset.
+ 34
+ 35 This basically provides a CLI to the
+ 36 :py:func:`~lydata.utils.infer_and_combine_levels` function. See its docs for
+ 37 more details on what exactly is happening here.
+ 38 """
+ 39 logger.debug(self.model_dump_json(indent=2))
+ 40
+ 41 data = self.input.load()
+ 42 modality_names = list(self.modalities.keys()) if self.modalities else None
+ 43
+ 44 infer_lvls_kwargs = {
+ 45 "modalities": modality_names,
+ 46 "sides": self.sides,
+ 47 "subdivisions": self.lnl_subdivisions,
+ 48 }
+ 49 enhanced = infer_and_combine_levels(
+ 50 dataset=data,
+ 51 infer_superlevels_kwargs=infer_lvls_kwargs,
+ 52 infer_sublevels_kwargs=infer_lvls_kwargs,
+ 53 combine_kwargs={
+ 54 "modalities": self.modalities,
+ 55 "method": self.method,
+ 56 },
+ 57 )
+ 58 save_table_to_csv(file_path=self.output_file, table=enhanced)
+ 59
+ 60
+ 61if __name__ == "__main__":
+ 62 main = assemble_main(settings_cls=EnhanceCLI, prog_name="enhance")
+ 63 main()
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a_filter_py.html b/htmlcov/z_9b7bcb970ba14d6a_filter_py.html
new file mode 100644
index 0000000..b6da490
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a_filter_py.html
@@ -0,0 +1,196 @@
+
+
+
+
+ Coverage for src/lyscripts/data/filter.py: 39%
+
+
+
+
+
+
+
+ 1"""Filter a dataset according to some common criteria.
+ 2
+ 3This is essentially a command line interface to building a
+ 4:py:class:`query object <lydata.accessor.Q>` and applying it to the dataset.
+ 5"""
+ 6
+ 7from pathlib import Path
+ 8from typing import Literal
+ 9
+ 10from loguru import logger
+ 11from lydata import Q
+ 12from pydantic import Field
+ 13from pydantic_settings import CliImplicitFlag
+ 14
+ 15from lyscripts.cli import assemble_main
+ 16from lyscripts.configs import BaseCLI, DataConfig
+ 17from lyscripts.data.utils import save_table_to_csv
+ 18
+ 19
+ 20class FilterCLI(BaseCLI):
+ 21 """In- or exclude patients where a certain column fulfills a certain condition."""
+ 22
+ 23 input: DataConfig
+ 24 include: CliImplicitFlag[bool] = Field(
+ 25 False,
+ 26 description="Include patients where the condition is met (default: exclude).",
+ 27 )
+ 28 column: list[str] | str = Field(
+ 29 description=(
+ 30 "The column to filter by. May be a tuple of three strings, since data "
+ 31 "has a three-level header. If it is only one string, the lydata package "
+ 32 "tries to map that to a three-level header."
+ 33 ),
+ 34 )
+ 35 operator: Literal["==", "!=", ">", "<", ">=", "<=", "in", "contains"] = Field(
+ 36 description="The operator to use for comparison.",
+ 37 )
+ 38 value: float | int | str = Field(description="The value to compare against.")
+ 39 output_file: Path = Field(description="The path to save the filtered dataset to.")
+ 40
+ 41 def model_post_init(self, __context):
+ 42 """Cast to ``float``, if not possible ``int``, if not possible ``str``."""
+ 43 if isinstance(self.column, list):
+ 44 if len(self.column) == 1:
+ 45 self.column = self.column[0]
+ 46 elif len(self.column) == 3:
+ 47 self.column = tuple(self.column)
+ 48 else:
+ 49 raise ValueError(
+ 50 "The column attribute must be an iterable of three strings or a "
+ 51 f"single string, but it is {self.column}.",
+ 52 )
+ 53
+ 54 try:
+ 55 self.value = float(self.value)
+ 56 return super().model_post_init(__context)
+ 57 except ValueError:
+ 58 pass
+ 59
+ 60 try:
+ 61 self.value = int(self.value)
+ 62 return super().model_post_init(__context)
+ 63 except ValueError:
+ 64 pass
+ 65
+ 66 return super().model_post_init(__context)
+ 67
+ 68 def cli_cmd(self):
+ 69 """Execute the ``filter`` command.
+ 70
+ 71 This command uses the :py:class:`~lydata.accessor.Q` objects of the `lydata`_
+ 72 library to filter the dataset according to the given criteria.
+ 73
+ 74 .. _lydata: https://lydata.readthedocs.io
+ 75 """
+ 76 logger.debug(self.model_dump_json(indent=2))
+ 77
+ 78 data = self.input.load()
+ 79 query = Q(
+ 80 column=self.column,
+ 81 operator=self.operator,
+ 82 value=self.value,
+ 83 )
+ 84 logger.debug(f"Created query object: {query}")
+ 85 mask = query.execute(data)
+ 86
+ 87 if self.include:
+ 88 filtered = data[mask]
+ 89 logger.info(f"Keeping {sum(mask)} of {len(data)} patients.")
+ 90 else:
+ 91 filtered = data[~mask]
+ 92 logger.info(f"Excluding {sum(mask)} of {len(data)} patients.")
+ 93
+ 94 save_table_to_csv(file_path=self.output_file, table=filtered)
+ 95
+ 96
+ 97if __name__ == "__main__":
+ 98 main = assemble_main(settings_cls=FilterCLI, prog_name="filter")
+ 99 main()
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a_generate_py.html b/htmlcov/z_9b7bcb970ba14d6a_generate_py.html
new file mode 100644
index 0000000..6d3d59b
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a_generate_py.html
@@ -0,0 +1,193 @@
+
+
+
+
+ Coverage for src/lyscripts/data/generate.py: 90%
+
+
+
+
+
+
+
+ 1"""Script to generate a synthetic dataset.
+ 2
+ 3The generation is done by the :py:meth:`~lymph.models.Unilateral.draw_patients` method
+ 4of
+ 5the `lymph`_ package, which is why this requires the specification of a model
+ 6via the :py:class:`~lyscripts.configs.ModelConfig` class.
+ 7
+ 8.. _lymph: https://lymph-model.readthedocs.io/
+ 9"""
+ 10
+ 11import numpy as np
+ 12from loguru import logger
+ 13from lydata.utils import ModalityConfig
+ 14from pydantic import Field
+ 15
+ 16from lyscripts.cli import assemble_main
+ 17from lyscripts.configs import (
+ 18 BaseCLI,
+ 19 DistributionConfig,
+ 20 GraphConfig,
+ 21 ModelConfig,
+ 22 add_distributions,
+ 23 add_modalities,
+ 24 construct_model,
+ 25)
+ 26from lyscripts.data.utils import save_table_to_csv
+ 27
+ 28
+ 29class GenerateCLI(BaseCLI):
+ 30 """Settings for the command-line interface."""
+ 31
+ 32 graph: GraphConfig
+ 33 model: ModelConfig = ModelConfig()
+ 34 distributions: dict[str, DistributionConfig] = Field(
+ 35 default={},
+ 36 description=(
+ 37 "Mapping of model T-categories to predefined distributions over "
+ 38 "diagnose times."
+ 39 ),
+ 40 )
+ 41 t_stages_dist: dict[str, float] = Field(
+ 42 description=(
+ 43 "Specify what fraction of generated patients should come from the "
+ 44 "respective T-Stage."
+ 45 ),
+ 46 )
+ 47 modalities: dict[str, ModalityConfig]
+ 48 params: dict[str, float]
+ 49 num_patients: int = 200
+ 50 output_file: str
+ 51 seed: int = 42
+ 52
+ 53 def model_post_init(self, __context) -> None:
+ 54 """Make sure distribution over T-stages is normalized."""
+ 55 total = 0.0
+ 56 for t_stage in self.distributions:
+ 57 if t_stage not in self.t_stages_dist:
+ 58 raise ValueError(f"Missing distribution for T-stage {t_stage}.")
+ 59
+ 60 total += self.t_stages_dist[t_stage]
+ 61
+ 62 if not np.isclose(total, 1.0):
+ 63 raise ValueError("Sum of T-stage distributions must be 1.")
+ 64
+ 65 return super().model_post_init(__context)
+ 66
+ 67 def cli_cmd(self) -> None:
+ 68 """Run the ``generate`` command.
+ 69
+ 70 Here, the command constructs a model from the settings provided via the
+ 71 arguments. It then generates a synthetic dataset using the
+ 72 :py:meth:`~lymph.models.Unilateral.draw_patients` from the `lymph`_ package.
+ 73
+ 74 .. _lymph: https://lymph-model.readthedocs.io/
+ 75 """
+ 76 logger.debug(self.model_dump_json(indent=2))
+ 77
+ 78 model = construct_model(self.model, self.graph)
+ 79 model = add_distributions(model, self.distributions)
+ 80 model = add_modalities(model, self.modalities)
+ 81 model.set_params(**self.params)
+ 82 logger.info(f"Set parameters: {model.get_params(as_dict=True)}")
+ 83
+ 84 synth_data = model.draw_patients(
+ 85 num=self.num_patients,
+ 86 stage_dist=list(self.t_stages_dist.values()),
+ 87 seed=self.seed,
+ 88 )
+ 89 logger.info(f"Generated synthetic data with shape {synth_data.shape}")
+ 90
+ 91 save_table_to_csv(file_path=self.output_file, table=synth_data)
+ 92
+ 93
+ 94if __name__ == "__main__":
+ 95 main = assemble_main(settings_cls=GenerateCLI, prog_name="data generate")
+ 96 main()
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a_join_py.html b/htmlcov/z_9b7bcb970ba14d6a_join_py.html
new file mode 100644
index 0000000..61609b3
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a_join_py.html
@@ -0,0 +1,174 @@
+
+
+
+
+ Coverage for src/lyscripts/data/join.py: 55%
+
+
+
+
+
+
+
+ 1"""Join multiple lymphatic progression datasets into a single dataset."""
+ 2
+ 3from pathlib import Path
+ 4
+ 5import pandas as pd
+ 6from pydantic import Field
+ 7
+ 8from lyscripts.cli import assemble_main
+ 9from lyscripts.configs import BaseCLI, DataConfig
+ 10from lyscripts.data.utils import save_table_to_csv
+ 11
+ 12
+ 13class JoinCLI(BaseCLI):
+ 14 """Join multiple lymphatic progression datasets into a single dataset."""
+ 15
+ 16 inputs: list[DataConfig] = Field(description="The datasets to join.")
+ 17 output_file: Path = Field(description="The path to the output dataset.")
+ 18
+ 19 def cli_cmd(self) -> None:
+ 20 r"""Start the ``join`` subcommand.
+ 21
+ 22 This will load all datasets specified in the ``inputs`` attribute and
+ 23 concatenate them into a single dataset.
+ 24
+ 25 Unfortunately, the use of `pydantic`_ does make this particular command a
+ 26 little bit more complicated (but also more powerful): If one simply wants to
+ 27 concatenate multiple datasets on disk, the ``inputs`` should be provided like
+ 28 this:
+ 29
+ 30 .. code-block:: bash
+ 31
+ 32 lydata join \
+ 33 --inputs='["data.source": "file1.csv", "data.source": "file2.csv"]' \
+ 34 --output="joined.csv"
+ 35
+ 36 But it also allows for concatenating datasets fetched directly from the
+ 37 `lydata Github repo`_. Due to the rather complex command signature, we
+ 38 recommend defining what to concatenate using a YAML file:
+ 39
+ 40 .. code-block:: yaml
+ 41
+ 42 inputs:
+ 43 - data.year: 2021
+ 44 data.institution: "usz"
+ 45 data.subsite: "oropharynx"
+ 46 - data.year: 2021
+ 47 data.institution: "clb"
+ 48 data.subsite: "oropharynx"
+ 49
+ 50 Then, the command will look like this:
+ 51
+ 52 .. code-block:: bash
+ 53
+ 54 lydata join --configs=datasets.ly.yaml --output=joined.csv
+ 55
+ 56 .. _pydantic: https://docs.pydantic.dev/latest/
+ 57 .. _lydata Github repo: https://github.com/rmnldwg/lydata
+ 58 """
+ 59 joined = None
+ 60
+ 61 for data_config in self.inputs:
+ 62 data = data_config.load()
+ 63 if joined is None:
+ 64 joined = data
+ 65 else:
+ 66 joined = pd.concat(
+ 67 [joined, data],
+ 68 axis="index",
+ 69 ignore_index=True,
+ 70 )
+ 71
+ 72 save_table_to_csv(file_path=self.output_file, table=joined)
+ 73
+ 74
+ 75if __name__ == "__main__":
+ 76 main = assemble_main(settings_cls=JoinCLI, prog_name="join")
+ 77 main()
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a_lyproxify_py.html b/htmlcov/z_9b7bcb970ba14d6a_lyproxify_py.html
new file mode 100644
index 0000000..8f5078d
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a_lyproxify_py.html
@@ -0,0 +1,436 @@
+
+
+
+
+ Coverage for src/lyscripts/data/lyproxify.py: 45%
+
+
+
+
+
+
+
+ 1"""Consumes raw data and transforms it into a CSV that `LyProX`_ understands.
+ 2
+ 3To do so, it needs a dictionary that defines a mapping from raw columns to the LyProX
+ 4style data format. See the documentation of the :py:func:`.transform_to_lyprox` function
+ 5for more information.
+ 6
+ 7.. _LyProX: https://lyprox.org
+ 8"""
+ 9
+ 10import importlib.util
+ 11import warnings
+ 12from pathlib import Path
+ 13from typing import Annotated, Any
+ 14
+ 15import pandas as pd
+ 16from loguru import logger
+ 17from pydantic import AfterValidator, Field, FilePath
+ 18
+ 19from lyscripts.cli import assemble_main
+ 20from lyscripts.configs import BaseCLI
+ 21from lyscripts.data.utils import save_table_to_csv
+ 22from lyscripts.utils import delete_private_keys, flatten, load_patient_data
+ 23
+ 24warnings.simplefilter(action="ignore", category=FutureWarning)
+ 25
+ 26
+ 27def ensure_python_file(file: Path) -> Path:
+ 28 """Check if the file is a Python file."""
+ 29 if file.suffix != ".py":
+ 30 raise ValueError("Mapping file must be a Python file.")
+ 31
+ 32 return file
+ 33
+ 34
+ 35def ensure_column_map(file: Path) -> Path:
+ 36 """Ensure the Python file contains a ``COLUMN_MAP`` dictionary."""
+ 37 spec = importlib.util.spec_from_file_location("map_module", file)
+ 38 mapping = importlib.util.module_from_spec(spec)
+ 39 spec.loader.exec_module(mapping)
+ 40
+ 41 if not hasattr(mapping, "COLUMN_MAP"):
+ 42 raise ValueError("Mapping file must contain a `COLUMN_MAP` dictionary.")
+ 43
+ 44 return file
+ 45
+ 46
+ 47class LyproxifyCLI(BaseCLI):
+ 48 """Map any CSV file to the LyProX format with the help of a Python mapping dict."""
+ 49
+ 50 input_file: FilePath = Field(description="Location of raw CSV data.")
+ 51 num_header_rows: int = Field(
+ 52 default=1,
+ 53 description="Number of rows comprising the header of the raw CSV file.",
+ 54 )
+ 55 mapping_file: Annotated[
+ 56 FilePath,
+ 57 AfterValidator(ensure_python_file),
+ 58 AfterValidator(ensure_column_map),
+ 59 ] = Field(
+ 60 description=(
+ 61 "Location of Python file containing a `COLUMN_MAP` dictionary. It may also "
+ 62 "contain an `EXCLUDE` list of tuples `(column, check)` to exclude patients."
+ 63 ),
+ 64 )
+ 65 drop_rows: list[int] = Field(
+ 66 default=[],
+ 67 description=(
+ 68 "Delete rows of specified indices. Counting of rows start at 0 _after_ "
+ 69 "the `header-rows`."
+ 70 ),
+ 71 )
+ 72 drop_cols: list[int] = Field(
+ 73 default=[],
+ 74 description="Delete columns of specified indices.",
+ 75 )
+ 76 output_file: Path = Field(description="Location to store the lyproxified CSV file.")
+ 77
+ 78 def cli_cmd(self) -> None:
+ 79 """Start the ``lyproxify`` subcommand.
+ 80
+ 81 After reading in the specified file, it will first ``drop_rows`` and
+ 82 ``drop_cols``, as specified in the command line arguments. Then, it will
+ 83 call :py:func:`.exclude_patients` which will further remove patients based
+ 84 on the ``EXCLUDE`` object in the ``mapping_file``. Finally, it will call
+ 85 :py:func:`.transform_to_lyprox` to transform the data into the LyProX format
+ 86 given the ``COLUMN_MAP`` object in the ``mapping_file``.
+ 87 """
+ 88 logger.debug(self.model_dump_json(indent=2))
+ 89
+ 90 raw = load_patient_data(
+ 91 file_path=self.input_file,
+ 92 header=list(range(self.num_header_rows)),
+ 93 )
+ 94 raw = clean_header(
+ 95 table=raw,
+ 96 num_cols=raw.shape[1],
+ 97 num_header_rows=self.num_header_rows,
+ 98 )
+ 99
+ 100 cols_to_drop = raw.columns[self.drop_cols]
+ 101 trimmed = raw.drop(cols_to_drop, axis="columns")
+ 102 trimmed = trimmed.drop(index=self.drop_rows)
+ 103 trimmed = trimmed.dropna(axis="index", how="all")
+ 104 logger.info(f"Dropped rows {self.drop_rows} and columns {cols_to_drop}.")
+ 105
+ 106 spec = importlib.util.spec_from_file_location("map_module", self.mapping_file)
+ 107 mapping = importlib.util.module_from_spec(spec)
+ 108 spec.loader.exec_module(mapping)
+ 109 logger.info(f"Imported mapping instructions from {self.mapping_file}")
+ 110
+ 111 reduced = exclude_patients(trimmed, mapping.EXCLUDE)
+ 112 processed = transform_to_lyprox(reduced, mapping.COLUMN_MAP)
+ 113
+ 114 if ("tumor", "1", "side") in processed.columns:
+ 115 processed = leftright_to_ipsicontra(processed)
+ 116
+ 117 save_table_to_csv(file_path=self.output_file, table=processed)
+ 118
+ 119
+ 120class ParsingError(Exception):
+ 121 """Error while parsing the CSV file."""
+ 122
+ 123
+ 124def clean_header(
+ 125 table: pd.DataFrame,
+ 126 num_cols: int,
+ 127 num_header_rows: int,
+ 128) -> pd.DataFrame:
+ 129 """Rename the header cells in the ``table``."""
+ 130 table = table.copy()
+ 131
+ 132 for col in range(num_cols):
+ 133 for row in range(num_header_rows):
+ 134 table.rename(
+ 135 columns={f"Unnamed: {col}_level_{row}": f"{col}_lvl_{row}"},
+ 136 inplace=True,
+ 137 )
+ 138
+ 139 logger.debug("Cleaned headers of the raw data.")
+ 140 return table
+ 141
+ 142
+ 143def get_instruction_depth(nested_column_map: dict[tuple, dict[str, Any]]) -> int:
+ 144 """Get the depth at which the column mapping instructions are nested.
+ 145
+ 146 Instructions are a dictionary that contains either a 'func' or 'default' key.
+ 147
+ 148 >>> nested_column_map = {"patient": {"age": {"func": int}}}
+ 149 >>> get_instruction_depth(nested_column_map)
+ 150 2
+ 151 >>> flat_column_map = flatten(nested_column_map, max_depth=2)
+ 152 >>> get_instruction_depth(flat_column_map)
+ 153 1
+ 154 >>> nested_column_map = {"patient": {"__doc__": "some patient info", "age": 61}}
+ 155 >>> get_instruction_depth(nested_column_map)
+ 156 Traceback (most recent call last):
+ 157 ...
+ 158 ValueError: Leaf of column map must be a dictionary with 'func' or 'default' key.
+ 159 """
+ 160 for _, value in nested_column_map.items():
+ 161 if isinstance(value, dict):
+ 162 if "func" in value or "default" in value:
+ 163 return 1
+ 164
+ 165 return 1 + get_instruction_depth(value)
+ 166
+ 167 raise ValueError(
+ 168 "Leaf of column map must be a dictionary with 'func' or 'default' key.",
+ 169 )
+ 170
+ 171 raise ValueError("Empty column map.")
+ 172
+ 173
+ 174def generate_markdown_docs(
+ 175 nested_column_map: dict[tuple, dict[str, Any]],
+ 176 depth: int = 0,
+ 177 indent_len: int = 4,
+ 178) -> str:
+ 179 r"""Generate a markdown nested, ordered list as documentation for the column map.
+ 180
+ 181 A key in the doctionary is supposed to be documented, when its value is a dictionary
+ 182 containing a ``"__doc__"`` key.
+ 183
+ 184 >>> nested_column_map = {
+ 185 ... "patient": {
+ 186 ... "__doc__": "some patient info",
+ 187 ... "age": {
+ 188 ... "__doc__": "age of the patient",
+ 189 ... "func": int,
+ 190 ... "columns": ["age"],
+ 191 ... },
+ 192 ... },
+ 193 ... }
+ 194 >>> generate_markdown_docs(nested_column_map)
+ 195 '1. **`patient:`** some patient info\n 1. **`age:`** age of the patient\n'
+ 196 """
+ 197 md_docs = ""
+ 198 indent = " " * indent_len * depth
+ 199 i = 1
+ 200 for key, value in nested_column_map.items():
+ 201 if isinstance(value, dict):
+ 202 if "__doc__" in value:
+ 203 md_docs += f"{indent}{i}. **`{key}:`** {value['__doc__']}\n"
+ 204 i += 1
+ 205
+ 206 md_docs += generate_markdown_docs(value, depth + 1, indent_len)
+ 207
+ 208 return md_docs
+ 209
+ 210
+ 211def transform_to_lyprox(
+ 212 raw: pd.DataFrame,
+ 213 column_map: dict[tuple, dict[str, Any]],
+ 214) -> pd.DataFrame:
+ 215 """Transform ``raw`` data into table that can be uploaded directly to `LyProX`_.
+ 216
+ 217 To do so, it uses instructions in the `colum_map` dictionary, that needs to have
+ 218 a particular structure:
+ 219
+ 220 For each column in the final 'lyproxified' `pd.DataFrame`, one entry must exist in
+ 221 the `column_map` dictionary. E.g., for the column corresponding to a patient's age,
+ 222 the dictionary should contain a key-value pair of this shape:
+ 223
+ 224 .. code-block:: python
+ 225
+ 226 column_map = {
+ 227 ("patient", "#", "age"): {
+ 228 "func": compute_age_from_raw,
+ 229 "kwargs": {"randomize": False},
+ 230 "columns": ["birthday", "date of diagnosis"]
+ 231 },
+ 232 }
+ 233
+ 234 In this example, the function ``compute_age_from_raw`` is called with the
+ 235 values of the columns ``"birthday"`` and ``"date of diagnosis"`` as positional
+ 236 arguments, and the keyword argument ``"randomize"`` is set to ``False``. The
+ 237 function then returns the patient's age, which is subsequently stored in the column
+ 238 ``("patient", "#", "age")``.
+ 239
+ 240 Note that the ``column_map`` dictionary must have either a ``"default"`` key or
+ 241 ``"func"`` along with ``"columns"`` and ``"kwargs"``, depending on the function
+ 242 definition. If the function does not take any arguments, ``"columns"`` can be
+ 243 omitted. If it also does not take any keyword arguments, ``"kwargs"`` can be
+ 244 omitted, too.
+ 245
+ 246 .. _LyProX: https://lyprox.org
+ 247 """
+ 248 column_map = delete_private_keys(column_map)
+ 249
+ 250 if (instruction_depth := get_instruction_depth(column_map)) > 1:
+ 251 column_map = flatten(column_map, max_depth=instruction_depth)
+ 252
+ 253 multi_idx = pd.MultiIndex.from_tuples(column_map.keys())
+ 254 processed = pd.DataFrame(columns=multi_idx)
+ 255
+ 256 for multi_idx_col, instruction in column_map.items():
+ 257 if instruction != "":
+ 258 if "default" in instruction:
+ 259 processed[multi_idx_col] = [instruction["default"]] * len(raw)
+ 260 elif "func" in instruction:
+ 261 cols = instruction.get("columns", [])
+ 262 kwargs = instruction.get("kwargs", {})
+ 263 func = instruction["func"]
+ 264
+ 265 try:
+ 266 processed[multi_idx_col] = [
+ 267 func(*vals, **kwargs) for vals in raw[cols].values
+ 268 ]
+ 269 except Exception as exc:
+ 270 raise ParsingError(
+ 271 f"Exception encountered while parsing column {multi_idx_col}",
+ 272 ) from exc
+ 273 else:
+ 274 raise ParsingError(
+ 275 f"Column {multi_idx_col} has neither a `default` value nor `func` "
+ 276 "describing how to fill this column.",
+ 277 )
+ 278
+ 279 logger.info("Transformed raw data to LyProX format.")
+ 280 return processed
+ 281
+ 282
+ 283def leftright_to_ipsicontra(data: pd.DataFrame):
+ 284 """Change absolute side reporting to tumor-relative.
+ 285
+ 286 Transform reporting of LNL involvement by absolute side (right & left) to a
+ 287 reporting relative to the tumor (ipsi- & contralateral). The table ``data`` should
+ 288 already be in the format LyProX requires, except for the side-reporting of LNL
+ 289 involvement.
+ 290 """
+ 291 len_before = len(data)
+ 292 left_data = data.loc[data["tumor", "1", "side"] != "right"]
+ 293 right_data = data.loc[data["tumor", "1", "side"] == "right"]
+ 294
+ 295 left_data = left_data.rename(columns={"left": "ipsi"}, level=1)
+ 296 left_data = left_data.rename(columns={"right": "contra"}, level=1)
+ 297 right_data = right_data.rename(columns={"left": "contra"}, level=1)
+ 298 right_data = right_data.rename(columns={"right": "ipsi"}, level=1)
+ 299
+ 300 data = pd.concat([left_data, right_data], ignore_index=True)
+ 301 if len_before != len(data):
+ 302 raise RuntimeError("Number of patients changed")
+ 303
+ 304 logger.info("Transformed side reporting to ipsi- and contralateral.")
+ 305 return data
+ 306
+ 307
+ 308def exclude_patients(raw: pd.DataFrame, exclude: list[tuple[str, Any]]):
+ 309 """Exclude patients in the ``raw`` data based on a list of what to ``exclude``.
+ 310
+ 311 The ``exclude`` list contains tuples ``(column, check)``. The ``check`` function
+ 312 will then exclude any patients from the cohort where ``check(raw[column])``
+ 313 evaluates to ``True``.
+ 314
+ 315 >>> exclude = [("age", lambda s: s > 50)]
+ 316 >>> table = pd.DataFrame({
+ 317 ... "age": [43, 82, 18, 67],
+ 318 ... "T-category": [ 3, 4, 2, 1],
+ 319 ... })
+ 320 >>> exclude_patients(table, exclude)
+ 321 age T-category
+ 322 0 43 3
+ 323 2 18 2
+ 324 """
+ 325 num_before = len(raw)
+ 326 filtered = raw.copy()
+ 327
+ 328 for column, check in exclude:
+ 329 is_excluded = check(filtered[column])
+ 330 filtered = filtered.loc[~is_excluded]
+ 331
+ 332 num_after = len(filtered)
+ 333 logger.info(f"Excluded {num_before - num_after} patients.")
+ 334 return filtered
+ 335
+ 336
+ 337if __name__ == "__main__":
+ 338 main = assemble_main(settings_cls=LyproxifyCLI, prog_name="lyproxify")
+ 339 main()
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a_split_py.html b/htmlcov/z_9b7bcb970ba14d6a_split_py.html
new file mode 100644
index 0000000..b055382
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a_split_py.html
@@ -0,0 +1,170 @@
+
+
+
+
+ Coverage for src/lyscripts/data/split.py: 53%
+
+
+
+
+
+
+
+ 1"""Split a dataset into cross-validation folds based on params.yaml file."""
+ 2
+ 3import warnings
+ 4from pathlib import Path
+ 5
+ 6import numpy as np
+ 7import pandas as pd
+ 8from loguru import logger
+ 9from pydantic import Field
+ 10
+ 11from lyscripts.cli import assemble_main
+ 12from lyscripts.configs import BaseCLI, CrossValidationConfig, DataConfig
+ 13from lyscripts.data.utils import save_table_to_csv
+ 14
+ 15warnings.simplefilter(action="ignore", category=FutureWarning)
+ 16
+ 17
+ 18class SplitCLI(BaseCLI):
+ 19 """Split a dataset into cross-validation folds."""
+ 20
+ 21 input: DataConfig
+ 22 cross_validation: CrossValidationConfig = CrossValidationConfig()
+ 23 output_dir: Path = Field(description="The folder to store the split CSV files in.")
+ 24
+ 25 def cli_cmd(self) -> None:
+ 26 """Run the ``split`` subcommand.
+ 27
+ 28 This will load the dataset specified in the ``input`` argument and split it
+ 29 into the number of folds specified in the ``cross_validation`` argument. The
+ 30 resulting splits will be stored in the folder specified in the ``output_dir``
+ 31 argument.
+ 32 """
+ 33 logger.debug(self.model_dump_json(indent=2))
+ 34
+ 35 self.output_dir.mkdir(parents=True, exist_ok=True)
+ 36 logger.info(f"Ensure output directory {self.output_dir} exists")
+ 37
+ 38 data = self.input.load()
+ 39
+ 40 shuffled_data = data.sample(
+ 41 frac=1.0,
+ 42 replace=False,
+ 43 random_state=self.cross_validation.seed,
+ 44 ).reset_index(drop=True)
+ 45
+ 46 split_datas = np.array_split(
+ 47 ary=shuffled_data,
+ 48 indices_or_sections=self.cross_validation.folds,
+ 49 )
+ 50 for fold in range(self.cross_validation.folds):
+ 51 _train_datas = [
+ 52 split_datas[i] for i in range(self.cross_validation.folds) if i != fold
+ 53 ]
+ 54 train_data = pd.concat(
+ 55 objs=_train_datas,
+ 56 axis="index",
+ 57 ignore_index=True,
+ 58 )
+ 59 eval_data = split_datas[fold]
+ 60
+ 61 save_table_to_csv(
+ 62 file_path=self.output_dir / f"{fold}_train.csv",
+ 63 table=train_data,
+ 64 )
+ 65 save_table_to_csv(
+ 66 file_path=self.output_dir / f"{fold}_eval.csv",
+ 67 table=eval_data,
+ 68 )
+ 69
+ 70
+ 71if __name__ == "__main__":
+ 72 main = assemble_main(settings_cls=SplitCLI, prog_name="split")
+ 73 main()
+
+
+
+
diff --git a/htmlcov/z_9b7bcb970ba14d6a_utils_py.html b/htmlcov/z_9b7bcb970ba14d6a_utils_py.html
new file mode 100644
index 0000000..8524805
--- /dev/null
+++ b/htmlcov/z_9b7bcb970ba14d6a_utils_py.html
@@ -0,0 +1,113 @@
+
+
+
+
+ Coverage for src/lyscripts/data/utils.py: 100%
+
+
+
+
+
+
+
+ 1"""Utilities related to the commands for data cleaning and processing."""
+ 2
+ 3from pathlib import Path
+ 4
+ 5import pandas as pd
+ 6from loguru import logger
+ 7
+ 8from lyscripts.decorators import check_output_dir_exists
+ 9
+ 10
+ 11@check_output_dir_exists
+ 12def save_table_to_csv(file_path: Path, table: pd.DataFrame):
+ 13 """Save a ``table`` to ``output_path``."""
+ 14 shape = table.shape
+ 15 logger.info(f"Saving table with {shape=} to {file_path.resolve()}")
+ 16 table.to_csv(file_path, index=None)
+
+
+
+
From 67d0f1d940c4e99f98f0a1f15539c783243e3147 Mon Sep 17 00:00:00 2001
From: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 23 Jul 2025 06:01:16 +0000
Subject: [PATCH 2/5] Update coverage data
---
README.md | 11 +-
data.json | 2 +-
htmlcov/class_index.html | 54 +-
htmlcov/function_index.html | 102 +--
htmlcov/index.html | 33 +-
htmlcov/status.json | 2 +-
htmlcov/z_055061514423972c___init___py.html | 8 +-
htmlcov/z_055061514423972c___main___py.html | 8 +-
htmlcov/z_055061514423972c_posteriors_py.html | 8 +-
.../z_055061514423972c_prevalences_py.html | 442 ++++++------
htmlcov/z_055061514423972c_priors_py.html | 8 +-
htmlcov/z_055061514423972c_risks_py.html | 8 +-
htmlcov/z_055061514423972c_utils_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc___init___py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc___main___py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc__version_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_cli_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_configs_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_decorators_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_evaluate_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_plots_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_sample_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_schedule_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_schema_py.html | 8 +-
htmlcov/z_5bf5c588c698c6cc_utils_py.html | 8 +-
htmlcov/z_9b7bcb970ba14d6a___init___py.html | 58 +-
htmlcov/z_9b7bcb970ba14d6a___main___py.html | 8 +-
htmlcov/z_9b7bcb970ba14d6a_enhance_py.html | 12 +-
htmlcov/z_9b7bcb970ba14d6a_fetch_py.html | 154 +++++
htmlcov/z_9b7bcb970ba14d6a_filter_py.html | 12 +-
htmlcov/z_9b7bcb970ba14d6a_generate_py.html | 8 +-
htmlcov/z_9b7bcb970ba14d6a_join_py.html | 99 +--
htmlcov/z_9b7bcb970ba14d6a_lyproxify_py.html | 648 +++++++++---------
htmlcov/z_9b7bcb970ba14d6a_split_py.html | 8 +-
htmlcov/z_9b7bcb970ba14d6a_utils_py.html | 8 +-
35 files changed, 1003 insertions(+), 802 deletions(-)
create mode 100644 htmlcov/z_9b7bcb970ba14d6a_fetch_py.html
diff --git a/README.md b/README.md
index 12a1fb9..fde690c 100644
--- a/README.md
+++ b/README.md
@@ -11,18 +11,19 @@
| src/lyscripts/compute/\_\_init\_\_.py | 9 | 1 | 89% | 21 |
| src/lyscripts/compute/\_\_main\_\_.py | 5 | 5 | 0% | 3-8 |
| src/lyscripts/compute/posteriors.py | 46 | 19 | 59% |97-137, 141-142 |
-| src/lyscripts/compute/prevalences.py | 80 | 7 | 91% |58-60, 94-99, 232-233 |
+| src/lyscripts/compute/prevalences.py | 82 | 7 | 91% |59-61, 95-100, 234-235 |
| src/lyscripts/compute/priors.py | 35 | 2 | 94% | 110-111 |
| src/lyscripts/compute/risks.py | 51 | 33 | 35% |47-65, 81-135, 139-140 |
| src/lyscripts/compute/utils.py | 120 | 6 | 95% |95, 146, 177, 188, 240, 250 |
| src/lyscripts/configs.py | 255 | 27 | 89% |90, 122, 165, 173, 217, 271, 277-278, 286, 472, 506-509, 514, 585, 624-637, 680 |
-| src/lyscripts/data/\_\_init\_\_.py | 12 | 1 | 92% | 43 |
+| src/lyscripts/data/\_\_init\_\_.py | 13 | 1 | 92% | 45 |
| src/lyscripts/data/\_\_main\_\_.py | 18 | 18 | 0% | 3-36 |
| src/lyscripts/data/enhance.py | 24 | 8 | 67% |39-58, 62-63 |
+| src/lyscripts/data/fetch.py | 21 | 7 | 67% |42-52, 56-57 |
| src/lyscripts/data/filter.py | 49 | 30 | 39% |43-66, 76-94, 98-99 |
| src/lyscripts/data/generate.py | 39 | 4 | 90% |58, 63, 95-96 |
-| src/lyscripts/data/join.py | 20 | 9 | 55% |59-72, 76-77 |
-| src/lyscripts/data/lyproxify.py | 121 | 67 | 45% |29-32, 37-44, 88-117, 130-140, 171, 248-280, 291-305, 338-339 |
+| src/lyscripts/data/join.py | 20 | 9 | 55% |60-73, 77-78 |
+| src/lyscripts/data/lyproxify.py | 123 | 67 | 46% |31-34, 39-46, 90-119, 132-142, 173, 250-282, 293-307, 340-341 |
| src/lyscripts/data/split.py | 30 | 14 | 53% |33-65, 72-73 |
| src/lyscripts/data/utils.py | 9 | 0 | 100% | |
| src/lyscripts/decorators.py | 41 | 4 | 90% | 53-55, 70 |
@@ -32,7 +33,7 @@
| src/lyscripts/schedule.py | 29 | 13 | 55% |25-27, 35, 44-45, 73-80, 84-85 |
| src/lyscripts/schema.py | 21 | 3 | 86% | 60-61, 65 |
| src/lyscripts/utils.py | 84 | 5 | 94% |25, 141-142, 196-197 |
-| **TOTAL** | **1551** | **392** | **75%** | |
+| **TOTAL** | **1577** | **399** | **75%** | |
## Setup coverage badge
diff --git a/data.json b/data.json
index 1ec6703..a934360 100644
--- a/data.json
+++ b/data.json
@@ -1 +1 @@
-{"coverage": 74.72598323662153, "raw_data": {"meta": {"format": 3, "version": "7.9.1", "timestamp": "2025-06-26T14:44:06.796026", "branch_coverage": false, "show_contexts": false}, "files": {"src/lyscripts/__init__.py": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 34, "percent_covered": 79.41176470588235, "percent_covered_display": "79", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [57, 58, 66, 68, 69, 70, 72], "excluded_lines": [], "functions": {"LyscriptsCLI.__init__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [57, 58], "excluded_lines": []}, "LyscriptsCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [66, 68, 69, 70, 72], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"LyscriptsCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [57, 58, 66, 68, 69, 70, 72], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": []}}}, "src/lyscripts/_version.py": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": []}}, "classes": {"": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": []}}}, "src/lyscripts/cli.py": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 35, 37, 44, 46, 49, 70], "summary": {"covered_lines": 14, "num_statements": 26, "percent_covered": 53.84615384615385, "percent_covered_display": "54", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67, 81, 82, 83, 84, 85, 86], "excluded_lines": [], "functions": {"assemble_main": {"executed_lines": [35, 46], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "assemble_main.main": {"executed_lines": [37, 44], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "somewhat_safely_get_loglevel": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67], "excluded_lines": []}, "configure_logging": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [81, 82, 83, 84, 85, 86], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 49, 70], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 35, 37, 44, 46, 49, 70], "summary": {"covered_lines": 14, "num_statements": 26, "percent_covered": 53.84615384615385, "percent_covered_display": "54", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67, 81, 82, 83, 84, 85, 86], "excluded_lines": []}}}, "src/lyscripts/compute/__init__.py": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 9, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": [], "functions": {"ComputeCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": []}, "": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"ComputeCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": []}, "": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/compute/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": []}}}, "src/lyscripts/compute/posteriors.py": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 51, 52, 53, 54, 55, 57, 58, 60, 66, 75, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 27, "num_statements": 46, "percent_covered": 58.69565217391305, "percent_covered_display": "59", "missing_lines": 19, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137, 141, 142], "excluded_lines": [], "functions": {"compute_posteriors": {"executed_lines": [51, 52, 53, 54, 55, 57, 58, 60, 66, 75], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PosteriorsCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137], "excluded_lines": []}, "": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 17, "num_statements": 19, "percent_covered": 89.47368421052632, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}}, "classes": {"PosteriorsCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137], "excluded_lines": []}, "": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 51, 52, 53, 54, 55, 57, 58, 60, 66, 75, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 27, "num_statements": 29, "percent_covered": 93.10344827586206, "percent_covered_display": "93", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}}}, "src/lyscripts/compute/prevalences.py": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 28, 40, 43, 54, 55, 57, 62, 63, 64, 66, 72, 73, 78, 79, 81, 87, 101, 103, 106, 108, 109, 110, 111, 112, 113, 114, 117, 141, 142, 144, 145, 146, 148, 155, 161, 162, 164, 170, 173, 175, 177, 178, 181, 183, 184, 185, 186, 188, 189, 190, 192, 201, 202, 204, 214, 219, 220, 221, 222, 231], "summary": {"covered_lines": 73, "num_statements": 80, "percent_covered": 91.25, "percent_covered_display": "91", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [58, 59, 60, 94, 99, 232, 233], "excluded_lines": [], "functions": {"compute_prevalences": {"executed_lines": [54, 55, 57, 62, 63, 64, 66, 72, 73, 78, 79, 81, 87, 101, 103], "summary": {"covered_lines": 15, "num_statements": 20, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [58, 59, 60, 94, 99], "excluded_lines": []}, "generate_query_from_diagnosis": {"executed_lines": [108, 109, 110, 111, 112, 113, 114], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "observe_prevalence": {"executed_lines": [141, 142, 144, 145, 146, 148, 155], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PrevalencesCLI.cli_cmd": {"executed_lines": [177, 178, 181, 183, 184, 185, 186, 188, 189, 190, 192, 201, 202, 204, 214, 219, 220, 221, 222], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 28, 40, 43, 106, 117, 161, 162, 164, 170, 173, 175, 231], "summary": {"covered_lines": 25, "num_statements": 27, "percent_covered": 92.5925925925926, "percent_covered_display": "93", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [232, 233], "excluded_lines": []}}, "classes": {"PrevalencesCLI": {"executed_lines": [177, 178, 181, 183, 184, 185, 186, 188, 189, 190, 192, 201, 202, 204, 214, 219, 220, 221, 222], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 28, 40, 43, 54, 55, 57, 62, 63, 64, 66, 72, 73, 78, 79, 81, 87, 101, 103, 106, 108, 109, 110, 111, 112, 113, 114, 117, 141, 142, 144, 145, 146, 148, 155, 161, 162, 164, 170, 173, 175, 231], "summary": {"covered_lines": 54, "num_statements": 61, "percent_covered": 88.52459016393442, "percent_covered_display": "89", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [58, 59, 60, 94, 99, 232, 233], "excluded_lines": []}}}, "src/lyscripts/compute/priors.py": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 42, 43, 44, 46, 52, 53, 60, 63, 64, 66, 68, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106, 109], "summary": {"covered_lines": 33, "num_statements": 35, "percent_covered": 94.28571428571429, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": [], "functions": {"compute_priors": {"executed_lines": [42, 43, 44, 46, 52, 53, 60], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PriorsCLI.cli_cmd": {"executed_lines": [84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 63, 64, 66, 68, 109], "summary": {"covered_lines": 14, "num_statements": 16, "percent_covered": 87.5, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": []}}, "classes": {"PriorsCLI": {"executed_lines": [84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 42, 43, 44, 46, 52, 53, 60, 63, 64, 66, 68, 109], "summary": {"covered_lines": 21, "num_statements": 23, "percent_covered": 91.30434782608695, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": []}}}, "src/lyscripts/compute/risks.py": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 51, "percent_covered": 35.294117647058826, "percent_covered_display": "35", "missing_lines": 33, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65, 81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135, 139, 140], "excluded_lines": [], "functions": {"compute_risks": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65], "excluded_lines": []}, "RisksCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 22, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 22, "excluded_lines": 0}, "missing_lines": [81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135], "excluded_lines": []}, "": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 20, "percent_covered": 90.0, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [139, 140], "excluded_lines": []}}, "classes": {"RisksCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 22, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 22, "excluded_lines": 0}, "missing_lines": [81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135], "excluded_lines": []}, "": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 29, "percent_covered": 62.06896551724138, "percent_covered_display": "62", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65, 139, 140], "excluded_lines": []}}}, "src/lyscripts/compute/utils.py": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 49, 55, 57, 58, 59, 60, 62, 63, 66, 68, 69, 70, 71, 72, 73, 74, 77, 92, 94, 97, 98, 99, 101, 104, 106, 107, 108, 111, 112, 115, 116, 118, 121, 128, 145, 148, 149, 151, 153, 155, 156, 158, 159, 161, 163, 165, 166, 168, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 187, 189, 191, 194, 210, 211, 212, 213, 214, 215, 216, 217, 219, 222, 239, 242, 243, 244, 246, 247, 248, 249, 252, 254, 257, 259, 260, 261, 263, 264, 265, 266, 267, 269, 271, 272, 273, 275, 276], "summary": {"covered_lines": 114, "num_statements": 120, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [95, 146, 177, 188, 240, 250], "excluded_lines": [], "functions": {"is_hdf5_compatible": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "to_hdf5_attrs": {"executed_lines": [57, 58, 59, 60, 62, 63], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "from_hdf5_attrs": {"executed_lines": [68, 69, 70, 71, 72, 73, 74], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "extract_modalities": {"executed_lines": [92, 94, 97, 98, 99, 101], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [95], "excluded_lines": []}, "ensure_parent_dir": {"executed_lines": [106, 107, 108], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage._get_dataset": {"executed_lines": [145, 148, 149], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [146], "excluded_lines": []}, "HDF5FileStorage.load": {"executed_lines": [153, 155, 156, 158, 159], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage.get_attrs": {"executed_lines": [163, 165, 166, 168, 169], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage.save": {"executed_lines": [173, 175, 176, 178, 180], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [177], "excluded_lines": []}, "HDF5FileStorage.set_attrs": {"executed_lines": [184, 186, 187, 189, 191], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [188], "excluded_lines": []}, "reduce_pattern": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 219], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "complete_pattern": {"executed_lines": [239, 242, 243, 244, 246, 247, 248, 249, 252, 254], "summary": {"covered_lines": 10, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [240, 250], "excluded_lines": []}, "get_cached": {"executed_lines": [259, 260, 261, 263, 264, 275, 276], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_cached.log_cache_info_wrapper": {"executed_lines": [265, 266, 267, 269, 271, 272, 273], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 55, 66, 77, 104, 111, 112, 115, 116, 118, 121, 128, 151, 161, 171, 182, 194, 222, 257], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"BaseComputeCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage": {"executed_lines": [145, 148, 149, 153, 155, 156, 158, 159, 163, 165, 166, 168, 169, 173, 175, 176, 178, 180, 184, 186, 187, 189, 191], "summary": {"covered_lines": 23, "num_statements": 26, "percent_covered": 88.46153846153847, "percent_covered_display": "88", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [146, 177, 188], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 49, 55, 57, 58, 59, 60, 62, 63, 66, 68, 69, 70, 71, 72, 73, 74, 77, 92, 94, 97, 98, 99, 101, 104, 106, 107, 108, 111, 112, 115, 116, 118, 121, 128, 151, 161, 171, 182, 194, 210, 211, 212, 213, 214, 215, 216, 217, 219, 222, 239, 242, 243, 244, 246, 247, 248, 249, 252, 254, 257, 259, 260, 261, 263, 264, 265, 266, 267, 269, 271, 272, 273, 275, 276], "summary": {"covered_lines": 91, "num_statements": 94, "percent_covered": 96.80851063829788, "percent_covered_display": "97", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [95, 240, 250], "excluded_lines": []}}}, "src/lyscripts/configs.py": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 89, 92, 94, 96, 102, 104, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 161, 162, 164, 167, 168, 170, 171, 176, 177, 179, 182, 186, 187, 189, 190, 202, 204, 205, 206, 208, 209, 211, 214, 216, 219, 220, 222, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288, 296, 298, 299, 306, 307, 316, 321, 325, 332, 336, 341, 343, 348, 349, 350, 355, 357, 359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 457, 464, 466, 467, 469, 470, 475, 476, 478, 482, 487, 491, 495, 496, 498, 500, 501, 503, 505, 511, 513, 519, 521, 522, 523, 524, 525, 526, 529, 550, 551, 553, 554, 555, 561, 562, 565, 572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595, 598, 604, 605, 606, 608, 609, 610, 612, 613, 616, 640, 643, 644, 657, 672, 673, 675, 677, 678, 679, 684, 686, 688, 692, 693, 699, 701, 703, 713, 714, 716, 718, 728, 729, 738, 743, 744], "summary": {"covered_lines": 228, "num_statements": 255, "percent_covered": 89.41176470588235, "percent_covered_display": "89", "missing_lines": 27, "excluded_lines": 0}, "missing_lines": [90, 122, 165, 173, 217, 271, 277, 278, 286, 472, 506, 507, 509, 514, 585, 624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637, 680], "excluded_lines": [], "functions": {"DataConfig.load": {"executed_lines": [89, 92], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [90], "excluded_lines": []}, "DataConfig.get_load_kwargs": {"executed_lines": [96], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_pattern": {"executed_lines": [104], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DiagnosisConfig.to_involvement": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [122], "excluded_lines": []}, "retrieve_graph_representation": {"executed_lines": [161, 162, 164, 167, 168, 170, 171], "summary": {"covered_lines": 7, "num_statements": 9, "percent_covered": 77.77777777777777, "percent_covered_display": "78", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [165, 173], "excluded_lines": []}, "GraphConfig.from_model": {"executed_lines": [189, 190], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "has_model_symbol": {"executed_lines": [204, 205, 206, 208, 209, 211], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_symmetry_kwargs": {"executed_lines": [216, 219, 220, 222], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [217], "excluded_lines": []}, "ModelConfig.from_model": {"executed_lines": [261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288], "summary": {"covered_lines": 11, "num_statements": 15, "percent_covered": 73.33333333333333, "percent_covered_display": "73", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [271, 277, 278, 286], "excluded_lines": []}, "modalityconfig_from_model": {"executed_lines": [298, 299], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DeprecatedModelConfig.model_post_init": {"executed_lines": [343, 348, 349, 350, 355], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DeprecatedModelConfig.translate": {"executed_lines": [359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SamplingConfig.load": {"executed_lines": [457], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "map_to_optional_bool": {"executed_lines": [466, 467, 469, 470], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [472], "excluded_lines": []}, "ScenarioConfig.model_post_init": {"executed_lines": [500, 501], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ScenarioConfig.interpolate": {"executed_lines": [505], "summary": {"covered_lines": 1, "num_statements": 4, "percent_covered": 25.0, "percent_covered_display": "25", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [506, 507, 509], "excluded_lines": []}, "ScenarioConfig.normalize": {"executed_lines": [513], "summary": {"covered_lines": 1, "num_statements": 2, "percent_covered": 50.0, "percent_covered_display": "50", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [514], "excluded_lines": []}, "_construct_model_from_external": {"executed_lines": [521, 522, 523, 524, 525, 526], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "construct_model": {"executed_lines": [550, 551, 553, 554, 555, 561, 562], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "add_distributions": {"executed_lines": [572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595], "summary": {"covered_lines": 17, "num_statements": 18, "percent_covered": 94.44444444444444, "percent_covered_display": "94", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [585], "excluded_lines": []}, "add_modalities": {"executed_lines": [604, 605, 606, 608, 609, 610, 612, 613], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "add_data": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__init__": {"executed_lines": [672, 673], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DynamicYamlConfigSettingsSource._read_file": {"executed_lines": [677, 678, 679, 684], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [680], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__call__": {"executed_lines": [688, 692, 693, 699], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__repr__": {"executed_lines": [703], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BaseCLI.settings_customise_sources": {"executed_lines": [738, 743, 744], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 94, 102, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 176, 177, 179, 182, 186, 187, 202, 214, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 296, 306, 307, 316, 321, 325, 332, 336, 341, 357, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 464, 475, 476, 478, 482, 487, 491, 495, 496, 498, 503, 511, 519, 529, 565, 598, 616, 640, 643, 644, 657, 675, 686, 701, 713, 714, 716, 718, 728, 729], "summary": {"covered_lines": 112, "num_statements": 112, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"CrossValidationConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DataConfig": {"executed_lines": [89, 92, 96], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [90], "excluded_lines": []}, "DiagnosisConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [122], "excluded_lines": []}, "DistributionConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "InvolvementConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "GraphConfig": {"executed_lines": [189, 190], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ModelConfig": {"executed_lines": [261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288], "summary": {"covered_lines": 11, "num_statements": 15, "percent_covered": 73.33333333333333, "percent_covered_display": "73", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [271, 277, 278, 286], "excluded_lines": []}, "DeprecatedModelConfig": {"executed_lines": [343, 348, 349, 350, 355, 359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SamplingConfig": {"executed_lines": [457], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ScenarioConfig": {"executed_lines": [500, 501, 505, 513], "summary": {"covered_lines": 4, "num_statements": 8, "percent_covered": 50.0, "percent_covered_display": "50", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [506, 507, 509, 514], "excluded_lines": []}, "DynamicYamlConfigSettingsSource": {"executed_lines": [672, 673, 677, 678, 679, 684, 688, 692, 693, 699, 703], "summary": {"covered_lines": 11, "num_statements": 12, "percent_covered": 91.66666666666667, "percent_covered_display": "92", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [680], "excluded_lines": []}, "BaseCLI": {"executed_lines": [738, 743, 744], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 94, 102, 104, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 161, 162, 164, 167, 168, 170, 171, 176, 177, 179, 182, 186, 187, 202, 204, 205, 206, 208, 209, 211, 214, 216, 219, 220, 222, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 296, 298, 299, 306, 307, 316, 321, 325, 332, 336, 341, 357, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 464, 466, 467, 469, 470, 475, 476, 478, 482, 487, 491, 495, 496, 498, 503, 511, 519, 521, 522, 523, 524, 525, 526, 529, 550, 551, 553, 554, 555, 561, 562, 565, 572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595, 598, 604, 605, 606, 608, 609, 610, 612, 613, 616, 640, 643, 644, 657, 675, 686, 701, 713, 714, 716, 718, 728, 729], "summary": {"covered_lines": 174, "num_statements": 190, "percent_covered": 91.57894736842105, "percent_covered_display": "92", "missing_lines": 16, "excluded_lines": 0}, "missing_lines": [165, 173, 217, 472, 585, 624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637], "excluded_lines": []}}}, "src/lyscripts/data/__init__.py": {"executed_lines": [1, 17, 19, 28, 31, 32, 34, 35, 36, 37, 38, 39, 41], "summary": {"covered_lines": 11, "num_statements": 12, "percent_covered": 91.66666666666667, "percent_covered_display": "92", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [43], "excluded_lines": [], "functions": {"DataCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [43], "excluded_lines": []}, "": {"executed_lines": [1, 17, 19, 28, 31, 32, 34, 35, 36, 37, 38, 39, 41], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"DataCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [43], "excluded_lines": []}, "": {"executed_lines": [1, 17, 19, 28, 31, 32, 34, 35, 36, 37, 38, 39, 41], "summary": {"covered_lines": 11, "num_statements": 11, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/data/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 15, 20, 21, 25, 26, 27, 28, 29, 31, 32, 35, 36], "excluded_lines": [], "functions": {"main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 10, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 10, "excluded_lines": 0}, "missing_lines": [15, 20, 21, 25, 26, 27, 28, 29, 31, 32], "excluded_lines": []}, "": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 35, 36], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 15, 20, 21, 25, 26, 27, 28, 29, 31, 32, 35, 36], "excluded_lines": []}}}, "src/lyscripts/data/enhance.py": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 24, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58, 62, 63], "excluded_lines": [], "functions": {"EnhanceCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [62, 63], "excluded_lines": []}}, "classes": {"EnhanceCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [62, 63], "excluded_lines": []}}}, "src/lyscripts/data/filter.py": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 49, "percent_covered": 38.775510204081634, "percent_covered_display": "39", "missing_lines": 30, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66, 76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94, 98, 99], "excluded_lines": [], "functions": {"FilterCLI.model_post_init": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66], "excluded_lines": []}, "FilterCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 21, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [98, 99], "excluded_lines": []}}, "classes": {"FilterCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 28, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 28, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66, 76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 21, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [98, 99], "excluded_lines": []}}}, "src/lyscripts/data/generate.py": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 55, 56, 57, 60, 62, 65, 67, 76, 78, 79, 80, 81, 82, 84, 89, 91, 94], "summary": {"covered_lines": 35, "num_statements": 39, "percent_covered": 89.74358974358974, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [58, 63, 95, 96], "excluded_lines": [], "functions": {"GenerateCLI.model_post_init": {"executed_lines": [55, 56, 57, 60, 62, 65], "summary": {"covered_lines": 6, "num_statements": 8, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [58, 63], "excluded_lines": []}, "GenerateCLI.cli_cmd": {"executed_lines": [76, 78, 79, 80, 81, 82, 84, 89, 91], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 67, 94], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [95, 96], "excluded_lines": []}}, "classes": {"GenerateCLI": {"executed_lines": [55, 56, 57, 60, 62, 65, 76, 78, 79, 80, 81, 82, 84, 89, 91], "summary": {"covered_lines": 15, "num_statements": 17, "percent_covered": 88.23529411764706, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [58, 63], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 67, 94], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [95, 96], "excluded_lines": []}}}, "src/lyscripts/data/join.py": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 75], "summary": {"covered_lines": 11, "num_statements": 20, "percent_covered": 55.0, "percent_covered_display": "55", "missing_lines": 9, "excluded_lines": 0}, "missing_lines": [59, 61, 62, 63, 64, 66, 72, 76, 77], "excluded_lines": [], "functions": {"JoinCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [59, 61, 62, 63, 64, 66, 72], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 75], "summary": {"covered_lines": 11, "num_statements": 13, "percent_covered": 84.61538461538461, "percent_covered_display": "85", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [76, 77], "excluded_lines": []}}, "classes": {"JoinCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [59, 61, 62, 63, 64, 66, 72], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 75], "summary": {"covered_lines": 11, "num_statements": 13, "percent_covered": 84.61538461538461, "percent_covered_display": "85", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [76, 77], "excluded_lines": []}}}, "src/lyscripts/data/lyproxify.py": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 19, 20, 21, 22, 24, 27, 35, 47, 48, 50, 51, 55, 65, 72, 76, 78, 120, 121, 124, 143, 160, 161, 162, 163, 165, 167, 174, 197, 198, 199, 200, 201, 202, 203, 204, 206, 208, 211, 283, 308, 325, 326, 328, 329, 330, 332, 333, 334, 337], "summary": {"covered_lines": 54, "num_statements": 121, "percent_covered": 44.62809917355372, "percent_covered_display": "45", "missing_lines": 67, "excluded_lines": 0}, "missing_lines": [29, 30, 32, 37, 38, 39, 41, 42, 44, 88, 90, 94, 100, 101, 102, 103, 104, 106, 107, 108, 109, 111, 112, 114, 115, 117, 130, 132, 133, 134, 139, 140, 171, 248, 250, 251, 253, 254, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 269, 270, 274, 279, 280, 291, 292, 293, 295, 296, 297, 298, 300, 301, 302, 304, 305, 338, 339], "excluded_lines": [], "functions": {"ensure_python_file": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [29, 30, 32], "excluded_lines": []}, "ensure_column_map": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [37, 38, 39, 41, 42, 44], "excluded_lines": []}, "LyproxifyCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [88, 90, 94, 100, 101, 102, 103, 104, 106, 107, 108, 109, 111, 112, 114, 115, 117], "excluded_lines": []}, "clean_header": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [130, 132, 133, 134, 139, 140], "excluded_lines": []}, "get_instruction_depth": {"executed_lines": [160, 161, 162, 163, 165, 167], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [171], "excluded_lines": []}, "generate_markdown_docs": {"executed_lines": [197, 198, 199, 200, 201, 202, 203, 204, 206, 208], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "transform_to_lyprox": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0}, "missing_lines": [248, 250, 251, 253, 254, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 269, 270, 274, 279, 280], "excluded_lines": []}, "leftright_to_ipsicontra": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [291, 292, 293, 295, 296, 297, 298, 300, 301, 302, 304, 305], "excluded_lines": []}, "exclude_patients": {"executed_lines": [325, 326, 328, 329, 330, 332, 333, 334], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 19, 20, 21, 22, 24, 27, 35, 47, 48, 50, 51, 55, 65, 72, 76, 78, 120, 121, 124, 143, 174, 211, 283, 308, 337], "summary": {"covered_lines": 30, "num_statements": 32, "percent_covered": 93.75, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [338, 339], "excluded_lines": []}}, "classes": {"LyproxifyCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [88, 90, 94, 100, 101, 102, 103, 104, 106, 107, 108, 109, 111, 112, 114, 115, 117], "excluded_lines": []}, "ParsingError": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 19, 20, 21, 22, 24, 27, 35, 47, 48, 50, 51, 55, 65, 72, 76, 78, 120, 121, 124, 143, 160, 161, 162, 163, 165, 167, 174, 197, 198, 199, 200, 201, 202, 203, 204, 206, 208, 211, 283, 308, 325, 326, 328, 329, 330, 332, 333, 334, 337], "summary": {"covered_lines": 54, "num_statements": 104, "percent_covered": 51.92307692307692, "percent_covered_display": "52", "missing_lines": 50, "excluded_lines": 0}, "missing_lines": [29, 30, 32, 37, 38, 39, 41, 42, 44, 130, 132, 133, 134, 139, 140, 171, 248, 250, 251, 253, 254, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 269, 270, 274, 279, 280, 291, 292, 293, 295, 296, 297, 298, 300, 301, 302, 304, 305, 338, 339], "excluded_lines": []}}}, "src/lyscripts/data/split.py": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 30, "percent_covered": 53.333333333333336, "percent_covered_display": "53", "missing_lines": 14, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65, 72, 73], "excluded_lines": [], "functions": {"SplitCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [72, 73], "excluded_lines": []}}, "classes": {"SplitCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [72, 73], "excluded_lines": []}}}, "src/lyscripts/data/utils.py": {"executed_lines": [1, 3, 5, 6, 8, 11, 12, 14, 15, 16], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": [], "functions": {"save_table_to_csv": {"executed_lines": [14, 15, 16], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 11, 12], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 3, 5, 6, 8, 11, 12, 14, 15, 16], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/decorators.py": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 18, 19, 20, 23, 30, 33, 34, 36, 37, 38, 39, 41, 42, 51, 57, 59, 62, 65, 66, 68, 69, 72, 74, 77, 80, 81, 83, 84, 86, 88], "summary": {"covered_lines": 37, "num_statements": 41, "percent_covered": 90.2439024390244, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [53, 54, 55, 70], "excluded_lines": [], "functions": {"assemble_signature": {"executed_lines": [18, 19, 20], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state": {"executed_lines": [30, 59], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state.log_decorator": {"executed_lines": [33, 34, 57], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state.log_decorator.wrapper": {"executed_lines": [36, 37, 38, 39, 41, 42, 51], "summary": {"covered_lines": 7, "num_statements": 10, "percent_covered": 70.0, "percent_covered_display": "70", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [53, 54, 55], "excluded_lines": []}, "check_input_file_exists": {"executed_lines": [65, 66, 74], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_input_file_exists.inner": {"executed_lines": [68, 69, 72], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [70], "excluded_lines": []}, "check_output_dir_exists": {"executed_lines": [80, 81, 88], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_output_dir_exists.inner": {"executed_lines": [83, 84, 86], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 23, 62, 77], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 18, 19, 20, 23, 30, 33, 34, 36, 37, 38, 39, 41, 42, 51, 57, 59, 62, 65, 66, 68, 69, 72, 74, 77, 80, 81, 83, 84, 86, 88], "summary": {"covered_lines": 37, "num_statements": 41, "percent_covered": 90.2439024390244, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [53, 54, 55, 70], "excluded_lines": []}}}, "src/lyscripts/evaluate.py": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 75, "percent_covered": 24.0, "percent_covered_display": "24", "missing_lines": 57, "excluded_lines": 0}, "missing_lines": [29, 35, 43, 48, 50, 57, 63, 70, 87, 104, 105, 106, 107, 108, 109, 120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141, 146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204, 208, 209, 211, 212], "excluded_lines": [], "functions": {"_add_parser": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [29, 35], "excluded_lines": []}, "_add_arguments": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [43, 48, 50, 57, 63, 70], "excluded_lines": []}, "comp_bic": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [87], "excluded_lines": []}, "compute_evidence": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [104, 105, 106, 107, 108, 109], "excluded_lines": []}, "compute_ti_results": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 14, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 14, "excluded_lines": 0}, "missing_lines": [120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141], "excluded_lines": []}, "main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 24, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 24, "excluded_lines": 0}, "missing_lines": [146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 22, "percent_covered": 81.81818181818181, "percent_covered_display": "82", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [208, 209, 211, 212], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 75, "percent_covered": 24.0, "percent_covered_display": "24", "missing_lines": 57, "excluded_lines": 0}, "missing_lines": [29, 35, 43, 48, 50, 57, 63, 70, 87, 104, 105, 106, 107, 108, 109, 120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141, 146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204, 208, 209, 211, 212], "excluded_lines": []}}}, "src/lyscripts/plots.py": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 61, 64, 66, 69, 74, 75, 76, 77, 78, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 112, 115, 116, 118, 120, 121, 123, 125, 126, 135, 136, 137, 138, 139, 140, 142, 144, 146, 148, 150, 152, 154, 155, 157, 158, 160, 163, 164, 166, 167, 169, 170, 179, 180, 181, 182, 183, 184, 190, 198, 199, 201, 202, 204, 206, 208, 216, 218, 225, 227, 234, 239, 240, 241, 243, 244, 246, 247, 249, 252, 268, 269, 270, 271, 273, 274, 275, 276, 279, 281, 282, 288, 289, 290, 291, 294, 303, 306, 307, 310, 311, 314, 335, 338, 340, 343, 345, 354, 355, 357, 358, 360, 363, 395, 396, 397, 402, 403, 404, 410, 411], "summary": {"covered_lines": 143, "num_statements": 166, "percent_covered": 86.144578313253, "percent_covered_display": "86", "missing_lines": 23, "excluded_lines": 0}, "missing_lines": [26, 27, 46, 47, 56, 94, 99, 104, 185, 186, 336, 341, 370, 375, 377, 378, 380, 381, 382, 383, 385, 392, 399], "excluded_lines": [], "functions": {"floor_at_decimal": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [46, 47], "excluded_lines": []}, "ceil_at_decimal": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [56], "excluded_lines": []}, "floor_to_step": {"executed_lines": [61], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ceil_to_step": {"executed_lines": [66], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "clean_and_check": {"executed_lines": [74, 75, 76, 77, 78], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AbstractDistribution.draw": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [94], "excluded_lines": []}, "AbstractDistribution.left_percentile": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [99], "excluded_lines": []}, "AbstractDistribution.right_percentile": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [104], "excluded_lines": []}, "AbstractDistribution._get_label": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AbstractDistribution.label": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.values": {"executed_lines": [123], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.from_hdf5": {"executed_lines": [135, 136, 137, 138, 139, 140], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.left_percentile": {"executed_lines": [144], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.right_percentile": {"executed_lines": [148], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.draw": {"executed_lines": [152, 154, 155, 157, 158, 160], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.from_hdf5": {"executed_lines": [179, 180, 181, 182, 183, 184, 190], "summary": {"covered_lines": 7, "num_statements": 9, "percent_covered": 77.77777777777777, "percent_covered_display": "78", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [185, 186], "excluded_lines": []}, "BetaPosterior._get_label": {"executed_lines": [199], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.num_fail": {"executed_lines": [204], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.pdf": {"executed_lines": [208], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.left_percentile": {"executed_lines": [218], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.right_percentile": {"executed_lines": [227], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.draw": {"executed_lines": [239, 240, 241, 243, 244, 246, 247, 249], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_size": {"executed_lines": [268, 269, 270, 271, 273, 274, 275, 276], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_label": {"executed_lines": [281, 282, 288, 289, 290, 291], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_xlims": {"executed_lines": [303, 306, 307, 310, 311], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "draw": {"executed_lines": [335, 338, 340, 343, 345, 354, 355, 357, 358, 360], "summary": {"covered_lines": 10, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [336, 341], "excluded_lines": []}, "split_legends": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 10, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 10, "excluded_lines": 0}, "missing_lines": [370, 375, 377, 378, 380, 381, 382, 383, 385, 392], "excluded_lines": []}, "use_mpl_stylesheet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [399], "excluded_lines": []}, "save_figure": {"executed_lines": [410, 411], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 64, 69, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 115, 116, 118, 120, 121, 125, 126, 142, 146, 150, 163, 164, 166, 167, 169, 170, 198, 201, 202, 206, 216, 225, 234, 252, 279, 294, 314, 363, 395, 396, 397, 402, 403, 404], "summary": {"covered_lines": 69, "num_statements": 71, "percent_covered": 97.1830985915493, "percent_covered_display": "97", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [26, 27], "excluded_lines": []}}, "classes": {"AbstractDistribution": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 4, "percent_covered": 25.0, "percent_covered_display": "25", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [94, 99, 104], "excluded_lines": []}, "Histogram": {"executed_lines": [123, 135, 136, 137, 138, 139, 140, 144, 148, 152, 154, 155, 157, 158, 160], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior": {"executed_lines": [179, 180, 181, 182, 183, 184, 190, 199, 204, 208, 218, 227, 239, 240, 241, 243, 244, 246, 247, 249], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [185, 186], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 61, 64, 66, 69, 74, 75, 76, 77, 78, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 115, 116, 118, 120, 121, 125, 126, 142, 146, 150, 163, 164, 166, 167, 169, 170, 198, 201, 202, 206, 216, 225, 234, 252, 268, 269, 270, 271, 273, 274, 275, 276, 279, 281, 282, 288, 289, 290, 291, 294, 303, 306, 307, 310, 311, 314, 335, 338, 340, 343, 345, 354, 355, 357, 358, 360, 363, 395, 396, 397, 402, 403, 404, 410, 411], "summary": {"covered_lines": 107, "num_statements": 125, "percent_covered": 85.6, "percent_covered_display": "86", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [26, 27, 46, 47, 56, 336, 341, 370, 375, 377, 378, 380, 381, 382, 383, 385, 392, 399], "excluded_lines": []}}}, "src/lyscripts/sample.py": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 68, 69, 71, 73, 75, 78, 79, 81, 83, 84, 85, 86, 89, 90, 92, 93, 95, 97, 98, 100, 101, 103, 106, 107, 109, 110, 112, 114, 115, 117, 118, 120, 123, 126, 135, 140, 141, 142, 146, 147, 148, 150, 153, 162, 163, 175, 184, 185, 187, 190, 193, 209, 215, 217, 225, 257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300, 304, 305, 307, 311, 316, 322, 325, 327, 328, 334, 347, 348, 350, 351, 352, 359, 365, 366, 368, 385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410, 419], "summary": {"covered_lines": 125, "num_statements": 136, "percent_covered": 91.91176470588235, "percent_covered_display": "92", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [35, 36, 74, 132, 172, 188, 301, 309, 313, 420, 421], "excluded_lines": [], "functions": {"CompletedItersColumn.__init__": {"executed_lines": [68, 69], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "CompletedItersColumn.render": {"executed_lines": [73, 75], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [74], "excluded_lines": []}, "ItersPerSecondColumn.render": {"executed_lines": [83, 84, 85, 86], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime.update": {"executed_lines": [97, 98], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime.relative_diff": {"executed_lines": [103], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted.update": {"executed_lines": [114, 115], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted.newly_accepted": {"executed_lines": [120], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_prob_fn": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [132], "excluded_lines": []}, "ensure_initial_state": {"executed_lines": [140, 141, 142, 146, 147, 148, 150], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ensure_history_table": {"executed_lines": [162, 163], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [172], "excluded_lines": []}, "update_history_table": {"executed_lines": [184, 185, 187, 190], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [188], "excluded_lines": []}, "is_converged": {"executed_lines": [209], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "_get_columns": {"executed_lines": [217], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "run_sampling": {"executed_lines": [257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [301], "excluded_lines": []}, "DummyPool.__enter__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [309], "excluded_lines": []}, "DummyPool.__exit__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [313], "excluded_lines": []}, "get_pool": {"executed_lines": [322], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "init_sampler": {"executed_lines": [327, 328, 334], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SampleCLI.cli_cmd": {"executed_lines": [385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 71, 78, 79, 81, 89, 90, 92, 93, 95, 100, 101, 106, 107, 109, 110, 112, 117, 118, 123, 126, 135, 153, 175, 193, 215, 225, 304, 305, 307, 311, 316, 325, 347, 348, 350, 351, 352, 359, 365, 366, 368, 419], "summary": {"covered_lines": 61, "num_statements": 65, "percent_covered": 93.84615384615384, "percent_covered_display": "94", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [35, 36, 420, 421], "excluded_lines": []}}, "classes": {"CompletedItersColumn": {"executed_lines": [68, 69, 73, 75], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [74], "excluded_lines": []}, "ItersPerSecondColumn": {"executed_lines": [83, 84, 85, 86], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime": {"executed_lines": [97, 98, 103], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted": {"executed_lines": [114, 115, 120], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DummyPool": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [309, 313], "excluded_lines": []}, "SampleCLI": {"executed_lines": [385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 71, 78, 79, 81, 89, 90, 92, 93, 95, 100, 101, 106, 107, 109, 110, 112, 117, 118, 123, 126, 135, 140, 141, 142, 146, 147, 148, 150, 153, 162, 163, 175, 184, 185, 187, 190, 193, 209, 215, 217, 225, 257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300, 304, 305, 307, 311, 316, 322, 325, 327, 328, 334, 347, 348, 350, 351, 352, 359, 365, 366, 368, 419], "summary": {"covered_lines": 99, "num_statements": 107, "percent_covered": 92.5233644859813, "percent_covered_display": "93", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [35, 36, 132, 172, 188, 301, 420, 421], "excluded_lines": []}}}, "src/lyscripts/schedule.py": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 29, "percent_covered": 55.172413793103445, "percent_covered_display": "55", "missing_lines": 13, "excluded_lines": 0}, "missing_lines": [25, 26, 27, 35, 44, 45, 73, 75, 76, 78, 80, 84, 85], "excluded_lines": [], "functions": {"geometric_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [25, 26, 27], "excluded_lines": []}, "linear_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [35], "excluded_lines": []}, "power_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [44, 45], "excluded_lines": []}, "ScheduleCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [73, 75, 76, 78, 80], "excluded_lines": []}, "": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [84, 85], "excluded_lines": []}}, "classes": {"ScheduleCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [73, 75, 76, 78, 80], "excluded_lines": []}, "": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 24, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [25, 26, 27, 35, 44, 45, 84, 85], "excluded_lines": []}}}, "src/lyscripts/schema.py": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 21, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [60, 61, 65], "excluded_lines": [], "functions": {"main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [60, 61], "excluded_lines": []}, "": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 19, "percent_covered": 94.73684210526316, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [65], "excluded_lines": []}}, "classes": {"SchemaSettings": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 21, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [60, 61, 65], "excluded_lines": []}}}, "src/lyscripts/utils.py": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 23, 24, 26, 27, 30, 33, 42, 43, 45, 46, 47, 48, 50, 53, 63, 65, 66, 67, 68, 70, 72, 75, 91, 93, 94, 95, 97, 98, 100, 102, 105, 115, 117, 118, 119, 120, 122, 124, 127, 137, 138, 139, 140, 143, 146, 151, 152, 154, 155, 156, 159, 160, 162, 163, 164, 165, 168, 169, 177, 178, 179, 180, 183, 184, 192, 193, 195, 199], "summary": {"covered_lines": 79, "num_statements": 84, "percent_covered": 94.04761904761905, "percent_covered_display": "94", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [25, 141, 142, 196, 197], "excluded_lines": [], "functions": {"binom_pmf": {"executed_lines": [23, 24, 26, 27, 30], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [25], "excluded_lines": []}, "get_dict_depth": {"executed_lines": [42, 43, 45, 46, 47, 48, 50], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "delete_private_keys": {"executed_lines": [63, 65, 66, 67, 68, 70, 72], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "flatten": {"executed_lines": [91, 93, 94, 95, 97, 98, 100, 102], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "unflatten": {"executed_lines": [115, 117, 118, 119, 120, 122, 124], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_modalities_subset": {"executed_lines": [137, 138, 139, 140, 143], "summary": {"covered_lines": 5, "num_statements": 7, "percent_covered": 71.42857142857143, "percent_covered_display": "71", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}, "load_patient_data": {"executed_lines": [151, 152, 154, 155, 156], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "load_yaml_params": {"executed_lines": [162, 163, 164, 165], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "load_model_samples": {"executed_lines": [177, 178, 179, 180], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_hdf5_backend": {"executed_lines": [192, 193, 195, 199], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [196, 197], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 33, 53, 75, 105, 127, 146, 159, 160, 168, 169, 183, 184], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 23, 24, 26, 27, 30, 33, 42, 43, 45, 46, 47, 48, 50, 53, 63, 65, 66, 67, 68, 70, 72, 75, 91, 93, 94, 95, 97, 98, 100, 102, 105, 115, 117, 118, 119, 120, 122, 124, 127, 137, 138, 139, 140, 143, 146, 151, 152, 154, 155, 156, 159, 160, 162, 163, 164, 165, 168, 169, 177, 178, 179, 180, 183, 184, 192, 193, 195, 199], "summary": {"covered_lines": 79, "num_statements": 84, "percent_covered": 94.04761904761905, "percent_covered_display": "94", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [25, 141, 142, 196, 197], "excluded_lines": []}}}}, "totals": {"covered_lines": 1159, "num_statements": 1551, "percent_covered": 74.72598323662153, "percent_covered_display": "75", "missing_lines": 392, "excluded_lines": 0}}, "coverage_path": "."}
\ No newline at end of file
+{"coverage": 74.6987951807229, "raw_data": {"meta": {"format": 3, "version": "7.9.2", "timestamp": "2025-07-23T06:01:14.940919", "branch_coverage": false, "show_contexts": false}, "files": {"src/lyscripts/__init__.py": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 34, "percent_covered": 79.41176470588235, "percent_covered_display": "79", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [57, 58, 66, 68, 69, 70, 72], "excluded_lines": [], "functions": {"LyscriptsCLI.__init__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [57, 58], "excluded_lines": []}, "LyscriptsCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [66, 68, 69, 70, 72], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"LyscriptsCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [57, 58, 66, 68, 69, 70, 72], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 20, 21, 22, 23, 25, 26, 27, 28, 29, 33, 35, 38, 39, 41, 45, 50, 51, 52, 53, 55, 60, 75], "summary": {"covered_lines": 27, "num_statements": 27, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [3, 5, 6], "excluded_lines": []}}}, "src/lyscripts/_version.py": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": [], "functions": {"": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": []}}, "classes": {"": {"executed_lines": [4, 6, 7, 13, 15, 16, 17, 18, 20, 21], "summary": {"covered_lines": 10, "num_statements": 13, "percent_covered": 76.92307692307692, "percent_covered_display": "77", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [8, 9, 11], "excluded_lines": []}}}, "src/lyscripts/cli.py": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 35, 37, 44, 46, 49, 70], "summary": {"covered_lines": 14, "num_statements": 26, "percent_covered": 53.84615384615385, "percent_covered_display": "54", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67, 81, 82, 83, 84, 85, 86], "excluded_lines": [], "functions": {"assemble_main": {"executed_lines": [35, 46], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "assemble_main.main": {"executed_lines": [37, 44], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "somewhat_safely_get_loglevel": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67], "excluded_lines": []}, "configure_logging": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [81, 82, 83, 84, 85, 86], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 49, 70], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 11, 12, 14, 15, 16, 17, 18, 21, 35, 37, 44, 46, 49, 70], "summary": {"covered_lines": 14, "num_statements": 26, "percent_covered": 53.84615384615385, "percent_covered_display": "54", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [61, 62, 63, 64, 65, 67, 81, 82, 83, 84, 85, 86], "excluded_lines": []}}}, "src/lyscripts/compute/__init__.py": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 9, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": [], "functions": {"ComputeCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": []}, "": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"ComputeCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [21], "excluded_lines": []}, "": {"executed_lines": [1, 6, 8, 11, 12, 14, 15, 16, 17, 19], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/compute/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": [], "functions": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [3, 4, 6, 7, 8], "excluded_lines": []}}}, "src/lyscripts/compute/posteriors.py": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 51, 52, 53, 54, 55, 57, 58, 60, 66, 75, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 27, "num_statements": 46, "percent_covered": 58.69565217391305, "percent_covered_display": "59", "missing_lines": 19, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137, 141, 142], "excluded_lines": [], "functions": {"compute_posteriors": {"executed_lines": [51, 52, 53, 54, 55, 57, 58, 60, 66, 75], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PosteriorsCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137], "excluded_lines": []}, "": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 17, "num_statements": 19, "percent_covered": 89.47368421052632, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}}, "classes": {"PosteriorsCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [97, 99, 102, 104, 105, 106, 107, 109, 110, 111, 113, 122, 123, 125, 135, 136, 137], "excluded_lines": []}, "": {"executed_lines": [1, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 29, 32, 51, 52, 53, 54, 55, 57, 58, 60, 66, 75, 78, 79, 81, 87, 91, 140], "summary": {"covered_lines": 27, "num_statements": 29, "percent_covered": 93.10344827586206, "percent_covered_display": "93", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}}}, "src/lyscripts/compute/prevalences.py": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 29, 41, 44, 55, 56, 58, 63, 64, 65, 67, 73, 74, 79, 80, 82, 88, 102, 104, 107, 109, 110, 111, 112, 113, 114, 115, 118, 142, 143, 144, 146, 147, 148, 150, 157, 163, 164, 166, 172, 175, 177, 179, 180, 183, 185, 186, 187, 188, 190, 191, 192, 194, 203, 204, 206, 216, 221, 222, 223, 224, 233], "summary": {"covered_lines": 75, "num_statements": 82, "percent_covered": 91.46341463414635, "percent_covered_display": "91", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [59, 60, 61, 95, 100, 234, 235], "excluded_lines": [], "functions": {"compute_prevalences": {"executed_lines": [55, 56, 58, 63, 64, 65, 67, 73, 74, 79, 80, 82, 88, 102, 104], "summary": {"covered_lines": 15, "num_statements": 20, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [59, 60, 61, 95, 100], "excluded_lines": []}, "generate_query_from_diagnosis": {"executed_lines": [109, 110, 111, 112, 113, 114, 115], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "observe_prevalence": {"executed_lines": [142, 143, 144, 146, 147, 148, 150, 157], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PrevalencesCLI.cli_cmd": {"executed_lines": [179, 180, 183, 185, 186, 187, 188, 190, 191, 192, 194, 203, 204, 206, 216, 221, 222, 223, 224], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 29, 41, 44, 107, 118, 163, 164, 166, 172, 175, 177, 233], "summary": {"covered_lines": 26, "num_statements": 28, "percent_covered": 92.85714285714286, "percent_covered_display": "93", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [234, 235], "excluded_lines": []}}, "classes": {"PrevalencesCLI": {"executed_lines": [179, 180, 183, 185, 186, 187, 188, 190, 191, 192, 194, 203, 204, 206, 216, 221, 222, 223, 224], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 29, 41, 44, 55, 56, 58, 63, 64, 65, 67, 73, 74, 79, 80, 82, 88, 102, 104, 107, 109, 110, 111, 112, 113, 114, 115, 118, 142, 143, 144, 146, 147, 148, 150, 157, 163, 164, 166, 172, 175, 177, 233], "summary": {"covered_lines": 56, "num_statements": 63, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [59, 60, 61, 95, 100, 234, 235], "excluded_lines": []}}}, "src/lyscripts/compute/priors.py": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 42, 43, 44, 46, 52, 53, 60, 63, 64, 66, 68, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106, 109], "summary": {"covered_lines": 33, "num_statements": 35, "percent_covered": 94.28571428571429, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": [], "functions": {"compute_priors": {"executed_lines": [42, 43, 44, 46, 52, 53, 60], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "PriorsCLI.cli_cmd": {"executed_lines": [84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 63, 64, 66, 68, 109], "summary": {"covered_lines": 14, "num_statements": 16, "percent_covered": 87.5, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": []}}, "classes": {"PriorsCLI": {"executed_lines": [84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 105, 106], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 12, 14, 15, 16, 23, 26, 42, 43, 44, 46, 52, 53, 60, 63, 64, 66, 68, 109], "summary": {"covered_lines": 21, "num_statements": 23, "percent_covered": 91.30434782608695, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [110, 111], "excluded_lines": []}}}, "src/lyscripts/compute/risks.py": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 51, "percent_covered": 35.294117647058826, "percent_covered_display": "35", "missing_lines": 33, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65, 81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135, 139, 140], "excluded_lines": [], "functions": {"compute_risks": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 9, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 9, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65], "excluded_lines": []}, "RisksCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 22, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 22, "excluded_lines": 0}, "missing_lines": [81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135], "excluded_lines": []}, "": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 20, "percent_covered": 90.0, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [139, 140], "excluded_lines": []}}, "classes": {"RisksCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 22, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 22, "excluded_lines": 0}, "missing_lines": [81, 82, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 106, 107, 109, 119, 120, 122, 132, 133, 134, 135], "excluded_lines": []}, "": {"executed_lines": [1, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 29, 32, 68, 69, 71, 77, 79, 138], "summary": {"covered_lines": 18, "num_statements": 29, "percent_covered": 62.06896551724138, "percent_covered_display": "62", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [47, 48, 49, 50, 52, 53, 55, 61, 65, 139, 140], "excluded_lines": []}}}, "src/lyscripts/compute/utils.py": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 49, 55, 57, 58, 59, 60, 62, 63, 66, 68, 69, 70, 71, 72, 73, 74, 77, 92, 94, 97, 98, 99, 101, 104, 106, 107, 108, 111, 112, 115, 116, 118, 121, 128, 145, 148, 149, 151, 153, 155, 156, 158, 159, 161, 163, 165, 166, 168, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 187, 189, 191, 194, 210, 211, 212, 213, 214, 215, 216, 217, 219, 222, 239, 242, 243, 244, 246, 247, 248, 249, 252, 254, 257, 259, 260, 261, 263, 264, 265, 266, 267, 269, 271, 272, 273, 275, 276], "summary": {"covered_lines": 114, "num_statements": 120, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [95, 146, 177, 188, 240, 250], "excluded_lines": [], "functions": {"is_hdf5_compatible": {"executed_lines": [49], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "to_hdf5_attrs": {"executed_lines": [57, 58, 59, 60, 62, 63], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "from_hdf5_attrs": {"executed_lines": [68, 69, 70, 71, 72, 73, 74], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "extract_modalities": {"executed_lines": [92, 94, 97, 98, 99, 101], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [95], "excluded_lines": []}, "ensure_parent_dir": {"executed_lines": [106, 107, 108], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage._get_dataset": {"executed_lines": [145, 148, 149], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [146], "excluded_lines": []}, "HDF5FileStorage.load": {"executed_lines": [153, 155, 156, 158, 159], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage.get_attrs": {"executed_lines": [163, 165, 166, 168, 169], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage.save": {"executed_lines": [173, 175, 176, 178, 180], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [177], "excluded_lines": []}, "HDF5FileStorage.set_attrs": {"executed_lines": [184, 186, 187, 189, 191], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [188], "excluded_lines": []}, "reduce_pattern": {"executed_lines": [210, 211, 212, 213, 214, 215, 216, 217, 219], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "complete_pattern": {"executed_lines": [239, 242, 243, 244, 246, 247, 248, 249, 252, 254], "summary": {"covered_lines": 10, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [240, 250], "excluded_lines": []}, "get_cached": {"executed_lines": [259, 260, 261, 263, 264, 275, 276], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_cached.log_cache_info_wrapper": {"executed_lines": [265, 266, 267, 269, 271, 272, 273], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 55, 66, 77, 104, 111, 112, 115, 116, 118, 121, 128, 151, 161, 171, 182, 194, 222, 257], "summary": {"covered_lines": 35, "num_statements": 35, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"BaseComputeCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "HDF5FileStorage": {"executed_lines": [145, 148, 149, 153, 155, 156, 158, 159, 163, 165, 166, 168, 169, 173, 175, 176, 178, 180, 184, 186, 187, 189, 191], "summary": {"covered_lines": 23, "num_statements": 26, "percent_covered": 88.46153846153847, "percent_covered_display": "88", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [146, 177, 188], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14, 24, 25, 27, 28, 29, 36, 40, 44, 47, 49, 55, 57, 58, 59, 60, 62, 63, 66, 68, 69, 70, 71, 72, 73, 74, 77, 92, 94, 97, 98, 99, 101, 104, 106, 107, 108, 111, 112, 115, 116, 118, 121, 128, 151, 161, 171, 182, 194, 210, 211, 212, 213, 214, 215, 216, 217, 219, 222, 239, 242, 243, 244, 246, 247, 248, 249, 252, 254, 257, 259, 260, 261, 263, 264, 265, 266, 267, 269, 271, 272, 273, 275, 276], "summary": {"covered_lines": 91, "num_statements": 94, "percent_covered": 96.80851063829788, "percent_covered_display": "97", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [95, 240, 250], "excluded_lines": []}}}, "src/lyscripts/configs.py": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 89, 92, 94, 96, 102, 104, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 161, 162, 164, 167, 168, 170, 171, 176, 177, 179, 182, 186, 187, 189, 190, 202, 204, 205, 206, 208, 209, 211, 214, 216, 219, 220, 222, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288, 296, 298, 299, 306, 307, 316, 321, 325, 332, 336, 341, 343, 348, 349, 350, 355, 357, 359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 457, 464, 466, 467, 469, 470, 475, 476, 478, 482, 487, 491, 495, 496, 498, 500, 501, 503, 505, 511, 513, 519, 521, 522, 523, 524, 525, 526, 529, 550, 551, 553, 554, 555, 561, 562, 565, 572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595, 598, 604, 605, 606, 608, 609, 610, 612, 613, 616, 640, 643, 644, 657, 672, 673, 675, 677, 678, 679, 684, 686, 688, 692, 693, 699, 701, 703, 713, 714, 716, 718, 728, 729, 738, 743, 744], "summary": {"covered_lines": 228, "num_statements": 255, "percent_covered": 89.41176470588235, "percent_covered_display": "89", "missing_lines": 27, "excluded_lines": 0}, "missing_lines": [90, 122, 165, 173, 217, 271, 277, 278, 286, 472, 506, 507, 509, 514, 585, 624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637, 680], "excluded_lines": [], "functions": {"DataConfig.load": {"executed_lines": [89, 92], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [90], "excluded_lines": []}, "DataConfig.get_load_kwargs": {"executed_lines": [96], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_pattern": {"executed_lines": [104], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DiagnosisConfig.to_involvement": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [122], "excluded_lines": []}, "retrieve_graph_representation": {"executed_lines": [161, 162, 164, 167, 168, 170, 171], "summary": {"covered_lines": 7, "num_statements": 9, "percent_covered": 77.77777777777777, "percent_covered_display": "78", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [165, 173], "excluded_lines": []}, "GraphConfig.from_model": {"executed_lines": [189, 190], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "has_model_symbol": {"executed_lines": [204, 205, 206, 208, 209, 211], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_symmetry_kwargs": {"executed_lines": [216, 219, 220, 222], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [217], "excluded_lines": []}, "ModelConfig.from_model": {"executed_lines": [261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288], "summary": {"covered_lines": 11, "num_statements": 15, "percent_covered": 73.33333333333333, "percent_covered_display": "73", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [271, 277, 278, 286], "excluded_lines": []}, "modalityconfig_from_model": {"executed_lines": [298, 299], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DeprecatedModelConfig.model_post_init": {"executed_lines": [343, 348, 349, 350, 355], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DeprecatedModelConfig.translate": {"executed_lines": [359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387], "summary": {"covered_lines": 14, "num_statements": 14, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SamplingConfig.load": {"executed_lines": [457], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "map_to_optional_bool": {"executed_lines": [466, 467, 469, 470], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [472], "excluded_lines": []}, "ScenarioConfig.model_post_init": {"executed_lines": [500, 501], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ScenarioConfig.interpolate": {"executed_lines": [505], "summary": {"covered_lines": 1, "num_statements": 4, "percent_covered": 25.0, "percent_covered_display": "25", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [506, 507, 509], "excluded_lines": []}, "ScenarioConfig.normalize": {"executed_lines": [513], "summary": {"covered_lines": 1, "num_statements": 2, "percent_covered": 50.0, "percent_covered_display": "50", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [514], "excluded_lines": []}, "_construct_model_from_external": {"executed_lines": [521, 522, 523, 524, 525, 526], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "construct_model": {"executed_lines": [550, 551, 553, 554, 555, 561, 562], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "add_distributions": {"executed_lines": [572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595], "summary": {"covered_lines": 17, "num_statements": 18, "percent_covered": 94.44444444444444, "percent_covered_display": "94", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [585], "excluded_lines": []}, "add_modalities": {"executed_lines": [604, 605, 606, 608, 609, 610, 612, 613], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "add_data": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__init__": {"executed_lines": [672, 673], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DynamicYamlConfigSettingsSource._read_file": {"executed_lines": [677, 678, 679, 684], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [680], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__call__": {"executed_lines": [688, 692, 693, 699], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DynamicYamlConfigSettingsSource.__repr__": {"executed_lines": [703], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BaseCLI.settings_customise_sources": {"executed_lines": [738, 743, 744], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 94, 102, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 176, 177, 179, 182, 186, 187, 202, 214, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 296, 306, 307, 316, 321, 325, 332, 336, 341, 357, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 464, 475, 476, 478, 482, 487, 491, 495, 496, 498, 503, 511, 519, 529, 565, 598, 616, 640, 643, 644, 657, 675, 686, 701, 713, 714, 716, 718, 728, 729], "summary": {"covered_lines": 112, "num_statements": 112, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"CrossValidationConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DataConfig": {"executed_lines": [89, 92, 96], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [90], "excluded_lines": []}, "DiagnosisConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [122], "excluded_lines": []}, "DistributionConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "InvolvementConfig": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "GraphConfig": {"executed_lines": [189, 190], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ModelConfig": {"executed_lines": [261, 270, 273, 275, 276, 280, 281, 282, 283, 285, 288], "summary": {"covered_lines": 11, "num_statements": 15, "percent_covered": 73.33333333333333, "percent_covered_display": "73", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [271, 277, 278, 286], "excluded_lines": []}, "DeprecatedModelConfig": {"executed_lines": [343, 348, 349, 350, 355, 359, 360, 362, 363, 364, 366, 367, 368, 370, 372, 379, 380, 381, 387], "summary": {"covered_lines": 19, "num_statements": 19, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SamplingConfig": {"executed_lines": [457], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ScenarioConfig": {"executed_lines": [500, 501, 505, 513], "summary": {"covered_lines": 4, "num_statements": 8, "percent_covered": 50.0, "percent_covered_display": "50", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [506, 507, 509, 514], "excluded_lines": []}, "DynamicYamlConfigSettingsSource": {"executed_lines": [672, 673, 677, 678, 679, 684, 688, 692, 693, 699, 703], "summary": {"covered_lines": 11, "num_statements": 12, "percent_covered": 91.66666666666667, "percent_covered_display": "92", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [680], "excluded_lines": []}, "BaseCLI": {"executed_lines": [738, 743, 744], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 39, 44, 46, 48, 51, 56, 57, 59, 63, 69, 70, 72, 78, 82, 87, 94, 102, 104, 107, 108, 110, 115, 120, 128, 129, 131, 135, 139, 145, 146, 148, 153, 159, 161, 162, 164, 167, 168, 170, 171, 176, 177, 179, 182, 186, 187, 202, 204, 205, 206, 208, 209, 211, 214, 216, 219, 220, 222, 225, 226, 228, 232, 236, 240, 244, 253, 258, 259, 296, 298, 299, 306, 307, 316, 321, 325, 332, 336, 341, 357, 390, 391, 393, 396, 400, 404, 412, 416, 420, 424, 431, 435, 439, 443, 451, 464, 466, 467, 469, 470, 475, 476, 478, 482, 487, 491, 495, 496, 498, 503, 511, 519, 521, 522, 523, 524, 525, 526, 529, 550, 551, 553, 554, 555, 561, 562, 565, 572, 573, 574, 576, 578, 579, 580, 581, 582, 583, 587, 588, 589, 590, 592, 594, 595, 598, 604, 605, 606, 608, 609, 610, 612, 613, 616, 640, 643, 644, 657, 675, 686, 701, 713, 714, 716, 718, 728, 729], "summary": {"covered_lines": 174, "num_statements": 190, "percent_covered": 91.57894736842105, "percent_covered_display": "92", "missing_lines": 16, "excluded_lines": 0}, "missing_lines": [165, 173, 217, 472, 585, 624, 625, 627, 628, 629, 631, 632, 633, 635, 636, 637], "excluded_lines": []}}}, "src/lyscripts/data/__init__.py": {"executed_lines": [1, 17, 19, 29, 32, 33, 35, 36, 37, 38, 39, 40, 41, 43], "summary": {"covered_lines": 12, "num_statements": 13, "percent_covered": 92.3076923076923, "percent_covered_display": "92", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [45], "excluded_lines": [], "functions": {"DataCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [45], "excluded_lines": []}, "": {"executed_lines": [1, 17, 19, 29, 32, 33, 35, 36, 37, 38, 39, 40, 41, 43], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"DataCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [45], "excluded_lines": []}, "": {"executed_lines": [1, 17, 19, 29, 32, 33, 35, 36, 37, 38, 39, 40, 41, 43], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/data/__main__.py": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 15, 20, 21, 25, 26, 27, 28, 29, 31, 32, 35, 36], "excluded_lines": [], "functions": {"main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 10, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 10, "excluded_lines": 0}, "missing_lines": [15, 20, 21, 25, 26, 27, 28, 29, 31, 32], "excluded_lines": []}, "": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 8, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 35, 36], "excluded_lines": []}}, "classes": {"": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 18, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [3, 5, 6, 7, 10, 13, 15, 20, 21, 25, 26, 27, 28, 29, 31, 32, 35, 36], "excluded_lines": []}}}, "src/lyscripts/data/enhance.py": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 24, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58, 62, 63], "excluded_lines": [], "functions": {"EnhanceCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [62, 63], "excluded_lines": []}}, "classes": {"EnhanceCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [39, 41, 42, 44, 49, 58], "excluded_lines": []}, "": {"executed_lines": [1, 7, 9, 10, 11, 13, 14, 15, 18, 19, 21, 22, 23, 24, 25, 30, 32, 61], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [62, 63], "excluded_lines": []}}}, "src/lyscripts/data/fetch.py": {"executed_lines": [1, 3, 5, 6, 7, 8, 10, 11, 14, 15, 17, 24, 31, 38, 40, 55], "summary": {"covered_lines": 14, "num_statements": 21, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [42, 43, 45, 51, 52, 56, 57], "excluded_lines": [], "functions": {"FetchCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [42, 43, 45, 51, 52], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 10, 11, 14, 15, 17, 24, 31, 38, 40, 55], "summary": {"covered_lines": 14, "num_statements": 16, "percent_covered": 87.5, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [56, 57], "excluded_lines": []}}, "classes": {"FetchCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [42, 43, 45, 51, 52], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 10, 11, 14, 15, 17, 24, 31, 38, 40, 55], "summary": {"covered_lines": 14, "num_statements": 16, "percent_covered": 87.5, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [56, 57], "excluded_lines": []}}}, "src/lyscripts/data/filter.py": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 49, "percent_covered": 38.775510204081634, "percent_covered_display": "39", "missing_lines": 30, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66, 76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94, 98, 99], "excluded_lines": [], "functions": {"FilterCLI.model_post_init": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66], "excluded_lines": []}, "FilterCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 11, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 21, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [98, 99], "excluded_lines": []}}, "classes": {"FilterCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 28, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 28, "excluded_lines": 0}, "missing_lines": [43, 44, 45, 46, 47, 49, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 66, 76, 78, 79, 84, 85, 87, 88, 89, 91, 92, 94], "excluded_lines": []}, "": {"executed_lines": [1, 7, 8, 10, 11, 12, 13, 15, 16, 17, 20, 21, 23, 24, 28, 35, 38, 39, 41, 68, 97], "summary": {"covered_lines": 19, "num_statements": 21, "percent_covered": 90.47619047619048, "percent_covered_display": "90", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [98, 99], "excluded_lines": []}}}, "src/lyscripts/data/generate.py": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 55, 56, 57, 60, 62, 65, 67, 76, 78, 79, 80, 81, 82, 84, 89, 91, 94], "summary": {"covered_lines": 35, "num_statements": 39, "percent_covered": 89.74358974358974, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [58, 63, 95, 96], "excluded_lines": [], "functions": {"GenerateCLI.model_post_init": {"executed_lines": [55, 56, 57, 60, 62, 65], "summary": {"covered_lines": 6, "num_statements": 8, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [58, 63], "excluded_lines": []}, "GenerateCLI.cli_cmd": {"executed_lines": [76, 78, 79, 80, 81, 82, 84, 89, 91], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 67, 94], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [95, 96], "excluded_lines": []}}, "classes": {"GenerateCLI": {"executed_lines": [55, 56, 57, 60, 62, 65, 76, 78, 79, 80, 81, 82, 84, 89, 91], "summary": {"covered_lines": 15, "num_statements": 17, "percent_covered": 88.23529411764706, "percent_covered_display": "88", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [58, 63], "excluded_lines": []}, "": {"executed_lines": [1, 11, 12, 13, 14, 16, 17, 26, 29, 30, 32, 33, 34, 41, 47, 48, 49, 50, 51, 53, 67, 94], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [95, 96], "excluded_lines": []}}}, "src/lyscripts/data/join.py": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 76], "summary": {"covered_lines": 11, "num_statements": 20, "percent_covered": 55.0, "percent_covered_display": "55", "missing_lines": 9, "excluded_lines": 0}, "missing_lines": [60, 62, 63, 64, 65, 67, 73, 77, 78], "excluded_lines": [], "functions": {"JoinCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [60, 62, 63, 64, 65, 67, 73], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 76], "summary": {"covered_lines": 11, "num_statements": 13, "percent_covered": 84.61538461538461, "percent_covered_display": "85", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [77, 78], "excluded_lines": []}}, "classes": {"JoinCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 7, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 7, "excluded_lines": 0}, "missing_lines": [60, 62, 63, 64, 65, 67, 73], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 9, 10, 13, 14, 16, 17, 19, 76], "summary": {"covered_lines": 11, "num_statements": 13, "percent_covered": 84.61538461538461, "percent_covered_display": "85", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [77, 78], "excluded_lines": []}}}, "src/lyscripts/data/lyproxify.py": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 29, 37, 49, 50, 52, 53, 57, 67, 74, 78, 80, 122, 123, 126, 145, 162, 163, 164, 165, 167, 169, 176, 199, 200, 201, 202, 203, 204, 205, 206, 208, 210, 213, 285, 310, 327, 328, 330, 331, 332, 334, 335, 336, 339], "summary": {"covered_lines": 56, "num_statements": 123, "percent_covered": 45.52845528455285, "percent_covered_display": "46", "missing_lines": 67, "excluded_lines": 0}, "missing_lines": [31, 32, 34, 39, 40, 41, 43, 44, 46, 90, 92, 96, 102, 103, 104, 105, 106, 108, 109, 110, 111, 113, 114, 116, 117, 119, 132, 134, 135, 136, 141, 142, 173, 250, 252, 253, 255, 256, 258, 259, 260, 261, 262, 263, 264, 265, 267, 268, 271, 272, 276, 281, 282, 293, 294, 295, 297, 298, 299, 300, 302, 303, 304, 306, 307, 340, 341], "excluded_lines": [], "functions": {"ensure_python_file": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [31, 32, 34], "excluded_lines": []}, "ensure_column_map": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [39, 40, 41, 43, 44, 46], "excluded_lines": []}, "LyproxifyCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [90, 92, 96, 102, 103, 104, 105, 106, 108, 109, 110, 111, 113, 114, 116, 117, 119], "excluded_lines": []}, "clean_header": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [132, 134, 135, 136, 141, 142], "excluded_lines": []}, "get_instruction_depth": {"executed_lines": [162, 163, 164, 165, 167, 169], "summary": {"covered_lines": 6, "num_statements": 7, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [173], "excluded_lines": []}, "generate_markdown_docs": {"executed_lines": [199, 200, 201, 202, 203, 204, 205, 206, 208, 210], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "transform_to_lyprox": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 20, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 20, "excluded_lines": 0}, "missing_lines": [250, 252, 253, 255, 256, 258, 259, 260, 261, 262, 263, 264, 265, 267, 268, 271, 272, 276, 281, 282], "excluded_lines": []}, "leftright_to_ipsicontra": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [293, 294, 295, 297, 298, 299, 300, 302, 303, 304, 306, 307], "excluded_lines": []}, "exclude_patients": {"executed_lines": [327, 328, 330, 331, 332, 334, 335, 336], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 29, 37, 49, 50, 52, 53, 57, 67, 74, 78, 80, 122, 123, 126, 145, 176, 213, 285, 310, 339], "summary": {"covered_lines": 32, "num_statements": 34, "percent_covered": 94.11764705882354, "percent_covered_display": "94", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [340, 341], "excluded_lines": []}}, "classes": {"LyproxifyCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 17, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 17, "excluded_lines": 0}, "missing_lines": [90, 92, 96, 102, 103, 104, 105, 106, 108, 109, 110, 111, 113, 114, 116, 117, 119], "excluded_lines": []}, "ParsingError": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 10, 11, 12, 13, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 29, 37, 49, 50, 52, 53, 57, 67, 74, 78, 80, 122, 123, 126, 145, 162, 163, 164, 165, 167, 169, 176, 199, 200, 201, 202, 203, 204, 205, 206, 208, 210, 213, 285, 310, 327, 328, 330, 331, 332, 334, 335, 336, 339], "summary": {"covered_lines": 56, "num_statements": 106, "percent_covered": 52.83018867924528, "percent_covered_display": "53", "missing_lines": 50, "excluded_lines": 0}, "missing_lines": [31, 32, 34, 39, 40, 41, 43, 44, 46, 132, 134, 135, 136, 141, 142, 173, 250, 252, 253, 255, 256, 258, 259, 260, 261, 262, 263, 264, 265, 267, 268, 271, 272, 276, 281, 282, 293, 294, 295, 297, 298, 299, 300, 302, 303, 304, 306, 307, 340, 341], "excluded_lines": []}}}, "src/lyscripts/data/split.py": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 30, "percent_covered": 53.333333333333336, "percent_covered_display": "53", "missing_lines": 14, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65, 72, 73], "excluded_lines": [], "functions": {"SplitCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [72, 73], "excluded_lines": []}}, "classes": {"SplitCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 12, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 12, "excluded_lines": 0}, "missing_lines": [33, 35, 36, 38, 40, 46, 50, 51, 54, 59, 61, 65], "excluded_lines": []}, "": {"executed_lines": [1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 18, 19, 21, 22, 23, 25, 71], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [72, 73], "excluded_lines": []}}}, "src/lyscripts/data/utils.py": {"executed_lines": [1, 3, 5, 6, 8, 11, 12, 14, 15, 16], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": [], "functions": {"save_table_to_csv": {"executed_lines": [14, 15, 16], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 8, 11, 12], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 3, 5, 6, 8, 11, 12, 14, 15, 16], "summary": {"covered_lines": 9, "num_statements": 9, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}}, "src/lyscripts/decorators.py": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 18, 19, 20, 23, 30, 33, 34, 36, 37, 38, 39, 41, 42, 51, 57, 59, 62, 65, 66, 68, 69, 72, 74, 77, 80, 81, 83, 84, 86, 88], "summary": {"covered_lines": 37, "num_statements": 41, "percent_covered": 90.2439024390244, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [53, 54, 55, 70], "excluded_lines": [], "functions": {"assemble_signature": {"executed_lines": [18, 19, 20], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state": {"executed_lines": [30, 59], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state.log_decorator": {"executed_lines": [33, 34, 57], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_state.log_decorator.wrapper": {"executed_lines": [36, 37, 38, 39, 41, 42, 51], "summary": {"covered_lines": 7, "num_statements": 10, "percent_covered": 70.0, "percent_covered_display": "70", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [53, 54, 55], "excluded_lines": []}, "check_input_file_exists": {"executed_lines": [65, 66, 74], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_input_file_exists.inner": {"executed_lines": [68, 69, 72], "summary": {"covered_lines": 3, "num_statements": 4, "percent_covered": 75.0, "percent_covered_display": "75", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [70], "excluded_lines": []}, "check_output_dir_exists": {"executed_lines": [80, 81, 88], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "check_output_dir_exists.inner": {"executed_lines": [83, 84, 86], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 23, 62, 77], "summary": {"covered_lines": 10, "num_statements": 10, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 8, 9, 10, 11, 12, 13, 16, 18, 19, 20, 23, 30, 33, 34, 36, 37, 38, 39, 41, 42, 51, 57, 59, 62, 65, 66, 68, 69, 72, 74, 77, 80, 81, 83, 84, 86, 88], "summary": {"covered_lines": 37, "num_statements": 41, "percent_covered": 90.2439024390244, "percent_covered_display": "90", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [53, 54, 55, 70], "excluded_lines": []}}}, "src/lyscripts/evaluate.py": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 75, "percent_covered": 24.0, "percent_covered_display": "24", "missing_lines": 57, "excluded_lines": 0}, "missing_lines": [29, 35, 43, 48, 50, 57, 63, 70, 87, 104, 105, 106, 107, 108, 109, 120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141, 146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204, 208, 209, 211, 212], "excluded_lines": [], "functions": {"_add_parser": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [29, 35], "excluded_lines": []}, "_add_arguments": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [43, 48, 50, 57, 63, 70], "excluded_lines": []}, "comp_bic": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [87], "excluded_lines": []}, "compute_evidence": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 6, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 6, "excluded_lines": 0}, "missing_lines": [104, 105, 106, 107, 108, 109], "excluded_lines": []}, "compute_ti_results": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 14, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 14, "excluded_lines": 0}, "missing_lines": [120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141], "excluded_lines": []}, "main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 24, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 24, "excluded_lines": 0}, "missing_lines": [146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204], "excluded_lines": []}, "": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 22, "percent_covered": 81.81818181818181, "percent_covered_display": "82", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [208, 209, 211, 212], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 21, 24, 38, 73, 90, 112, 144, 207], "summary": {"covered_lines": 18, "num_statements": 75, "percent_covered": 24.0, "percent_covered_display": "24", "missing_lines": 57, "excluded_lines": 0}, "missing_lines": [29, 35, 43, 48, 50, 57, 63, 70, 87, 104, 105, 106, 107, 108, 109, 120, 121, 123, 124, 129, 130, 131, 133, 134, 135, 137, 138, 139, 141, 146, 148, 149, 150, 151, 152, 155, 156, 163, 169, 171, 181, 182, 185, 186, 187, 190, 191, 193, 198, 199, 201, 202, 204, 208, 209, 211, 212], "excluded_lines": []}}}, "src/lyscripts/plots.py": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 61, 64, 66, 69, 74, 75, 76, 77, 78, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 112, 115, 116, 118, 120, 121, 123, 125, 126, 135, 136, 137, 138, 139, 140, 142, 144, 146, 148, 150, 152, 154, 155, 157, 158, 160, 163, 164, 166, 167, 169, 170, 179, 180, 181, 182, 183, 184, 190, 198, 199, 201, 202, 204, 206, 208, 216, 218, 225, 227, 234, 239, 240, 241, 243, 244, 246, 247, 249, 252, 268, 269, 270, 271, 273, 274, 275, 276, 279, 281, 282, 288, 289, 290, 291, 294, 303, 306, 307, 310, 311, 314, 335, 338, 340, 343, 345, 354, 355, 357, 358, 360, 363, 395, 396, 397, 402, 403, 404, 410, 411], "summary": {"covered_lines": 143, "num_statements": 166, "percent_covered": 86.144578313253, "percent_covered_display": "86", "missing_lines": 23, "excluded_lines": 0}, "missing_lines": [26, 27, 46, 47, 56, 94, 99, 104, 185, 186, 336, 341, 370, 375, 377, 378, 380, 381, 382, 383, 385, 392, 399], "excluded_lines": [], "functions": {"floor_at_decimal": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [46, 47], "excluded_lines": []}, "ceil_at_decimal": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [56], "excluded_lines": []}, "floor_to_step": {"executed_lines": [61], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ceil_to_step": {"executed_lines": [66], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "clean_and_check": {"executed_lines": [74, 75, 76, 77, 78], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AbstractDistribution.draw": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [94], "excluded_lines": []}, "AbstractDistribution.left_percentile": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [99], "excluded_lines": []}, "AbstractDistribution.right_percentile": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [104], "excluded_lines": []}, "AbstractDistribution._get_label": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AbstractDistribution.label": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.values": {"executed_lines": [123], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.from_hdf5": {"executed_lines": [135, 136, 137, 138, 139, 140], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.left_percentile": {"executed_lines": [144], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.right_percentile": {"executed_lines": [148], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "Histogram.draw": {"executed_lines": [152, 154, 155, 157, 158, 160], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.from_hdf5": {"executed_lines": [179, 180, 181, 182, 183, 184, 190], "summary": {"covered_lines": 7, "num_statements": 9, "percent_covered": 77.77777777777777, "percent_covered_display": "78", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [185, 186], "excluded_lines": []}, "BetaPosterior._get_label": {"executed_lines": [199], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.num_fail": {"executed_lines": [204], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.pdf": {"executed_lines": [208], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.left_percentile": {"executed_lines": [218], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.right_percentile": {"executed_lines": [227], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior.draw": {"executed_lines": [239, 240, 241, 243, 244, 246, 247, 249], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_size": {"executed_lines": [268, 269, 270, 271, 273, 274, 275, 276], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_label": {"executed_lines": [281, 282, 288, 289, 290, 291], "summary": {"covered_lines": 6, "num_statements": 6, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_xlims": {"executed_lines": [303, 306, 307, 310, 311], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "draw": {"executed_lines": [335, 338, 340, 343, 345, 354, 355, 357, 358, 360], "summary": {"covered_lines": 10, "num_statements": 12, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [336, 341], "excluded_lines": []}, "split_legends": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 10, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 10, "excluded_lines": 0}, "missing_lines": [370, 375, 377, 378, 380, 381, 382, 383, 385, 392], "excluded_lines": []}, "use_mpl_stylesheet": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [399], "excluded_lines": []}, "save_figure": {"executed_lines": [410, 411], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 64, 69, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 115, 116, 118, 120, 121, 125, 126, 142, 146, 150, 163, 164, 166, 167, 169, 170, 198, 201, 202, 206, 216, 225, 234, 252, 279, 294, 314, 363, 395, 396, 397, 402, 403, 404], "summary": {"covered_lines": 69, "num_statements": 71, "percent_covered": 97.1830985915493, "percent_covered_display": "97", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [26, 27], "excluded_lines": []}}, "classes": {"AbstractDistribution": {"executed_lines": [112], "summary": {"covered_lines": 1, "num_statements": 4, "percent_covered": 25.0, "percent_covered_display": "25", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [94, 99, 104], "excluded_lines": []}, "Histogram": {"executed_lines": [123, 135, 136, 137, 138, 139, 140, 144, 148, 152, 154, 155, 157, 158, 160], "summary": {"covered_lines": 15, "num_statements": 15, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "BetaPosterior": {"executed_lines": [179, 180, 181, 182, 183, 184, 190, 199, 204, 208, 218, 227, 239, 240, 241, 243, 244, 246, 247, 249], "summary": {"covered_lines": 20, "num_statements": 22, "percent_covered": 90.9090909090909, "percent_covered_display": "91", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [185, 186], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 19, 25, 30, 37, 38, 41, 50, 59, 61, 64, 66, 69, 74, 75, 76, 77, 78, 81, 84, 85, 87, 88, 89, 91, 92, 96, 97, 101, 102, 106, 109, 110, 115, 116, 118, 120, 121, 125, 126, 142, 146, 150, 163, 164, 166, 167, 169, 170, 198, 201, 202, 206, 216, 225, 234, 252, 268, 269, 270, 271, 273, 274, 275, 276, 279, 281, 282, 288, 289, 290, 291, 294, 303, 306, 307, 310, 311, 314, 335, 338, 340, 343, 345, 354, 355, 357, 358, 360, 363, 395, 396, 397, 402, 403, 404, 410, 411], "summary": {"covered_lines": 107, "num_statements": 125, "percent_covered": 85.6, "percent_covered_display": "86", "missing_lines": 18, "excluded_lines": 0}, "missing_lines": [26, 27, 46, 47, 56, 336, 341, 370, 375, 377, 378, 380, 381, 382, 383, 385, 392, 399], "excluded_lines": []}}}, "src/lyscripts/sample.py": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 68, 69, 71, 73, 75, 78, 79, 81, 83, 84, 85, 86, 89, 90, 92, 93, 95, 97, 98, 100, 101, 103, 106, 107, 109, 110, 112, 114, 115, 117, 118, 120, 123, 126, 135, 140, 141, 142, 146, 147, 148, 150, 153, 162, 163, 175, 184, 185, 187, 190, 193, 209, 215, 217, 225, 257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300, 304, 305, 307, 311, 316, 322, 325, 327, 328, 334, 347, 348, 350, 351, 352, 359, 365, 366, 368, 385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410, 419], "summary": {"covered_lines": 125, "num_statements": 136, "percent_covered": 91.91176470588235, "percent_covered_display": "92", "missing_lines": 11, "excluded_lines": 0}, "missing_lines": [35, 36, 74, 132, 172, 188, 301, 309, 313, 420, 421], "excluded_lines": [], "functions": {"CompletedItersColumn.__init__": {"executed_lines": [68, 69], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "CompletedItersColumn.render": {"executed_lines": [73, 75], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [74], "excluded_lines": []}, "ItersPerSecondColumn.render": {"executed_lines": [83, 84, 85, 86], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime.update": {"executed_lines": [97, 98], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime.relative_diff": {"executed_lines": [103], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted.update": {"executed_lines": [114, 115], "summary": {"covered_lines": 2, "num_statements": 2, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted.newly_accepted": {"executed_lines": [120], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "log_prob_fn": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [132], "excluded_lines": []}, "ensure_initial_state": {"executed_lines": [140, 141, 142, 146, 147, 148, 150], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "ensure_history_table": {"executed_lines": [162, 163], "summary": {"covered_lines": 2, "num_statements": 3, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [172], "excluded_lines": []}, "update_history_table": {"executed_lines": [184, 185, 187, 190], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [188], "excluded_lines": []}, "is_converged": {"executed_lines": [209], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "_get_columns": {"executed_lines": [217], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "run_sampling": {"executed_lines": [257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300], "summary": {"covered_lines": 19, "num_statements": 20, "percent_covered": 95.0, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [301], "excluded_lines": []}, "DummyPool.__enter__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [309], "excluded_lines": []}, "DummyPool.__exit__": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [313], "excluded_lines": []}, "get_pool": {"executed_lines": [322], "summary": {"covered_lines": 1, "num_statements": 1, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "init_sampler": {"executed_lines": [327, 328, 334], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "SampleCLI.cli_cmd": {"executed_lines": [385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 71, 78, 79, 81, 89, 90, 92, 93, 95, 100, 101, 106, 107, 109, 110, 112, 117, 118, 123, 126, 135, 153, 175, 193, 215, 225, 304, 305, 307, 311, 316, 325, 347, 348, 350, 351, 352, 359, 365, 366, 368, 419], "summary": {"covered_lines": 61, "num_statements": 65, "percent_covered": 93.84615384615384, "percent_covered_display": "94", "missing_lines": 4, "excluded_lines": 0}, "missing_lines": [35, 36, 420, 421], "excluded_lines": []}}, "classes": {"CompletedItersColumn": {"executed_lines": [68, 69, 73, 75], "summary": {"covered_lines": 4, "num_statements": 5, "percent_covered": 80.0, "percent_covered_display": "80", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [74], "excluded_lines": []}, "ItersPerSecondColumn": {"executed_lines": [83, 84, 85, 86], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "AcorTime": {"executed_lines": [97, 98, 103], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "NumAccepted": {"executed_lines": [114, 115, 120], "summary": {"covered_lines": 3, "num_statements": 3, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "DummyPool": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [309, 313], "excluded_lines": []}, "SampleCLI": {"executed_lines": [385, 387, 391, 392, 393, 394, 395, 398, 400, 401, 402, 410], "summary": {"covered_lines": 12, "num_statements": 12, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 19, 21, 22, 23, 25, 27, 29, 30, 31, 32, 34, 38, 40, 41, 42, 43, 44, 45, 46, 47, 49, 60, 63, 64, 66, 71, 78, 79, 81, 89, 90, 92, 93, 95, 100, 101, 106, 107, 109, 110, 112, 117, 118, 123, 126, 135, 140, 141, 142, 146, 147, 148, 150, 153, 162, 163, 175, 184, 185, 187, 190, 193, 209, 215, 217, 225, 257, 258, 260, 261, 262, 264, 265, 267, 268, 269, 270, 275, 277, 278, 280, 291, 297, 298, 300, 304, 305, 307, 311, 316, 322, 325, 327, 328, 334, 347, 348, 350, 351, 352, 359, 365, 366, 368, 419], "summary": {"covered_lines": 99, "num_statements": 107, "percent_covered": 92.5233644859813, "percent_covered_display": "93", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [35, 36, 132, 172, 188, 301, 420, 421], "excluded_lines": []}}}, "src/lyscripts/schedule.py": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 29, "percent_covered": 55.172413793103445, "percent_covered_display": "55", "missing_lines": 13, "excluded_lines": 0}, "missing_lines": [25, 26, 27, 35, 44, 45, 73, 75, 76, 78, 80, 84, 85], "excluded_lines": [], "functions": {"geometric_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 3, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [25, 26, 27], "excluded_lines": []}, "linear_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 1, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [35], "excluded_lines": []}, "power_schedule": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [44, 45], "excluded_lines": []}, "ScheduleCLI.cli_cmd": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [73, 75, 76, 78, 80], "excluded_lines": []}, "": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 18, "percent_covered": 88.88888888888889, "percent_covered_display": "89", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [84, 85], "excluded_lines": []}}, "classes": {"ScheduleCLI": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 5, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [73, 75, 76, 78, 80], "excluded_lines": []}, "": {"executed_lines": [1, 13, 15, 16, 17, 19, 20, 23, 30, 38, 48, 55, 56, 58, 62, 66, 71, 83], "summary": {"covered_lines": 16, "num_statements": 24, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 8, "excluded_lines": 0}, "missing_lines": [25, 26, 27, 35, 44, 45, 84, 85], "excluded_lines": []}}}, "src/lyscripts/schema.py": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 21, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [60, 61, 65], "excluded_lines": [], "functions": {"main": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 2, "percent_covered": 0.0, "percent_covered_display": "0", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [60, 61], "excluded_lines": []}, "": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 19, "percent_covered": 94.73684210526316, "percent_covered_display": "95", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [65], "excluded_lines": []}}, "classes": {"SchemaSettings": {"executed_lines": [], "summary": {"covered_lines": 0, "num_statements": 0, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "": {"executed_lines": [1, 27, 29, 30, 32, 35, 36, 38, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 58, 64], "summary": {"covered_lines": 18, "num_statements": 21, "percent_covered": 85.71428571428571, "percent_covered_display": "86", "missing_lines": 3, "excluded_lines": 0}, "missing_lines": [60, 61, 65], "excluded_lines": []}}}, "src/lyscripts/utils.py": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 23, 24, 26, 27, 30, 33, 42, 43, 45, 46, 47, 48, 50, 53, 63, 65, 66, 67, 68, 70, 72, 75, 91, 93, 94, 95, 97, 98, 100, 102, 105, 115, 117, 118, 119, 120, 122, 124, 127, 137, 138, 139, 140, 143, 146, 151, 152, 154, 155, 156, 159, 160, 162, 163, 164, 165, 168, 169, 177, 178, 179, 180, 183, 184, 192, 193, 195, 199], "summary": {"covered_lines": 79, "num_statements": 84, "percent_covered": 94.04761904761905, "percent_covered_display": "94", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [25, 141, 142, 196, 197], "excluded_lines": [], "functions": {"binom_pmf": {"executed_lines": [23, 24, 26, 27, 30], "summary": {"covered_lines": 5, "num_statements": 6, "percent_covered": 83.33333333333333, "percent_covered_display": "83", "missing_lines": 1, "excluded_lines": 0}, "missing_lines": [25], "excluded_lines": []}, "get_dict_depth": {"executed_lines": [42, 43, 45, 46, 47, 48, 50], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "delete_private_keys": {"executed_lines": [63, 65, 66, 67, 68, 70, 72], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "flatten": {"executed_lines": [91, 93, 94, 95, 97, 98, 100, 102], "summary": {"covered_lines": 8, "num_statements": 8, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "unflatten": {"executed_lines": [115, 117, 118, 119, 120, 122, 124], "summary": {"covered_lines": 7, "num_statements": 7, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_modalities_subset": {"executed_lines": [137, 138, 139, 140, 143], "summary": {"covered_lines": 5, "num_statements": 7, "percent_covered": 71.42857142857143, "percent_covered_display": "71", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [141, 142], "excluded_lines": []}, "load_patient_data": {"executed_lines": [151, 152, 154, 155, 156], "summary": {"covered_lines": 5, "num_statements": 5, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "load_yaml_params": {"executed_lines": [162, 163, 164, 165], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "load_model_samples": {"executed_lines": [177, 178, 179, 180], "summary": {"covered_lines": 4, "num_statements": 4, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}, "get_hdf5_backend": {"executed_lines": [192, 193, 195, 199], "summary": {"covered_lines": 4, "num_statements": 6, "percent_covered": 66.66666666666667, "percent_covered_display": "67", "missing_lines": 2, "excluded_lines": 0}, "missing_lines": [196, 197], "excluded_lines": []}, "": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 33, 53, 75, 105, 127, 146, 159, 160, 168, 169, 183, 184], "summary": {"covered_lines": 23, "num_statements": 23, "percent_covered": 100.0, "percent_covered_display": "100", "missing_lines": 0, "excluded_lines": 0}, "missing_lines": [], "excluded_lines": []}}, "classes": {"": {"executed_lines": [1, 3, 5, 6, 7, 8, 9, 10, 11, 13, 18, 21, 23, 24, 26, 27, 30, 33, 42, 43, 45, 46, 47, 48, 50, 53, 63, 65, 66, 67, 68, 70, 72, 75, 91, 93, 94, 95, 97, 98, 100, 102, 105, 115, 117, 118, 119, 120, 122, 124, 127, 137, 138, 139, 140, 143, 146, 151, 152, 154, 155, 156, 159, 160, 162, 163, 164, 165, 168, 169, 177, 178, 179, 180, 183, 184, 192, 193, 195, 199], "summary": {"covered_lines": 79, "num_statements": 84, "percent_covered": 94.04761904761905, "percent_covered_display": "94", "missing_lines": 5, "excluded_lines": 0}, "missing_lines": [25, 141, 142, 196, 197], "excluded_lines": []}}}}, "totals": {"covered_lines": 1178, "num_statements": 1577, "percent_covered": 74.6987951807229, "percent_covered_display": "75", "missing_lines": 399, "excluded_lines": 0}}, "coverage_path": "."}
\ No newline at end of file
diff --git a/htmlcov/class_index.html b/htmlcov/class_index.html
index 7e51ec3..ba51c2d 100644
--- a/htmlcov/class_index.html
+++ b/htmlcov/class_index.html
@@ -54,8 +54,8 @@
- coverage.py v7.9.1,
- created at 2025-06-26 14:44 +0000
+ coverage.py v7.9.2,
+ created at 2025-07-23 06:01 +0000
@@ -153,8 +153,8 @@
| 93% |
- | src/lyscripts/compute/prevalences.py |
- PrevalencesCLI |
+ src/lyscripts/compute/prevalences.py |
+ PrevalencesCLI |
19 |
0 |
0 |
@@ -163,10 +163,10 @@
| src/lyscripts/compute/prevalences.py |
(no class) |
- 61 |
+ 63 |
7 |
0 |
- 89% |
+ 89% |
| src/lyscripts/compute/priors.py |
@@ -297,8 +297,8 @@
| 92% |
- | src/lyscripts/data/__init__.py |
- DataCLI |
+ src/lyscripts/data/__init__.py |
+ DataCLI |
1 |
1 |
0 |
@@ -307,10 +307,10 @@
| src/lyscripts/data/__init__.py |
(no class) |
- 11 |
+ 12 |
0 |
0 |
- 100% |
+ 100% |
| src/lyscripts/data/__main__.py |
@@ -336,6 +336,22 @@
| 0 |
89% |
+
+ | src/lyscripts/data/fetch.py |
+ FetchCLI |
+ 5 |
+ 5 |
+ 0 |
+ 0% |
+
+
+ | src/lyscripts/data/fetch.py |
+ (no class) |
+ 16 |
+ 2 |
+ 0 |
+ 88% |
+
| src/lyscripts/data/filter.py |
FilterCLI |
@@ -385,8 +401,8 @@
| 85% |
- | src/lyscripts/data/lyproxify.py |
- LyproxifyCLI |
+ src/lyscripts/data/lyproxify.py |
+ LyproxifyCLI |
17 |
17 |
0 |
@@ -395,10 +411,10 @@
| src/lyscripts/data/lyproxify.py |
(no class) |
- 104 |
+ 106 |
50 |
0 |
- 52% |
+ 53% |
| src/lyscripts/data/split.py |
@@ -565,10 +581,10 @@
| Total |
|
- 1551 |
- 392 |
+ 1577 |
+ 399 |
0 |
- 75% |
+ 75% |
@@ -580,8 +596,8 @@
- | src/lyscripts/compute/prevalences.py |
- compute_prevalences |
+ src/lyscripts/compute/prevalences.py |
+ compute_prevalences |
20 |
5 |
0 |
75% |
- | src/lyscripts/compute/prevalences.py |
- generate_query_from_diagnosis |
+ src/lyscripts/compute/prevalences.py |
+ generate_query_from_diagnosis |
7 |
0 |
0 |
100% |
- | src/lyscripts/compute/prevalences.py |
- observe_prevalence |
- 7 |
+ src/lyscripts/compute/prevalences.py |
+ observe_prevalence |
+ 8 |
0 |
0 |
- 100% |
+ 100% |
- | src/lyscripts/compute/prevalences.py |
- PrevalencesCLI.cli_cmd |
+ src/lyscripts/compute/prevalences.py |
+ PrevalencesCLI.cli_cmd |
19 |
0 |
0 |
@@ -235,10 +235,10 @@
| src/lyscripts/compute/prevalences.py |
(no function) |
- 27 |
+ 28 |
2 |
0 |
- 93% |
+ 93% |
| src/lyscripts/compute/priors.py |
@@ -633,8 +633,8 @@
| 100% |
- | src/lyscripts/data/__init__.py |
- DataCLI.cli_cmd |
+ src/lyscripts/data/__init__.py |
+ DataCLI.cli_cmd |
1 |
1 |
0 |
@@ -643,10 +643,10 @@
| src/lyscripts/data/__init__.py |
(no function) |
- 11 |
+ 12 |
0 |
0 |
- 100% |
+ 100% |
| src/lyscripts/data/__main__.py |
@@ -680,6 +680,22 @@
| 0 |
89% |
+
+ | src/lyscripts/data/fetch.py |
+ FetchCLI.cli_cmd |
+ 5 |
+ 5 |
+ 0 |
+ 0% |
+
+
+ | src/lyscripts/data/fetch.py |
+ (no function) |
+ 16 |
+ 2 |
+ 0 |
+ 88% |
+
| src/lyscripts/data/filter.py |
FilterCLI.model_post_init |
@@ -745,72 +761,72 @@
| 85% |
- | src/lyscripts/data/lyproxify.py |
- ensure_python_file |
+ src/lyscripts/data/lyproxify.py |
+ ensure_python_file |
3 |
3 |
0 |
0% |
- | src/lyscripts/data/lyproxify.py |
- ensure_column_map |
+ src/lyscripts/data/lyproxify.py |
+ ensure_column_map |
6 |
6 |
0 |
0% |
- | src/lyscripts/data/lyproxify.py |
- LyproxifyCLI.cli_cmd |
+ src/lyscripts/data/lyproxify.py |
+ LyproxifyCLI.cli_cmd |
17 |
17 |
0 |
0% |
- | src/lyscripts/data/lyproxify.py |
- clean_header |
+ src/lyscripts/data/lyproxify.py |
+ clean_header |
6 |
6 |
0 |
0% |
- | src/lyscripts/data/lyproxify.py |
- get_instruction_depth |
+ src/lyscripts/data/lyproxify.py |
+ get_instruction_depth |
7 |
1 |
0 |
86% |
- | src/lyscripts/data/lyproxify.py |
- generate_markdown_docs |
+ src/lyscripts/data/lyproxify.py |
+ generate_markdown_docs |
10 |
0 |
0 |
100% |
- | src/lyscripts/data/lyproxify.py |
- transform_to_lyprox |
+ src/lyscripts/data/lyproxify.py |
+ transform_to_lyprox |
20 |
20 |
0 |
0% |
- | src/lyscripts/data/lyproxify.py |
- leftright_to_ipsicontra |
+ src/lyscripts/data/lyproxify.py |
+ leftright_to_ipsicontra |
12 |
12 |
0 |
0% |
- | src/lyscripts/data/lyproxify.py |
- exclude_patients |
+ src/lyscripts/data/lyproxify.py |
+ exclude_patients |
8 |
0 |
0 |
@@ -819,10 +835,10 @@
| src/lyscripts/data/lyproxify.py |
(no function) |
- 32 |
+ 34 |
2 |
0 |
- 94% |
+ 94% |
| src/lyscripts/data/split.py |
@@ -1525,10 +1541,10 @@
| Total |
|
- 1551 |
- 392 |
+ 1577 |
+ 399 |
0 |
- 75% |
+ 75% |
@@ -1540,8 +1556,8 @@
| src/lyscripts/compute/prevalences.py |
- 80 |
+ 82 |
7 |
0 |
- 91% |
+ 91% |
| src/lyscripts/compute/priors.py |
@@ -156,10 +156,10 @@
| src/lyscripts/data/__init__.py |
- 12 |
+ 13 |
1 |
0 |
- 92% |
+ 92% |
| src/lyscripts/data/__main__.py |
@@ -175,6 +175,13 @@
| 0 |
67% |
+
+ | src/lyscripts/data/fetch.py |
+ 21 |
+ 7 |
+ 0 |
+ 67% |
+
| src/lyscripts/data/filter.py |
49 |
@@ -198,10 +205,10 @@
| src/lyscripts/data/lyproxify.py |
- 121 |
+ 123 |
67 |
0 |
- 45% |
+ 46% |
| src/lyscripts/data/split.py |
@@ -270,10 +277,10 @@
| Total |
- 1551 |
- 392 |
+ 1577 |
+ 399 |
0 |
- 75% |
+ 75% |
@@ -284,8 +291,8 @@
diff --git a/htmlcov/z_055061514423972c___main___py.html b/htmlcov/z_055061514423972c___main___py.html
index fa31306..ae106e2 100644
--- a/htmlcov/z_055061514423972c___main___py.html
+++ b/htmlcov/z_055061514423972c___main___py.html
@@ -64,8 +64,8 @@
^ index
» next
- coverage.py v7.9.1,
- created at 2025-06-26 14:44 +0000
+ coverage.py v7.9.2,
+ created at 2025-07-23 06:01 +0000