diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a77f2f8..e1f935b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -21,6 +21,7 @@ - [ ] A visualization table is included, that can be used with the simulated data to reproduce figures from the original publication - [ ] The PEtab problem is valid (check with e.g. `petablint -vy problem.yaml`) - [ ] Labels (e.g. `parameterName` in the parameter table or `xLabel` in the visualization table) encode special characters with tex-style strings instead of unicode, e.g. use `H$_2$O` for water. +- [ ] `problems//metadata.yaml` contains a single-sentence `short_description` of the problem and passes `bmp-check-metadata` - [ ] The PEtab problem author(s) are assigned to the GitHub issue - [ ] The README has been updated with `bmp-create-overview --update` (requires `pip install -e src/python` from the repository root) - [ ] The new PEtab problem row in the generated table has the correct reference (and other entries) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 60a26ed..8c58dad 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,6 +37,9 @@ jobs: - name: Check SBML metadata run: bmp-check-sbml-metadata + - name: Check problem metadata files + run: bmp-check-metadata + - name: Check all PEtab YAML files under problems/ # informational only for now -- some pre-existing, unrelated problems # are known to fail this check diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8182204..cf4c7bb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,17 @@ Each problem lives under `problems//`: - `v1/` contains the PEtab problem itself: `problem.yaml`, `model.xml`, `conditions.tsv`, `measurements.tsv`, `observables.tsv`, `parameters.tsv`, and optionally `simulations.tsv` and `visualizations.tsv`. +- `metadata.yaml` contains version-independent metadata. Currently, this is a single-sentence + `short_description` of the problem, stating the type of model (e.g., biochemical reaction network, + compartmental epidemiological model), the biological system, and, where informative, the data. + For example: + ```yaml + short_description: Reaction network model of Epo-induced STAT5A/STAT5B phosphorylation and homo- and heterodimerization, fitted to quantitative mass spectrometry data. + ``` + The file is validated against the `ProblemMetadata` model in + [`metadata.py`](src/python/benchmark_models_petab/metadata.py) + (check with `bmp-check-metadata`); the corresponding JSON schema is + [`metadata.schema.json`](src/python/benchmark_models_petab/schemas/metadata.schema.json). - `resources/` (optional) holds anything else related to the problem that isn't part of the PEtab files themselves (e.g. raw data, scripts, notebooks, figures). There's no prescribed structure — organize it however fits your problem. diff --git a/problems/Alkan_SciSignal2018/metadata.yaml b/problems/Alkan_SciSignal2018/metadata.yaml new file mode 100644 index 0000000..abdb326 --- /dev/null +++ b/problems/Alkan_SciSignal2018/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of the DNA damage response and cell fate in cancer cells treated with chemotherapeutics, fitted to signaling and cell count data. diff --git a/problems/Armistead_CellDeathDis2024/metadata.yaml b/problems/Armistead_CellDeathDis2024/metadata.yaml new file mode 100644 index 0000000..2631044 --- /dev/null +++ b/problems/Armistead_CellDeathDis2024/metadata.yaml @@ -0,0 +1 @@ +short_description: Small reaction network model of sphingolipid metabolism with ceramide feedback, fitted to lipidomics data of wild-type and mutant zebrafish embryos. diff --git a/problems/Bachmann_MSB2011/metadata.yaml b/problems/Bachmann_MSB2011/metadata.yaml new file mode 100644 index 0000000..8d62e9e --- /dev/null +++ b/problems/Bachmann_MSB2011/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of Epo-induced JAK2/STAT5 signaling with two negative feedback regulators (SOCS3, CIS) in murine erythroid progenitor cells. diff --git a/problems/Beer_MolBioSystems2014/metadata.yaml b/problems/Beer_MolBioSystems2014/metadata.yaml new file mode 100644 index 0000000..c8ba170 --- /dev/null +++ b/problems/Beer_MolBioSystems2014/metadata.yaml @@ -0,0 +1 @@ +short_description: Small kinetic model of bacterial growth and production of the blue pigment indigoidine by engineered variants of a non-ribosomal peptide synthetase. diff --git a/problems/Bertozzi_PNAS2020/metadata.yaml b/problems/Bertozzi_PNAS2020/metadata.yaml new file mode 100644 index 0000000..846f7d4 --- /dev/null +++ b/problems/Bertozzi_PNAS2020/metadata.yaml @@ -0,0 +1 @@ +short_description: SIR compartmental epidemiological model of the early COVID-19 outbreak in New York and California. diff --git a/problems/Blasi_CellSystems2016/metadata.yaml b/problems/Blasi_CellSystems2016/metadata.yaml new file mode 100644 index 0000000..18d5f69 --- /dev/null +++ b/problems/Blasi_CellSystems2016/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of combinatorial histone H4 acetylation states generated by motif-specific acetylation reactions. diff --git a/problems/Boehm_JProteomeRes2014/metadata.yaml b/problems/Boehm_JProteomeRes2014/metadata.yaml new file mode 100644 index 0000000..6242d33 --- /dev/null +++ b/problems/Boehm_JProteomeRes2014/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of Epo-induced STAT5A/STAT5B phosphorylation and homo- and heterodimerization, fitted to quantitative mass spectrometry data. diff --git a/problems/Borghans_BiophysChem1997/metadata.yaml b/problems/Borghans_BiophysChem1997/metadata.yaml new file mode 100644 index 0000000..5bad55b --- /dev/null +++ b/problems/Borghans_BiophysChem1997/metadata.yaml @@ -0,0 +1 @@ +short_description: Three-variable ODE model of complex calcium oscillations with Ca2+-activated IP3 degradation, fitted to measured calcium bursting in a single hepatocyte. diff --git a/problems/Brannmark_JBC2010/metadata.yaml b/problems/Brannmark_JBC2010/metadata.yaml new file mode 100644 index 0000000..c6acdef --- /dev/null +++ b/problems/Brannmark_JBC2010/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of insulin receptor and IRS1 phosphorylation with receptor endocytosis and feedback in primary human adipocytes. diff --git a/problems/Bruno_JExpBot2016/metadata.yaml b/problems/Bruno_JExpBot2016/metadata.yaml new file mode 100644 index 0000000..71343bf --- /dev/null +++ b/problems/Bruno_JExpBot2016/metadata.yaml @@ -0,0 +1 @@ +short_description: Enzyme kinetic model of the in vitro cleavage of carotenoids and apocarotenoids by Arabidopsis carotenoid cleavage dioxygenase 4 (CCD4). diff --git a/problems/Chen_MSB2009/metadata.yaml b/problems/Chen_MSB2009/metadata.yaml new file mode 100644 index 0000000..9f773fd --- /dev/null +++ b/problems/Chen_MSB2009/metadata.yaml @@ -0,0 +1 @@ +short_description: Large mass-action reaction network model of ErbB receptor signaling to ERK and AKT in response to EGF and heregulin. diff --git a/problems/Crauste_CellSystems2017/metadata.yaml b/problems/Crauste_CellSystems2017/metadata.yaml new file mode 100644 index 0000000..7dddb22 --- /dev/null +++ b/problems/Crauste_CellSystems2017/metadata.yaml @@ -0,0 +1 @@ +short_description: Cell population model of CD8 T cell differentiation (naive, early/late effector, memory), fitted to cell counts in mice after vaccinia virus infection. diff --git a/problems/Elowitz_Nature2000/metadata.yaml b/problems/Elowitz_Nature2000/metadata.yaml new file mode 100644 index 0000000..7141a5a --- /dev/null +++ b/problems/Elowitz_Nature2000/metadata.yaml @@ -0,0 +1 @@ +short_description: Gene regulatory network model of the repressilator, a synthetic three-gene transcriptional oscillator in E. coli, fitted to a fluorescence time course. diff --git a/problems/Fiedler_BMCSystBiol2016/metadata.yaml b/problems/Fiedler_BMCSystBiol2016/metadata.yaml new file mode 100644 index 0000000..a3bf4ce --- /dev/null +++ b/problems/Fiedler_BMCSystBiol2016/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of RAF/MEK/ERK signaling with ERK feedback in HeLa cells, fitted to phospho-MEK/ERK data without and with RAF or MEK inhibitors. diff --git a/problems/Froehlich_CellSystems2018/metadata.yaml b/problems/Froehlich_CellSystems2018/metadata.yaml new file mode 100644 index 0000000..09997e1 --- /dev/null +++ b/problems/Froehlich_CellSystems2018/metadata.yaml @@ -0,0 +1 @@ +short_description: Large-scale reaction network model of cancer signaling pathways, parameterized by cell-line-specific gene expression and fitted to drug response viability data. diff --git a/problems/Fujita_SciSignal2010/metadata.yaml b/problems/Fujita_SciSignal2010/metadata.yaml new file mode 100644 index 0000000..df8148f --- /dev/null +++ b/problems/Fujita_SciSignal2010/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of EGF-induced EGFR, Akt and S6 phosphorylation in PC12 cells under different EGF stimulation profiles. diff --git a/problems/Giordano_Nature2020/metadata.yaml b/problems/Giordano_Nature2020/metadata.yaml new file mode 100644 index 0000000..193160f --- /dev/null +++ b/problems/Giordano_Nature2020/metadata.yaml @@ -0,0 +1 @@ +short_description: Compartmental epidemiological model (SIDARTHE, distinguishing detected/undetected and symptom severity) of the COVID-19 epidemic in Italy. diff --git a/problems/Isensee_JCB2018/metadata.yaml b/problems/Isensee_JCB2018/metadata.yaml new file mode 100644 index 0000000..d030491 --- /dev/null +++ b/problems/Isensee_JCB2018/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of cAMP-induced PKA activation and phosphorylation of its regulatory subunit in rat sensory neurons under pharmacological perturbations. diff --git a/problems/Lang_PLOSComputBiol2024/metadata.yaml b/problems/Lang_PLOSComputBiol2024/metadata.yaml new file mode 100644 index 0000000..50ec321 --- /dev/null +++ b/problems/Lang_PLOSComputBiol2024/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of the human cell cycle, generated from a rule-based model and fitted to compartment-resolved single-cell imaging data of RPE-1 cells. diff --git a/problems/Laske_PLOSComputBiol2019/metadata.yaml b/problems/Laske_PLOSComputBiol2019/metadata.yaml new file mode 100644 index 0000000..60a5792 --- /dev/null +++ b/problems/Laske_PLOSComputBiol2019/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of intracellular influenza A virus replication, fitted to viral RNA, genome import and virus release data in human A549 cells. diff --git a/problems/Liu_IFACPapersOnLine2025/metadata.yaml b/problems/Liu_IFACPapersOnLine2025/metadata.yaml new file mode 100644 index 0000000..6d7e1ec --- /dev/null +++ b/problems/Liu_IFACPapersOnLine2025/metadata.yaml @@ -0,0 +1 @@ +short_description: Population model of oncolytic virus therapy with uninfected and infected tumor cells and virus, fitted to tumor size data in zebrafish. diff --git a/problems/Lucarelli_CellSystems2018/metadata.yaml b/problems/Lucarelli_CellSystems2018/metadata.yaml new file mode 100644 index 0000000..c36659d --- /dev/null +++ b/problems/Lucarelli_CellSystems2018/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of TGF-beta-induced SMAD complex formation and target gene expression. diff --git a/problems/Okuonghae_ChaosSolitonsFractals2020/metadata.yaml b/problems/Okuonghae_ChaosSolitonsFractals2020/metadata.yaml new file mode 100644 index 0000000..ff043e6 --- /dev/null +++ b/problems/Okuonghae_ChaosSolitonsFractals2020/metadata.yaml @@ -0,0 +1 @@ +short_description: Compartmental epidemiological model of COVID-19 in Lagos, Nigeria, including asymptomatic and detected infections. diff --git a/problems/Oliveira_NatCommun2021/metadata.yaml b/problems/Oliveira_NatCommun2021/metadata.yaml new file mode 100644 index 0000000..6c16909 --- /dev/null +++ b/problems/Oliveira_NatCommun2021/metadata.yaml @@ -0,0 +1 @@ +short_description: Compartmental epidemiological model of COVID-19 in Bahia, Brazil (state excluding the capital Salvador), with non-detected cases, hospitalization and deaths. diff --git a/problems/Perelson_Science1996/metadata.yaml b/problems/Perelson_Science1996/metadata.yaml new file mode 100644 index 0000000..41fa767 --- /dev/null +++ b/problems/Perelson_Science1996/metadata.yaml @@ -0,0 +1 @@ +short_description: Within-host model of HIV-1 viral dynamics, fitted to plasma HIV-1 RNA levels after treatment with the protease inhibitor ritonavir. diff --git a/problems/Rahman_MBS2016/metadata.yaml b/problems/Rahman_MBS2016/metadata.yaml new file mode 100644 index 0000000..e65e1b1 --- /dev/null +++ b/problems/Rahman_MBS2016/metadata.yaml @@ -0,0 +1 @@ +short_description: Compartmental epidemiological model of HIV transmission with CD4+ T cell count-based infection stages and treatment, fitted to HIV prevalence in South Africa. diff --git a/problems/Raia_CancerResearch2011/metadata.yaml b/problems/Raia_CancerResearch2011/metadata.yaml new file mode 100644 index 0000000..6c2f454 --- /dev/null +++ b/problems/Raia_CancerResearch2011/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of IL-13-induced JAK2/STAT5 signaling in the primary mediastinal B-cell lymphoma cell line MedB-1. diff --git a/problems/Raimundez_PCB2020/metadata.yaml b/problems/Raimundez_PCB2020/metadata.yaml new file mode 100644 index 0000000..6525abc --- /dev/null +++ b/problems/Raimundez_PCB2020/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of EGFR and MET signaling to ERK and AKT in two gastric cancer cell lines in response to EGF and the anti-EGFR antibody cetuximab. diff --git a/problems/SalazarCavazos_MBoC2020/metadata.yaml b/problems/SalazarCavazos_MBoC2020/metadata.yaml new file mode 100644 index 0000000..9b8d8f1 --- /dev/null +++ b/problems/SalazarCavazos_MBoC2020/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of EGFR dimerization, multisite phosphorylation and binding of the adaptor proteins GRB2 and SHC1, generated from a rule-based model. diff --git a/problems/Schwen_PONE2014/metadata.yaml b/problems/Schwen_PONE2014/metadata.yaml new file mode 100644 index 0000000..1a9a5ea --- /dev/null +++ b/problems/Schwen_PONE2014/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of insulin binding, internalization and degradation in two subpopulations of isolated hepatocytes with low and high insulin binding. diff --git a/problems/Smith_BMCSystBiol2013/metadata.yaml b/problems/Smith_BMCSystBiol2013/metadata.yaml new file mode 100644 index 0000000..ff36c4d --- /dev/null +++ b/problems/Smith_BMCSystBiol2013/metadata.yaml @@ -0,0 +1 @@ +short_description: Large reaction network model of insulin signaling in rodent adipocytes and its regulation by oxidative stress. diff --git a/problems/Sneyd_PNAS2002/metadata.yaml b/problems/Sneyd_PNAS2002/metadata.yaml new file mode 100644 index 0000000..fe97b22 --- /dev/null +++ b/problems/Sneyd_PNAS2002/metadata.yaml @@ -0,0 +1 @@ +short_description: Kinetic model of the type-2 IP3 receptor, fitted to time courses of calcium release from liver microsomes at varying IP3 and calcium concentrations. diff --git a/problems/Weber_BMC2015/metadata.yaml b/problems/Weber_BMC2015/metadata.yaml new file mode 100644 index 0000000..a7ab06f --- /dev/null +++ b/problems/Weber_BMC2015/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of interactions of protein kinase D, a lipid kinase (PI4KIIIbeta) and a ceramide transfer protein (CERT) at the trans-Golgi network. diff --git a/problems/Zhao_QuantBiol2020/metadata.yaml b/problems/Zhao_QuantBiol2020/metadata.yaml new file mode 100644 index 0000000..a580bb1 --- /dev/null +++ b/problems/Zhao_QuantBiol2020/metadata.yaml @@ -0,0 +1 @@ +short_description: Compartmental epidemiological model of COVID-19 in Wuhan, Hubei and the rest of China, with quarantined and unquarantined infected individuals. diff --git a/problems/Zheng_PNAS2012/metadata.yaml b/problems/Zheng_PNAS2012/metadata.yaml new file mode 100644 index 0000000..ee3dece --- /dev/null +++ b/problems/Zheng_PNAS2012/metadata.yaml @@ -0,0 +1 @@ +short_description: Reaction network model of the combinatorial methylation of lysines 27 and 36 on histone H3. diff --git a/src/python/MANIFEST.in b/src/python/MANIFEST.in index 77d842d..2d06440 100644 --- a/src/python/MANIFEST.in +++ b/src/python/MANIFEST.in @@ -1 +1,2 @@ recursive-include benchmark_models_petab/problems * +include benchmark_models_petab/schemas/*.json diff --git a/src/python/benchmark_models_petab/C.py b/src/python/benchmark_models_petab/C.py index 9024b3d..46b52a9 100644 --- a/src/python/benchmark_models_petab/C.py +++ b/src/python/benchmark_models_petab/C.py @@ -14,6 +14,7 @@ V1_DIRNAME: str = "v1" PROBLEM_FILENAME: str = "problem.yaml" SIMULATIONS_FILENAME: str = "simulations.tsv" +METADATA_FILENAME: str = "metadata.yaml" GITHUB_REPO: str = "Benchmarking-Initiative/Benchmark-Models-PEtab" GITHUB_DEFAULT_BRANCH: str = "master" diff --git a/src/python/benchmark_models_petab/__init__.py b/src/python/benchmark_models_petab/__init__.py index d6a210d..4856ab3 100644 --- a/src/python/benchmark_models_petab/__init__.py +++ b/src/python/benchmark_models_petab/__init__.py @@ -6,6 +6,7 @@ from .base import get_problem, get_problem_yaml_path, get_simulation_df from .C import MODEL_DIRS, MODELS, MODELS_DIR +from .metadata import ProblemMetadata, get_metadata from .overview import get_overview_df from importlib.metadata import PackageNotFoundError, version diff --git a/src/python/benchmark_models_petab/check_metadata.py b/src/python/benchmark_models_petab/check_metadata.py new file mode 100644 index 0000000..7d0ba68 --- /dev/null +++ b/src/python/benchmark_models_petab/check_metadata.py @@ -0,0 +1,53 @@ +"""Check that every problem has a metadata file that matches the schema.""" + +import sys +from pathlib import Path + +import pydantic +import yaml + +from .C import MODELS +from .metadata import ProblemMetadata, get_metadata_path + + +def check_metadata_file(path: Path) -> list[str]: + """Check a single metadata file. + + Returns + ------- + A list of error messages, empty if the file is valid. + """ + if not path.is_file(): + return [f"Missing metadata file `{path.name}`."] + + try: + ProblemMetadata.from_yaml(path) + except yaml.YAMLError as e: + return [f"Invalid YAML: {e}"] + except pydantic.ValidationError as e: + return [ + f"{'/'.join(map(str, error['loc'])) or ''}: {error['msg']}" + for error in e.errors() + ] + + return [] + + +def main(): + """Check the metadata files of all problems.""" + num_failures = 0 + + for problem_id in MODELS: + errors = check_metadata_file(get_metadata_path(problem_id)) + + print(problem_id) + if errors: + print("\n".join("\t" + error for error in errors)) + num_failures += 1 + print("=" * 100) # just for output readability + + num_passed = len(MODELS) - num_failures + print(f"Result: {Path(__file__).stem}") + print(f"{num_passed} out of {len(MODELS)} passed.") + print(f"{num_failures} out of {len(MODELS)} failed.") + sys.exit(num_failures) diff --git a/src/python/benchmark_models_petab/metadata.py b/src/python/benchmark_models_petab/metadata.py new file mode 100644 index 0000000..f85e540 --- /dev/null +++ b/src/python/benchmark_models_petab/metadata.py @@ -0,0 +1,103 @@ +"""Version-independent per-problem metadata.""" + +import json +from importlib.resources import files +from pathlib import Path +from typing import Any + +import yaml +from pydantic import BaseModel, ConfigDict, Field + +from .C import METADATA_FILENAME, MODELS_DIR + +METADATA_SCHEMA_FILENAME = "metadata.schema.json" + + +class ProblemMetadata(BaseModel): + """Version-independent metadata of a benchmark problem.""" + + model_config = ConfigDict( + extra="forbid", + title="Benchmark problem metadata", + json_schema_extra={ + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + ) + + short_description: str = Field( + min_length=20, + max_length=160, + pattern=r"^[A-Z][\x20-\x7E]*\.$", + description=( + "Single-sentence summary of the problem: the type of model " + "(e.g., biochemical reaction network, compartmental " + "epidemiological model), the biological system, and, where " + "informative, the data. Plain ASCII text, no line breaks, " + "ending with a period." + ), + ) + + @classmethod + def from_yaml(cls, path: str | Path) -> "ProblemMetadata": + """Load and validate a metadata file. + + Raises + ------ + pydantic.ValidationError + If the metadata is invalid. + """ + with open(path, encoding="utf-8") as f: + return cls.model_validate(yaml.safe_load(f)) + + +def get_metadata_schema() -> dict[str, Any]: + """Get the JSON schema for the per-problem metadata files.""" + return ProblemMetadata.model_json_schema() + + +def get_metadata_path(id_: str) -> Path: + """Get the path to the metadata file of the given problem. + + Parameters + ---------- + id_: Problem name, as in `benchmark_models_petab.MODELS`. + + Returns + ------- + The path to the metadata file. The file is not guaranteed to exist. + """ + return Path(MODELS_DIR, id_, METADATA_FILENAME) + + +def get_metadata(id_: str) -> ProblemMetadata: + """Get the metadata of a benchmark problem. + + The metadata is not specific to a PEtab version. + + Parameters + ---------- + id_: Problem name, as in `benchmark_models_petab.MODELS`. + + Returns + ------- + The problem metadata. + """ + path = get_metadata_path(id_) + if not path.is_file(): + raise ValueError(f"Could not find metadata for problem `{id_}`.") + return ProblemMetadata.from_yaml(path) + + +def write_metadata_schema(path: str | Path | None = None) -> None: + """Write the JSON schema for the metadata files. + + By default, the packaged schema file is overwritten. Run after changing + `ProblemMetadata`. + """ + if path is None: + path = Path( + str(files(__package__) / "schemas" / METADATA_SCHEMA_FILENAME) + ) + with open(path, "w", encoding="utf-8") as f: + json.dump(get_metadata_schema(), f, indent=2) + f.write("\n") diff --git a/src/python/benchmark_models_petab/schemas/metadata.schema.json b/src/python/benchmark_models_petab/schemas/metadata.schema.json new file mode 100644 index 0000000..95d89fd --- /dev/null +++ b/src/python/benchmark_models_petab/schemas/metadata.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Version-independent metadata of a benchmark problem.", + "properties": { + "short_description": { + "description": "Single-sentence summary of the problem: the type of model (e.g., biochemical reaction network, compartmental epidemiological model), the biological system, and, where informative, the data. Plain ASCII text, no line breaks, ending with a period.", + "maxLength": 160, + "minLength": 20, + "pattern": "^[A-Z][\\x20-\\x7E]*\\.$", + "title": "Short Description", + "type": "string" + } + }, + "required": [ + "short_description" + ], + "title": "Benchmark problem metadata", + "type": "object" +} diff --git a/src/python/pyproject.toml b/src/python/pyproject.toml index cce8202..5b1a3a0 100644 --- a/src/python/pyproject.toml +++ b/src/python/pyproject.toml @@ -19,6 +19,8 @@ authors = [ urls = { "Homepage" = "https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab" } dependencies = [ "petab>=0.4.0", + "pydantic>=2", + "pyyaml", "numpy", "pandas", "python-libsbml", @@ -33,6 +35,7 @@ site = ["bokeh>=3.7.3"] [project.scripts] bmp-petablint = "benchmark_models_petab.check_petablint:main" bmp-check-petab-yaml = "benchmark_models_petab.check_petab_yaml:main" +bmp-check-metadata = "benchmark_models_petab.check_metadata:main" bmp-check-sbml-metadata = "benchmark_models_petab.check_sbml_metadata:main" bmp-check-tsv-format = "benchmark_models_petab.check_tsv_format:main" bmp-create-overview = "benchmark_models_petab.overview:main" diff --git a/src/python/test/test_metadata.py b/src/python/test/test_metadata.py new file mode 100644 index 0000000..b5e5cc1 --- /dev/null +++ b/src/python/test/test_metadata.py @@ -0,0 +1,95 @@ +"""Tests for the per-problem metadata files.""" + +import json +from importlib.resources import files + +import pydantic +import pytest + +import benchmark_models_petab as models +from benchmark_models_petab.check_metadata import check_metadata_file +from benchmark_models_petab.metadata import ( + METADATA_SCHEMA_FILENAME, + ProblemMetadata, + get_metadata_path, + get_metadata_schema, +) + + +def test_packaged_schema_is_up_to_date(): + """The packaged JSON schema matches `ProblemMetadata`.""" + schema_file = ( + files("benchmark_models_petab") / "schemas" / METADATA_SCHEMA_FILENAME + ) + packaged_schema = json.loads(schema_file.read_text(encoding="utf-8")) + assert packaged_schema == get_metadata_schema(), ( + "Outdated metadata schema. Regenerate with " + "`benchmark_models_petab.metadata.write_metadata_schema()`." + ) + + +def test_get_metadata(): + metadata = models.get_metadata("Boehm_JProteomeRes2014") + assert isinstance(metadata, ProblemMetadata) + assert "STAT5" in metadata.short_description + + with pytest.raises(ValueError): + models.get_metadata("not a problem name") + + +@pytest.mark.parametrize("problem_id", models.MODELS) +def test_all_problems_have_valid_metadata(problem_id): + assert check_metadata_file(get_metadata_path(problem_id)) == [] + + +@pytest.mark.parametrize( + "metadata", + [ + {"short_description": "Reaction network model of something."}, + ], +) +def test_valid_metadata(metadata): + ProblemMetadata.model_validate(metadata) + + +@pytest.mark.parametrize( + "metadata", + [ + {}, + {"short_description": None}, + {"short_description": "Too short."}, + {"short_description": "Reaction network model of something"}, + {"short_description": "reaction network model of something."}, + {"short_description": "Reaction network model\nof something."}, + {"short_description": "Reaction network model of Ca²⁺ signaling."}, + {"short_description": "Reaction network model of " + 200 * "x" + "."}, + {"description": "Reaction network model of something."}, + { + "short_description": "Reaction network model of something.", + "unknown": 1, + }, + ["short_description"], + ], +) +def test_invalid_metadata(metadata): + with pytest.raises(pydantic.ValidationError): + ProblemMetadata.model_validate(metadata) + + +def test_check_metadata_file(tmp_path): + path = tmp_path / "metadata.yaml" + assert check_metadata_file(path) == [ + "Missing metadata file `metadata.yaml`." + ] + + path.write_text("short_description: [unclosed\n") + assert check_metadata_file(path)[0].startswith("Invalid YAML") + + path.write_text("short_description: Too short.\n") + (error,) = check_metadata_file(path) + assert error.startswith("short_description: ") + + path.write_text( + "short_description: Reaction network model of something.\n" + ) + assert check_metadata_file(path) == []