orfdata 0.16.0__tar.gz → 0.18.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {orfdata-0.16.0/orfdata.egg-info → orfdata-0.18.0}/PKG-INFO +4 -1
- {orfdata-0.16.0 → orfdata-0.18.0}/README.md +3 -0
- orfdata-0.18.0/docs/catalogs.md +89 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/reference.md +37 -3
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/__init__.py +7 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/catalog.py +54 -11
- orfdata-0.18.0/orfdata/catalog_cli.py +63 -0
- orfdata-0.18.0/orfdata/catalog_manifest.py +101 -0
- orfdata-0.18.0/orfdata/catalogs.py +197 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/cli.py +24 -6
- orfdata-0.18.0/orfdata/data/catalogs.json +1 -0
- orfdata-0.18.0/orfdata/evidence_summary.py +108 -0
- orfdata-0.18.0/orfdata/version.py +1 -0
- {orfdata-0.16.0 → orfdata-0.18.0/orfdata.egg-info}/PKG-INFO +4 -1
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata.egg-info/SOURCES.txt +9 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/pyproject.toml +1 -1
- orfdata-0.18.0/tests/data/aggregate_evidence.json +877 -0
- orfdata-0.18.0/tests/test_catalog_registry.py +235 -0
- orfdata-0.18.0/tests/test_evidence_summary.py +178 -0
- orfdata-0.16.0/orfdata/version.py +0 -1
- {orfdata-0.16.0 → orfdata-0.18.0}/LICENSE +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/MANIFEST.in +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/deploy.sh +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/copy-yeast.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/coverage.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/design.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/human-tissue.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/ingolia-yeast.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/measurements.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/pairing.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/pfizer-protein.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/pfizer-rna.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/search-references.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/source-references.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/species.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/docs/storage.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/lint.sh +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/build.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/datasets.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/AUTHOR-LICENSE.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/NOTICE.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/catalog.json.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/AUTHOR-LICENSE.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/NOTICE.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/catalog.json.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/dataset.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/dataset_cli.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/datasets.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/initiation_context.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/measurements.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/model.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/pairing.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/reference.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/reference_mapping.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/search_reference.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/sequence.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/sources/__init__.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/sources/copy.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/sources/ingolia.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/sources/pfizer.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/sources/pfizer_rna.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata/sources/wang.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata.egg-info/dependency_links.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata.egg-info/entry_points.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata.egg-info/requires.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/orfdata.egg-info/top_level.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/setup.cfg +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/test.sh +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/conftest.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/GSM1905474.soft +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/GSM1905474_KL9_fpkm.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/GSM1905476.soft +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/GSM1905476_KL10_fpkm.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/GSM1905478.soft +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/GSM1905478_KL11_fpkm.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/GSM1905479.soft +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/GSM1905479_KL12_fpkm.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/NOTICE.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/geo-series.soft +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/methods.pdf +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/paper.xml +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/pmc-metadata.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/qconcat.fasta +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/copy/workbook.xlsx +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346111.metadata.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346111.quant.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346114.metadata.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346114.quant.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346115.metadata.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346115.quant.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346116.metadata.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346116.quant.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346117.metadata.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346117.quant.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346118.metadata.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346118.quant.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346120.metadata.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346120.quant.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346122.metadata.txt +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346122.quant.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/NOTICE.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/ingolia/readMe.txt.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/NOTICE.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/dog/expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/dog/manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/dog/protein.tsv +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/dog/search.fasta +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/human/expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/human/manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/human/protein.tsv +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/human/search.fasta +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/protein.tsv +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/search.fasta +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325522_family.soft.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325523_family.soft.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325525_family.soft.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/NOTICE.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/counts.csv.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-annotation.gtf.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-annotation.gtf.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/pfizer_rna/samples.xlsx +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/references/README.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/references/canis_lupus_familiaris.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/references/context-keys-v0.4.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/references/homo_sapiens.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/references/mus_musculus.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/references/saccharomyces_cerevisiae.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/regions/NOTICE.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/regions/ingolia.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/search_reference/NOTICE.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/search_reference/dog.fasta +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/search_reference/ensembl83-expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/search_reference/ensembl83-human.fasta.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/search_reference/expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/storage/README.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/storage/generate.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/storage/v1.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/storage/v1.sqlite +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/storage/v2.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/storage/v2.sqlite +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/storage/v3.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/storage/v3.sqlite +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/wang/NOTICE.md +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/wang/annotation.gtf.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/wang/copies.zip +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/wang/expected.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/wang/manifest.json +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/wang/protein.zip +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/wang/rna.zip +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/data/wang/search.fasta.gz +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_copy.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_cross_reference_pairing.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_ensembl_search_reference.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_ingolia.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_initiation_context.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_local_build_queries.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_measurements.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_paired_selectors.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_pairing.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_pfizer.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_pfizer_rna.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_reference_context.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_reference_mapping.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_reference_orf.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_region_targets.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_search_reference.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_selectors.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_storage.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_uorf_catalog.py +0 -0
- {orfdata-0.16.0 → orfdata-0.18.0}/tests/test_wang.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: orfdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
4
4
|
Summary: Reference open reading frames and experimental translation evidence on annotated transcripts
|
|
5
5
|
Author: OpenVax
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -99,6 +99,9 @@ checks. **The bundled experimental ORF catalogue is currently human-only**. Offl
|
|
|
99
99
|
bind real human, mouse, dog and yeast reference slices; those fixtures are not
|
|
100
100
|
new experimental ORF catalogues. [Species and reference identity](docs/species.md).
|
|
101
101
|
|
|
102
|
+
[Optional catalogue management](docs/catalogs.md) provides explicit, versioned
|
|
103
|
+
downloads and source builds while returning the same ORF objects and queries.
|
|
104
|
+
|
|
102
105
|
Install with `pip install orfdata[pyensembl]`. See the
|
|
103
106
|
[measurement API](docs/measurements.md) for native units, explicit samples and
|
|
104
107
|
assays, and DataCache-managed downloads. Downloadable
|
|
@@ -72,6 +72,9 @@ checks. **The bundled experimental ORF catalogue is currently human-only**. Offl
|
|
|
72
72
|
bind real human, mouse, dog and yeast reference slices; those fixtures are not
|
|
73
73
|
new experimental ORF catalogues. [Species and reference identity](docs/species.md).
|
|
74
74
|
|
|
75
|
+
[Optional catalogue management](docs/catalogs.md) provides explicit, versioned
|
|
76
|
+
downloads and source builds while returning the same ORF objects and queries.
|
|
77
|
+
|
|
75
78
|
Install with `pip install orfdata[pyensembl]`. See the
|
|
76
79
|
[measurement API](docs/measurements.md) for native units, explicit samples and
|
|
77
80
|
assays, and DataCache-managed downloads. Downloadable
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Optional ORF catalogues
|
|
2
|
+
|
|
3
|
+
`CatalogRegistry` manages versioned ORF catalogues with the same explicit
|
|
4
|
+
discovery, download, build and offline-open workflow as `DatasetRegistry`.
|
|
5
|
+
It returns the existing `ORFCatalog`; query results still bind to the original
|
|
6
|
+
PyEnsembl transcript through `ReferenceORF`.
|
|
7
|
+
|
|
8
|
+
The default optional registry currently has no released entries. The bundled
|
|
9
|
+
human catalogue remains available through `load_catalog()` without downloads.
|
|
10
|
+
The examples below use a caller-supplied registry; they do not imply a published
|
|
11
|
+
experimental catalogue for another species.
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import json
|
|
15
|
+
from orfdata import CatalogRegistry
|
|
16
|
+
|
|
17
|
+
with open("catalog-registry.json") as handle:
|
|
18
|
+
registry = CatalogRegistry(json.load(handle))
|
|
19
|
+
releases = registry.list(species="yeast")
|
|
20
|
+
for manifest in releases:
|
|
21
|
+
print(manifest.catalog_id, manifest.version, manifest.coverage)
|
|
22
|
+
|
|
23
|
+
# Select an entry from your populated registry.
|
|
24
|
+
name = releases[0].catalog_id
|
|
25
|
+
registry.download(name) # Explicit, checksum-verified acquisition.
|
|
26
|
+
catalog = registry.open(name) # Offline; no implicit download or repair.
|
|
27
|
+
records = catalog.query(evidence="ms", require_resolved=True)
|
|
28
|
+
orfs = catalog.for_transcript(transcript) # An already loaded PyEnsembl transcript.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use `info(name)` for immutable provenance and `download_metadata(name)` for
|
|
32
|
+
artifact URLs, checksums and access terms. `inspect(name)` checks the local
|
|
33
|
+
installation. Species, strain, assembly and annotation contexts remain attached
|
|
34
|
+
to accepted records; a matching accession alone cannot transfer evidence.
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
orfdata catalogs --registry catalog-registry.json list --species yeast
|
|
38
|
+
orfdata catalogs --registry catalog-registry.json download NAME
|
|
39
|
+
orfdata catalogs --registry catalog-registry.json query NAME --evidence ms --require-resolved
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Queries share the ordinary ORF CLI's gene, transcript, genomic-position and
|
|
43
|
+
evidence filters, and JSON/TSV/FASTA exports. They cannot override the selected
|
|
44
|
+
release with a local `--catalog` argument. Use `orfdata --catalog FILE` for the
|
|
45
|
+
existing standalone local-file workflow.
|
|
46
|
+
|
|
47
|
+
## Source builds
|
|
48
|
+
|
|
49
|
+
`registry.build(name, source_paths={"source.json": path})` explicitly rebuilds
|
|
50
|
+
an allowlisted source adapter. `registry.open_build(name)` opens that local
|
|
51
|
+
result, and `inspect_build(name)` checks its input pins, transform identity and
|
|
52
|
+
output receipt without reopening original input files. The CLI equivalents are
|
|
53
|
+
`catalogs build NAME --source-path source.json=PATH` and
|
|
54
|
+
`catalogs query NAME --built`. A local receipt's checksum is distinct from an
|
|
55
|
+
independently pinned published artifact; `inspect_build().verified` remains false.
|
|
56
|
+
|
|
57
|
+
Builds are atomic. `force=True` creates a fresh bundle while existing readers'
|
|
58
|
+
files survive. Cache roots follow `ORFDATA_CACHE`, `OPENVAX_DATA_CACHE`, or the
|
|
59
|
+
explicit `cache_root`; catalogues, quantitative datasets and local source builds
|
|
60
|
+
use separate locations.
|
|
61
|
+
|
|
62
|
+
## Defining a release
|
|
63
|
+
|
|
64
|
+
A registry definition maps a name to `default_version` and `versions`. Each
|
|
65
|
+
version contains a `CatalogManifest`, `distribution` access terms and an
|
|
66
|
+
`assets` entry for raw `catalog.json.gz` bytes, pinned by URL, SHA-256 and size.
|
|
67
|
+
The manifest declares references, original `SourceAsset` inputs, citations,
|
|
68
|
+
processing and coverage. `coverage.records` is required; source importers also
|
|
69
|
+
report original assignment counts and unresolved categories. Distinct protein
|
|
70
|
+
sequences and evidence summaries are separate counts.
|
|
71
|
+
|
|
72
|
+
Managed files use the existing `orfdata.catalog.v1` JSON representation and embed
|
|
73
|
+
the full manifest in `metadata.catalog_manifest`. Every accepted record must
|
|
74
|
+
declare a complete genomic reference included in that manifest. Opening validates
|
|
75
|
+
the whole manifest, species/assembly, references and declared output counts.
|
|
76
|
+
|
|
77
|
+
The initial `catalog-copy-v1` adapter wraps an independently prepared, unmanaged, pinned
|
|
78
|
+
portable catalogue named by `processing.catalog_asset`; it preserves native
|
|
79
|
+
metadata and adds the managed manifest. It rejects an input that already has a
|
|
80
|
+
managed manifest instead of replacing its release provenance. It does not infer missing references or
|
|
81
|
+
validate new biological models on behalf of a source importer. Python developers
|
|
82
|
+
can use `build_catalog(manifest, builder)`, where `builder(inputs, output_path)`
|
|
83
|
+
writes the managed file. DataCache verifies inputs and validates the completed
|
|
84
|
+
output before publishing the local build. Importer paths in metadata are never
|
|
85
|
+
executed as Python code.
|
|
86
|
+
|
|
87
|
+
[OpenProt source qualification](https://github.com/openvax/orfdata/issues/18)
|
|
88
|
+
and its [coordinate discrepancy](https://github.com/openvax/orfdata/issues/55)
|
|
89
|
+
remain separate from this data-management API.
|
|
@@ -12,7 +12,7 @@ PyEnsembl. Binding uses a transcript you have already loaded.
|
|
|
12
12
|
ORF. Each has:
|
|
13
13
|
|
|
14
14
|
- `model`: source identity, ORF type, coordinates, species and protein sequence.
|
|
15
|
-
- `evidence`: ribosome observations
|
|
15
|
+
- `evidence`: ribosome observations, peptide-level MS and native aggregate support.
|
|
16
16
|
- `transcript`: the original PyEnsembl transcript, including its original biotype.
|
|
17
17
|
- `coding_sequence` and `protein_sequence`: checked against that transcript.
|
|
18
18
|
|
|
@@ -46,10 +46,44 @@ possible protein. Inspect `orf.evidence.peptides` for peptide sequences, source
|
|
|
46
46
|
accessions, matching intervals, mapping counts and review quality. Missing counts
|
|
47
47
|
remain unknown; low or mixed spectrum reviews do not count as supporting MS.
|
|
48
48
|
|
|
49
|
+
Some sources provide only aggregate support counts. `EvidenceSummary` keeps the
|
|
50
|
+
native metric, definition, value, source/release, source checksum and explicitly
|
|
51
|
+
scoped entity. For example, [OpenProt's native metrics](https://api.openprot.org/api/2.0/SC/downloads/yeast_s288c-openprot-2_2-altprots+isoforms-min_2_pep-R64-1-1.106.tsv.readme.html)
|
|
52
|
+
include an MS score that sums study-specific unique-peptide
|
|
53
|
+
counts; it is neither global peptide uniqueness nor protein abundance. Its
|
|
54
|
+
translation score counts studies with significant translation detection.
|
|
55
|
+
`None` means unknown and remains distinct from a reported zero.
|
|
56
|
+
|
|
57
|
+
Positive summaries in `orf.evidence.summaries` can satisfy general evidence
|
|
58
|
+
queries. To require actual peptide records or named ribosome studies, use:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
orf.evidence.has_evidence("ms", require_resolved=True)
|
|
62
|
+
catalog.query(evidence="ribosome", require_resolved=True)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The CLI uses `--evidence ms --require-resolved`. Uniqueness and review filters
|
|
66
|
+
always require resolved peptide observations. Summaries never create peptide
|
|
67
|
+
sequences, residue coverage, retained variant peptides or mutant-translation
|
|
68
|
+
evidence. Their protein checksum and species/reference checks prevent attaching
|
|
69
|
+
them to a different protein or incompatible source context.
|
|
70
|
+
|
|
71
|
+
A sequence-level summary may have several transcript assignments. Those records
|
|
72
|
+
share the summary's `key`; `catalog.evidence_summaries` returns each native metric
|
|
73
|
+
once and rejects conflicting facts for the same identity. Native accession
|
|
74
|
+
aliases must be resolved explicitly by the source adapter. Counts from different
|
|
75
|
+
metrics and sources are not summed, and a later peptide-resolved snapshot is not
|
|
76
|
+
an additional independent experiment merely because it has more detail.
|
|
77
|
+
|
|
78
|
+
This API includes source-backed OpenProt human, mouse and yeast test slices.
|
|
79
|
+
It does not yet publish those species' OpenProt ORF catalogues; coordinate and
|
|
80
|
+
sequence qualification remains tracked in [#18](https://github.com/openvax/orfdata/issues/18).
|
|
81
|
+
|
|
49
82
|
The distinction between translation, peptide detection and functional protein
|
|
50
83
|
evidence follows the [TransCODE study](https://www.nature.com/articles/s41586-026-10459-x).
|
|
51
|
-
Source evidence
|
|
52
|
-
evidence for
|
|
84
|
+
Source evidence stays at its declared ORF or protein-sequence scope. It is not
|
|
85
|
+
automatically evidence for another isoform, a mutant protein, a fusion or
|
|
86
|
+
another species.
|
|
53
87
|
|
|
54
88
|
## Variants and RNA products
|
|
55
89
|
|
|
@@ -12,8 +12,11 @@ from .catalog import (
|
|
|
12
12
|
load_catalog,
|
|
13
13
|
load_uorf_catalog,
|
|
14
14
|
)
|
|
15
|
+
from .catalog_manifest import CatalogManifest
|
|
16
|
+
from .catalogs import CatalogRegistry, build_catalog
|
|
15
17
|
from .dataset import QuantitativeDataset
|
|
16
18
|
from .datasets import DatasetRegistry, build_dataset
|
|
19
|
+
from .evidence_summary import EvidenceSummary
|
|
17
20
|
from .initiation_context import KOZAK_PREFERENCES, InitiationContext, annotate_initiation_context
|
|
18
21
|
from .measurements import (
|
|
19
22
|
Assay,
|
|
@@ -44,6 +47,10 @@ __all__ = [
|
|
|
44
47
|
"ORFCatalog",
|
|
45
48
|
"ORFRecord",
|
|
46
49
|
"ORFEvidence",
|
|
50
|
+
"CatalogManifest",
|
|
51
|
+
"CatalogRegistry",
|
|
52
|
+
"build_catalog",
|
|
53
|
+
"EvidenceSummary",
|
|
47
54
|
"PeptideEvidence",
|
|
48
55
|
"ORFModel",
|
|
49
56
|
"ReferenceORF",
|
|
@@ -13,6 +13,7 @@ from importlib.resources import files
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import Optional
|
|
15
15
|
|
|
16
|
+
from .evidence_summary import EvidenceSummary, unique_summaries
|
|
16
17
|
from .initiation_context import KOZAK_PREFERENCES, InitiationContext
|
|
17
18
|
from .model import ORFModel, ReferenceORF, _species
|
|
18
19
|
from .reference import ReferenceContext
|
|
@@ -73,25 +74,41 @@ class ORFEvidence:
|
|
|
73
74
|
|
|
74
75
|
ribosome_studies: tuple = ()
|
|
75
76
|
peptides: tuple = ()
|
|
77
|
+
summaries: tuple = ()
|
|
78
|
+
|
|
79
|
+
def __post_init__(self):
|
|
80
|
+
object.__setattr__(self, "summaries", unique_summaries(self.summaries))
|
|
76
81
|
|
|
77
82
|
def to_dict(self):
|
|
78
|
-
|
|
83
|
+
result = asdict(self)
|
|
84
|
+
if not self.summaries:
|
|
85
|
+
result.pop("summaries")
|
|
86
|
+
return result
|
|
79
87
|
|
|
80
|
-
def has_evidence(self, method, *, require_unique=False, require_reviewed=False):
|
|
81
|
-
"""Select
|
|
88
|
+
def has_evidence(self, method, *, require_unique=False, require_reviewed=False, require_resolved=False):
|
|
89
|
+
"""Select reported support, or require named studies/resolved peptides.
|
|
90
|
+
|
|
91
|
+
Aggregate counts never satisfy uniqueness or spectrum-review filters.
|
|
92
|
+
No selection establishes evidence for a modified protein product.
|
|
93
|
+
"""
|
|
82
94
|
if method not in EVIDENCE_METHODS:
|
|
83
95
|
raise ValueError(f"Unknown evidence method: {method}")
|
|
84
96
|
if method == "ribosome":
|
|
85
97
|
if require_unique or require_reviewed:
|
|
86
98
|
raise ValueError("MS filters do not apply to ribosome evidence")
|
|
87
|
-
return bool(self.ribosome_studies)
|
|
88
|
-
|
|
99
|
+
return bool(self.ribosome_studies) or (not require_resolved and any(
|
|
100
|
+
s.method == "ribosome" and s.is_supporting for s in self.summaries))
|
|
101
|
+
resolved = any(
|
|
89
102
|
(method == "ms" or p.method == method or (method == "mhc" and p.method == "hla"))
|
|
90
103
|
and p.is_supporting
|
|
91
104
|
and (not require_unique or p.unique_in_source)
|
|
92
105
|
and (not require_reviewed or p.quality == "reviewed_support")
|
|
93
106
|
for p in self.peptides
|
|
94
107
|
)
|
|
108
|
+
return resolved or (not (require_resolved or require_unique or require_reviewed) and any(
|
|
109
|
+
(method == "ms" and s.method != "ribosome"
|
|
110
|
+
or s.method == method or method == "mhc" and s.method == "hla")
|
|
111
|
+
and s.is_supporting for s in self.summaries))
|
|
95
112
|
|
|
96
113
|
|
|
97
114
|
@dataclass(frozen=True)
|
|
@@ -116,6 +133,14 @@ class ORFRecord:
|
|
|
116
133
|
translation_table: int = 1
|
|
117
134
|
source: str = ""
|
|
118
135
|
reference_context: Optional[ReferenceContext] = None
|
|
136
|
+
summaries: tuple = ()
|
|
137
|
+
|
|
138
|
+
def __post_init__(self):
|
|
139
|
+
object.__setattr__(self, "summaries", unique_summaries(self.summaries))
|
|
140
|
+
if self.summaries:
|
|
141
|
+
model = self.model
|
|
142
|
+
for summary in self.summaries:
|
|
143
|
+
summary.validate_orf(model)
|
|
119
144
|
|
|
120
145
|
@property
|
|
121
146
|
def model(self):
|
|
@@ -140,7 +165,7 @@ class ORFRecord:
|
|
|
140
165
|
|
|
141
166
|
@property
|
|
142
167
|
def evidence(self):
|
|
143
|
-
return ORFEvidence(self.ribosome_studies, self.peptides)
|
|
168
|
+
return ORFEvidence(self.ribosome_studies, self.peptides, self.summaries)
|
|
144
169
|
|
|
145
170
|
@classmethod
|
|
146
171
|
def from_model(cls, model, evidence=None, *, initiation_context=None):
|
|
@@ -154,6 +179,7 @@ class ORFRecord:
|
|
|
154
179
|
**fields,
|
|
155
180
|
ribosome_studies=evidence.ribosome_studies,
|
|
156
181
|
peptides=evidence.peptides,
|
|
182
|
+
summaries=evidence.summaries,
|
|
157
183
|
initiation_context=initiation_context,
|
|
158
184
|
)
|
|
159
185
|
|
|
@@ -164,6 +190,8 @@ class ORFRecord:
|
|
|
164
190
|
def to_dict(self):
|
|
165
191
|
"""Return a serializable copy of this record."""
|
|
166
192
|
result = asdict(self)
|
|
193
|
+
if not self.summaries:
|
|
194
|
+
result.pop("summaries")
|
|
167
195
|
if self.reference_context is None:
|
|
168
196
|
result.pop("reference_context")
|
|
169
197
|
if self.initiation_context is not None:
|
|
@@ -284,9 +312,10 @@ class ORFRecord:
|
|
|
284
312
|
translation_change="not_measured",
|
|
285
313
|
)
|
|
286
314
|
|
|
287
|
-
def has_evidence(self, method, *, require_unique=False, require_reviewed=False):
|
|
315
|
+
def has_evidence(self, method, *, require_unique=False, require_reviewed=False, require_resolved=False):
|
|
288
316
|
return self.evidence.has_evidence(
|
|
289
|
-
method, require_unique=require_unique, require_reviewed=require_reviewed
|
|
317
|
+
method, require_unique=require_unique, require_reviewed=require_reviewed,
|
|
318
|
+
require_resolved=require_resolved,
|
|
290
319
|
)
|
|
291
320
|
|
|
292
321
|
def _coding_offset(self, position):
|
|
@@ -423,6 +452,13 @@ class ORFCatalog:
|
|
|
423
452
|
):
|
|
424
453
|
raise ValueError(f"Peptide sequence does not match {r.orf_id}")
|
|
425
454
|
|
|
455
|
+
self._evidence_summaries = unique_summaries(s for r in self.records for s in r.summaries)
|
|
456
|
+
|
|
457
|
+
@property
|
|
458
|
+
def evidence_summaries(self):
|
|
459
|
+
"""Distinct source counts, without multiplying transcript assignments."""
|
|
460
|
+
return self._evidence_summaries
|
|
461
|
+
|
|
426
462
|
@property
|
|
427
463
|
def metadata(self):
|
|
428
464
|
"""Return a copy of dataset version, sources, scope and provenance."""
|
|
@@ -472,6 +508,7 @@ class ORFCatalog:
|
|
|
472
508
|
evidence=None,
|
|
473
509
|
require_unique=False,
|
|
474
510
|
require_reviewed=False,
|
|
511
|
+
require_resolved=False,
|
|
475
512
|
biotype=None,
|
|
476
513
|
include_initiation_context=False,
|
|
477
514
|
):
|
|
@@ -479,10 +516,12 @@ class ORFCatalog:
|
|
|
479
516
|
|
|
480
517
|
A locus query requires an explicit assembly. Gene and transcript lookup
|
|
481
518
|
is a discovery aid, not evidence transfer between transcript isoforms.
|
|
482
|
-
Transcript identifiers match exactly as supplied in the source
|
|
483
|
-
|
|
519
|
+
Transcript identifiers match exactly as supplied in the source,
|
|
520
|
+
including versions when present. Empty results do not prove no translation.
|
|
484
521
|
``require_unique`` means unique within the evidence source's mapping
|
|
485
522
|
scope, not unique in every normal human proteome.
|
|
523
|
+
``require_resolved`` excludes aggregate-only support: MS needs a peptide
|
|
524
|
+
observation and ribosome evidence needs a named study.
|
|
486
525
|
``include_initiation_context`` also intersects placed spliced start
|
|
487
526
|
flanks, for example an upstream Kozak SNV. The default remains coding
|
|
488
527
|
blocks only. It does not include introns between context bases.
|
|
@@ -504,6 +543,8 @@ class ORFCatalog:
|
|
|
504
543
|
raise ValueError("require_unique applies to MS evidence only")
|
|
505
544
|
if require_reviewed and evidence not in EVIDENCE_METHODS[1:]:
|
|
506
545
|
raise ValueError("require_reviewed applies to MS evidence only")
|
|
546
|
+
if require_resolved and evidence is None:
|
|
547
|
+
raise ValueError("require_resolved requires an evidence method")
|
|
507
548
|
|
|
508
549
|
return tuple(
|
|
509
550
|
r
|
|
@@ -530,7 +571,8 @@ class ORFCatalog:
|
|
|
530
571
|
and (
|
|
531
572
|
evidence is None
|
|
532
573
|
or r.has_evidence(
|
|
533
|
-
evidence, require_unique=require_unique, require_reviewed=require_reviewed
|
|
574
|
+
evidence, require_unique=require_unique, require_reviewed=require_reviewed,
|
|
575
|
+
require_resolved=require_resolved,
|
|
534
576
|
)
|
|
535
577
|
)
|
|
536
578
|
)
|
|
@@ -604,6 +646,7 @@ def _load_catalog(path=None, *, expected_sha256=None, resource="orf-evidence"):
|
|
|
604
646
|
p["spectrum_ids"] = tuple(p.get("spectrum_ids", ()))
|
|
605
647
|
peptides.append(PeptideEvidence(**p))
|
|
606
648
|
row["peptides"] = tuple(peptides)
|
|
649
|
+
row["summaries"] = tuple(EvidenceSummary.from_dict(s) for s in row.get("summaries", ()))
|
|
607
650
|
records.append(ORFRecord(**row))
|
|
608
651
|
return ORFCatalog(records, metadata)
|
|
609
652
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Explicit optional catalogue discovery, acquisition and offline queries."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .catalogs import CatalogRegistry
|
|
8
|
+
from .cli import _add_query_arguments, _query
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run(argv=None, prog=None):
|
|
12
|
+
parser = argparse.ArgumentParser(description=__doc__, prog=prog)
|
|
13
|
+
parser.add_argument("--registry", help="Local JSON catalogue registry definitions")
|
|
14
|
+
parser.add_argument("--cache-root", help="Override the OpenVax cache root")
|
|
15
|
+
commands = parser.add_subparsers(dest="command", required=True)
|
|
16
|
+
listing = commands.add_parser("list", help="Discover release manifests without downloading")
|
|
17
|
+
listing.add_argument("--species")
|
|
18
|
+
listing.add_argument("--taxon-id", type=int)
|
|
19
|
+
query_parser = None
|
|
20
|
+
for command in ("info", "inspect", "download", "build", "query"):
|
|
21
|
+
subparser = commands.add_parser(command)
|
|
22
|
+
subparser.add_argument("name")
|
|
23
|
+
subparser.add_argument("--version")
|
|
24
|
+
if command in ("inspect", "query"):
|
|
25
|
+
subparser.add_argument("--built", action="store_true", help="Select a local source build explicitly")
|
|
26
|
+
if command in ("download", "build"):
|
|
27
|
+
subparser.add_argument("--force", action="store_true")
|
|
28
|
+
subparser.add_argument("--source-path", action="append", default=[], metavar="ASSET=PATH")
|
|
29
|
+
if command == "query":
|
|
30
|
+
query_parser = subparser
|
|
31
|
+
_add_query_arguments(subparser, catalog_path=False)
|
|
32
|
+
args = parser.parse_args(argv)
|
|
33
|
+
try:
|
|
34
|
+
definitions = json.loads(Path(args.registry).read_text()) if args.registry else None
|
|
35
|
+
registry = CatalogRegistry(definitions, cache_root=args.cache_root)
|
|
36
|
+
if args.command == "list":
|
|
37
|
+
output = [m.to_dict() for m in registry.list(species=args.species, taxon_id=args.taxon_id)]
|
|
38
|
+
elif args.command == "info":
|
|
39
|
+
output = registry.info(args.name, args.version).to_dict()
|
|
40
|
+
elif args.command == "inspect":
|
|
41
|
+
method = registry.inspect_build if args.built else registry.inspect
|
|
42
|
+
inspection = method(args.name, args.version)
|
|
43
|
+
output = dict(status=inspection.status, path=str(inspection.path), verified=inspection.verified)
|
|
44
|
+
if inspection.error is not None:
|
|
45
|
+
output["error"] = str(inspection.error)
|
|
46
|
+
elif args.command in ("download", "build"):
|
|
47
|
+
paths = {}
|
|
48
|
+
for item in args.source_path:
|
|
49
|
+
asset, separator, path = item.partition("=")
|
|
50
|
+
if not separator or not asset or not path or asset in paths:
|
|
51
|
+
raise ValueError("Source overrides require unique ASSET=PATH entries")
|
|
52
|
+
paths[asset] = path
|
|
53
|
+
method = registry.build if args.command == "build" else registry.download
|
|
54
|
+
method(args.name, args.version, force=args.force, source_paths=paths or None)
|
|
55
|
+
inspect = registry.inspect_build if args.command == "build" else registry.inspect
|
|
56
|
+
result = inspect(args.name, args.version)
|
|
57
|
+
output = dict(status=result.status, path=str(result.path), verified=result.verified)
|
|
58
|
+
else:
|
|
59
|
+
method = registry.open_build if args.built else registry.open
|
|
60
|
+
return _query(args, query_parser, lambda _: method(args.name, args.version))
|
|
61
|
+
except (ValueError, TypeError, KeyError, OSError) as error:
|
|
62
|
+
parser.error(str(error))
|
|
63
|
+
print(json.dumps(output, indent=2, sort_keys=True))
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Pinned provenance and coverage for an optional ORF catalogue."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from .measurements import SourceAsset, _identifier, _metadata, _Record
|
|
8
|
+
from .reference import ReferenceContext
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class CatalogManifest(_Record):
|
|
13
|
+
"""One species/assembly catalogue release, independent of abundance data."""
|
|
14
|
+
|
|
15
|
+
catalog_id: str
|
|
16
|
+
version: str
|
|
17
|
+
title: str
|
|
18
|
+
references: tuple
|
|
19
|
+
sources: tuple
|
|
20
|
+
citations: tuple
|
|
21
|
+
coverage: Mapping
|
|
22
|
+
processing: Mapping
|
|
23
|
+
schema: str = "orfdata.catalog-manifest.v1"
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def from_dict(cls, data):
|
|
27
|
+
values = dict(data)
|
|
28
|
+
values["references"] = tuple(ReferenceContext(**c) for c in values["references"])
|
|
29
|
+
values["sources"] = tuple(SourceAsset.from_dict(s) for s in values["sources"])
|
|
30
|
+
return cls(**values)
|
|
31
|
+
|
|
32
|
+
def __post_init__(self):
|
|
33
|
+
for name in ("catalog_id", "version", "title"):
|
|
34
|
+
_identifier(getattr(self, name), name)
|
|
35
|
+
for name in ("catalog_id", "version"):
|
|
36
|
+
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]*", getattr(self, name)):
|
|
37
|
+
raise ValueError(f"{name} must be a portable catalogue/path identifier")
|
|
38
|
+
if self.schema != "orfdata.catalog-manifest.v1":
|
|
39
|
+
raise ValueError("Unsupported catalogue manifest schema")
|
|
40
|
+
for name in ("references", "sources", "citations"):
|
|
41
|
+
values = getattr(self, name)
|
|
42
|
+
if isinstance(values, str):
|
|
43
|
+
raise TypeError(f"Catalogue {name} must be a sequence of entries")
|
|
44
|
+
values = tuple(values)
|
|
45
|
+
if not values:
|
|
46
|
+
raise ValueError(f"Catalogue {name} cannot be empty")
|
|
47
|
+
object.__setattr__(self, name, values)
|
|
48
|
+
if any(not isinstance(r, ReferenceContext) for r in self.references):
|
|
49
|
+
raise TypeError("Catalogue references must be ReferenceContexts")
|
|
50
|
+
if len({r.key for r in self.references}) != len(self.references):
|
|
51
|
+
raise ValueError("Duplicate catalogue reference contexts")
|
|
52
|
+
if len({r.taxon_id for r in self.references}) != 1:
|
|
53
|
+
raise ValueError("A catalogue must have one species")
|
|
54
|
+
builds = {r.genome_build for r in self.references if r.genome_build is not None}
|
|
55
|
+
if len(builds) != 1 or not any(r.is_genomic for r in self.references):
|
|
56
|
+
raise ValueError("A catalogue must have one assembly and a complete genomic reference")
|
|
57
|
+
if any(not isinstance(s, SourceAsset) for s in self.sources):
|
|
58
|
+
raise TypeError("Catalogue sources must be SourceAssets")
|
|
59
|
+
if len({s.asset_id for s in self.sources}) != len(self.sources):
|
|
60
|
+
raise ValueError("Duplicate catalogue source assets")
|
|
61
|
+
for citation in self.citations:
|
|
62
|
+
_identifier(citation, "citation")
|
|
63
|
+
_metadata(self, "coverage", "processing")
|
|
64
|
+
count = self.coverage.get("records")
|
|
65
|
+
if type(count) is not int or count < 0:
|
|
66
|
+
raise ValueError("Catalogue coverage must declare a nonnegative records count")
|
|
67
|
+
for key in ("distinct_protein_sequences", "evidence_summaries"):
|
|
68
|
+
if key in self.coverage and (type(self.coverage[key]) is not int or self.coverage[key] < 0):
|
|
69
|
+
raise ValueError(f"Catalogue {key} must be a nonnegative integer")
|
|
70
|
+
if not self.processing:
|
|
71
|
+
raise ValueError("Catalogue processing provenance is required")
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def taxon_id(self):
|
|
75
|
+
return self.references[0].taxon_id
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def genome_build(self):
|
|
79
|
+
return next(r.genome_build for r in self.references if r.is_genomic)
|
|
80
|
+
|
|
81
|
+
def validate_catalog(self, catalog):
|
|
82
|
+
"""Check content identity, accepted records and declared reference scope."""
|
|
83
|
+
if catalog.metadata.get("catalog_manifest") != self.to_dict():
|
|
84
|
+
raise ValueError("Catalogue manifest disagrees with the registered release")
|
|
85
|
+
if catalog.taxon_id != self.taxon_id or catalog.genome_build != self.genome_build:
|
|
86
|
+
raise ValueError("Catalogue species or assembly disagrees with manifest")
|
|
87
|
+
if len(catalog.records) != self.coverage["records"]:
|
|
88
|
+
raise ValueError("Catalogue record count disagrees with manifest")
|
|
89
|
+
references = {r.key for r in self.references}
|
|
90
|
+
for record in catalog.records:
|
|
91
|
+
reference = record.reference_context
|
|
92
|
+
if reference is None or not reference.is_genomic or reference.key not in references:
|
|
93
|
+
raise ValueError("Managed ORF records require a declared complete genomic reference")
|
|
94
|
+
for summary in catalog.evidence_summaries:
|
|
95
|
+
if summary.target.reference.key not in references:
|
|
96
|
+
raise ValueError("Evidence summary reference is absent from catalogue manifest")
|
|
97
|
+
counts = dict(distinct_protein_sequences=len({r.protein_sequence for r in catalog.records}),
|
|
98
|
+
evidence_summaries=len(catalog.evidence_summaries))
|
|
99
|
+
for key, actual in counts.items():
|
|
100
|
+
if key in self.coverage and self.coverage[key] != actual:
|
|
101
|
+
raise ValueError(f"Catalogue {key} count disagrees with manifest")
|