orfdata 0.17.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.17.0/orfdata.egg-info → orfdata-0.18.0}/PKG-INFO +4 -1
- {orfdata-0.17.0 → orfdata-0.18.0}/README.md +3 -0
- orfdata-0.18.0/docs/catalogs.md +89 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/__init__.py +5 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/catalog.py +2 -2
- 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.17.0 → orfdata-0.18.0}/orfdata/cli.py +21 -6
- orfdata-0.18.0/orfdata/data/catalogs.json +1 -0
- orfdata-0.18.0/orfdata/version.py +1 -0
- {orfdata-0.17.0 → orfdata-0.18.0/orfdata.egg-info}/PKG-INFO +4 -1
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/SOURCES.txt +6 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/pyproject.toml +1 -1
- orfdata-0.18.0/tests/test_catalog_registry.py +235 -0
- orfdata-0.17.0/orfdata/version.py +0 -1
- {orfdata-0.17.0 → orfdata-0.18.0}/LICENSE +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/MANIFEST.in +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/deploy.sh +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/copy-yeast.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/coverage.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/design.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/human-tissue.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/ingolia-yeast.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/measurements.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/pairing.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/pfizer-protein.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/pfizer-rna.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/reference.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/search-references.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/source-references.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/species.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/docs/storage.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/lint.sh +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/build.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/datasets.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/AUTHOR-LICENSE.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/NOTICE.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/catalog.json.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/AUTHOR-LICENSE.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/NOTICE.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/catalog.json.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/dataset.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/dataset_cli.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/datasets.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/evidence_summary.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/initiation_context.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/measurements.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/model.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/pairing.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/reference.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/reference_mapping.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/search_reference.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sequence.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/__init__.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/copy.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/ingolia.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/pfizer.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/pfizer_rna.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/wang.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/dependency_links.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/entry_points.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/requires.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/top_level.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/setup.cfg +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/test.sh +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/conftest.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/aggregate_evidence.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905474.soft +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905474_KL9_fpkm.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905476.soft +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905476_KL10_fpkm.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905478.soft +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905478_KL11_fpkm.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905479.soft +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905479_KL12_fpkm.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/NOTICE.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/geo-series.soft +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/methods.pdf +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/paper.xml +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/pmc-metadata.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/qconcat.fasta +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/workbook.xlsx +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346111.metadata.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346111.quant.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346114.metadata.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346114.quant.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346115.metadata.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346115.quant.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346116.metadata.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346116.quant.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346117.metadata.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346117.quant.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346118.metadata.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346118.quant.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346120.metadata.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346120.quant.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346122.metadata.txt +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346122.quant.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/NOTICE.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/readMe.txt.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/NOTICE.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/dog/expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/dog/manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/dog/protein.tsv +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/dog/search.fasta +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/human/expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/human/manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/human/protein.tsv +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/human/search.fasta +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/protein.tsv +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/search.fasta +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325522_family.soft.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325523_family.soft.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325525_family.soft.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/NOTICE.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/counts.csv.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-annotation.gtf.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-annotation.gtf.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/samples.xlsx +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/README.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/canis_lupus_familiaris.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/context-keys-v0.4.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/homo_sapiens.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/mus_musculus.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/saccharomyces_cerevisiae.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/regions/NOTICE.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/regions/ingolia.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/NOTICE.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/dog.fasta +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/ensembl83-expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/ensembl83-human.fasta.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/README.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/generate.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v1.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v1.sqlite +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v2.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v2.sqlite +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v3.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v3.sqlite +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/NOTICE.md +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/annotation.gtf.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/copies.zip +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/expected.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/manifest.json +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/protein.zip +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/rna.zip +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/search.fasta.gz +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_copy.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_cross_reference_pairing.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_ensembl_search_reference.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_evidence_summary.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_ingolia.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_initiation_context.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_local_build_queries.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_measurements.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_paired_selectors.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_pairing.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_pfizer.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_pfizer_rna.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_reference_context.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_reference_mapping.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_reference_orf.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_region_targets.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_search_reference.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_selectors.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_storage.py +0 -0
- {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_uorf_catalog.py +0 -0
- {orfdata-0.17.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,6 +12,8 @@ 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
|
|
17
19
|
from .evidence_summary import EvidenceSummary
|
|
@@ -45,6 +47,9 @@ __all__ = [
|
|
|
45
47
|
"ORFCatalog",
|
|
46
48
|
"ORFRecord",
|
|
47
49
|
"ORFEvidence",
|
|
50
|
+
"CatalogManifest",
|
|
51
|
+
"CatalogRegistry",
|
|
52
|
+
"build_catalog",
|
|
48
53
|
"EvidenceSummary",
|
|
49
54
|
"PeptideEvidence",
|
|
50
55
|
"ORFModel",
|
|
@@ -516,8 +516,8 @@ class ORFCatalog:
|
|
|
516
516
|
|
|
517
517
|
A locus query requires an explicit assembly. Gene and transcript lookup
|
|
518
518
|
is a discovery aid, not evidence transfer between transcript isoforms.
|
|
519
|
-
Transcript identifiers match exactly as supplied in the source
|
|
520
|
-
|
|
519
|
+
Transcript identifiers match exactly as supplied in the source,
|
|
520
|
+
including versions when present. Empty results do not prove no translation.
|
|
521
521
|
``require_unique`` means unique within the evidence source's mapping
|
|
522
522
|
scope, not unique in every normal human proteome.
|
|
523
523
|
``require_resolved`` excludes aggregate-only support: MS needs a peptide
|
|
@@ -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")
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Explicit optional catalogue downloads and builds using OpenVax DataCache."""
|
|
2
|
+
|
|
3
|
+
import gzip
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import replace
|
|
6
|
+
from importlib.resources import files
|
|
7
|
+
|
|
8
|
+
from .catalog import ORFCatalog, load_catalog
|
|
9
|
+
from .catalog_manifest import CatalogManifest
|
|
10
|
+
from .datasets import _datacache, _digest, get_data_dir
|
|
11
|
+
from .reference import species_taxon
|
|
12
|
+
|
|
13
|
+
_ASSET = "catalog.json.gz"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _build_store(manifest, cache_root):
|
|
17
|
+
return (get_data_dir(cache_root) / "catalog-imports" / manifest.catalog_id /
|
|
18
|
+
manifest.version / _digest(manifest.to_dict()))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _open(path, manifest):
|
|
22
|
+
catalog = load_catalog(path)
|
|
23
|
+
manifest.validate_catalog(catalog)
|
|
24
|
+
return catalog
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _copy_catalog(inputs, output, manifest):
|
|
28
|
+
"""Wrap pinned portable data with release provenance, retaining native facts."""
|
|
29
|
+
asset = manifest.processing.get("catalog_asset")
|
|
30
|
+
if asset not in inputs:
|
|
31
|
+
raise ValueError("Copy importer requires a declared catalog_asset")
|
|
32
|
+
catalog = load_catalog(inputs[asset])
|
|
33
|
+
metadata = catalog.metadata
|
|
34
|
+
if "catalog_manifest" in metadata:
|
|
35
|
+
raise ValueError("Copy importer expects an unmanaged source catalogue; cannot replace release provenance")
|
|
36
|
+
metadata["catalog_manifest"] = manifest.to_dict()
|
|
37
|
+
catalog = ORFCatalog(catalog.records, metadata)
|
|
38
|
+
manifest.validate_catalog(catalog)
|
|
39
|
+
with open(output, "wb") as raw, gzip.GzipFile(filename="", mode="wb", fileobj=raw, mtime=0) as handle:
|
|
40
|
+
handle.write(json.dumps(catalog.to_dict(), sort_keys=True, separators=(",", ":"), allow_nan=False).encode())
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CatalogRegistry:
|
|
44
|
+
"""Discover offline, explicitly acquire, then open the existing ORFCatalog.
|
|
45
|
+
|
|
46
|
+
Each definition names a pinned default_version, manifests, distribution
|
|
47
|
+
terms and one checksummed catalog.json.gz asset. No method except download
|
|
48
|
+
or build may acquire data. Bundled load_catalog() remains independent.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, definitions=None, *, cache_root=None):
|
|
52
|
+
if definitions is None:
|
|
53
|
+
definitions = json.loads(files("orfdata").joinpath("data/catalogs.json").read_text())
|
|
54
|
+
self._definitions = json.loads(json.dumps(definitions, allow_nan=False))
|
|
55
|
+
self._cache_root = cache_root
|
|
56
|
+
self._manifests = {}
|
|
57
|
+
self._registry = None
|
|
58
|
+
for name, entry in self._definitions.items():
|
|
59
|
+
if entry["default_version"] not in entry["versions"]:
|
|
60
|
+
raise ValueError("A catalogue default must be a pinned version")
|
|
61
|
+
for version, release in entry["versions"].items():
|
|
62
|
+
manifest = CatalogManifest.from_dict(release["manifest"])
|
|
63
|
+
if (manifest.catalog_id, manifest.version) != (name, version):
|
|
64
|
+
raise ValueError("Registry and manifest catalogue identities disagree")
|
|
65
|
+
if set(release["assets"]) != {_ASSET} or release["assets"][_ASSET].get("decompress", False):
|
|
66
|
+
raise ValueError("A catalogue release requires raw catalog.json.gz bytes")
|
|
67
|
+
distribution = release["distribution"]
|
|
68
|
+
if not isinstance(distribution.get("access_terms"), str) or not distribution["access_terms"].strip():
|
|
69
|
+
raise ValueError("Catalogue artifact access terms are required")
|
|
70
|
+
if type(distribution.get("redistributable")) is not bool:
|
|
71
|
+
raise ValueError("Catalogue artifact redistribution must be explicit")
|
|
72
|
+
self._manifests[name, version] = manifest
|
|
73
|
+
|
|
74
|
+
def _version(self, name, version):
|
|
75
|
+
if name not in self._definitions:
|
|
76
|
+
raise ValueError(f"Unknown catalogue {name!r}; use CatalogRegistry.list()")
|
|
77
|
+
version = self._definitions[name]["default_version"] if version is None else version
|
|
78
|
+
if (name, version) not in self._manifests:
|
|
79
|
+
raise ValueError(f"Unknown catalogue version {name!r} {version!r}")
|
|
80
|
+
return version
|
|
81
|
+
|
|
82
|
+
def list(self, *, species=None, taxon_id=None):
|
|
83
|
+
"""Return matching versioned manifests without downloading data."""
|
|
84
|
+
if species is not None:
|
|
85
|
+
found = species_taxon(species)
|
|
86
|
+
if found is None:
|
|
87
|
+
raise ValueError("Unknown species alias; supply its explicit taxon_id")
|
|
88
|
+
if taxon_id is not None and taxon_id != found:
|
|
89
|
+
raise ValueError("Species and taxon_id disagree")
|
|
90
|
+
taxon_id = found
|
|
91
|
+
return tuple(m for _, m in sorted(self._manifests.items()) if taxon_id is None or m.taxon_id == taxon_id)
|
|
92
|
+
|
|
93
|
+
def info(self, name, version=None):
|
|
94
|
+
return self._manifests[name, self._version(name, version)]
|
|
95
|
+
|
|
96
|
+
def download_metadata(self, name, version=None):
|
|
97
|
+
release = self._definitions[name]["versions"][self._version(name, version)]
|
|
98
|
+
return json.loads(json.dumps({k: release[k] for k in ("distribution", "assets")}))
|
|
99
|
+
|
|
100
|
+
def _data_registry(self):
|
|
101
|
+
if self._registry is None:
|
|
102
|
+
definitions = {name: dict(default_version=entry["default_version"], versions={
|
|
103
|
+
version: release["assets"] for version, release in entry["versions"].items()})
|
|
104
|
+
for name, entry in self._definitions.items()}
|
|
105
|
+
|
|
106
|
+
def store_path(name, version):
|
|
107
|
+
release = self._definitions[name]["versions"][version]
|
|
108
|
+
return get_data_dir(self._cache_root) / "catalogs" / name / version / _digest(release)
|
|
109
|
+
|
|
110
|
+
self._registry = _datacache().VersionedDatasetRegistry(definitions, store_path=store_path)
|
|
111
|
+
return self._registry
|
|
112
|
+
|
|
113
|
+
def inspect(self, name, version=None, *, verify_files=True):
|
|
114
|
+
return self._data_registry().inspect(name, self._version(name, version), verify_files=verify_files)
|
|
115
|
+
|
|
116
|
+
def download(self, name, version=None, *, force=False, source_paths=None, **options):
|
|
117
|
+
"""Explicitly install a pinned artifact; accept verified local overrides."""
|
|
118
|
+
return self._data_registry().download(name, self._version(name, version), force=force,
|
|
119
|
+
source_paths=source_paths, **options)
|
|
120
|
+
|
|
121
|
+
def open(self, name, version=None, *, verify_files=True):
|
|
122
|
+
"""Validate installed bytes and content, then return an offline ORFCatalog."""
|
|
123
|
+
manifest = self.info(name, version)
|
|
124
|
+
path = self._data_registry().local_path(name, manifest.version, asset=_ASSET, verify_files=verify_files)
|
|
125
|
+
return _open(path, manifest)
|
|
126
|
+
|
|
127
|
+
def build(self, name, version=None, *, force=False, source_paths=None, download_options=None):
|
|
128
|
+
"""Explicitly materialize an allowlisted catalogue source adapter."""
|
|
129
|
+
manifest = self.info(name, version)
|
|
130
|
+
if manifest.processing.get("importer") != "catalog-copy-v1":
|
|
131
|
+
raise ValueError("No packaged importer for this catalogue release")
|
|
132
|
+
return build_catalog(manifest, lambda inputs, output: _copy_catalog(inputs, output, manifest),
|
|
133
|
+
cache_root=self._cache_root, source_paths=source_paths, force=force,
|
|
134
|
+
download_options=download_options)
|
|
135
|
+
|
|
136
|
+
def inspect_build(self, name, version=None, *, verify_files=True):
|
|
137
|
+
"""Inspect build receipts offline; their hashes are not released-artifact pins."""
|
|
138
|
+
manifest = self.info(name, version)
|
|
139
|
+
inspected = _datacache().inspect_materialization(_build_store(manifest, self._cache_root),
|
|
140
|
+
verify_files=verify_files)
|
|
141
|
+
if inspected.status != "available":
|
|
142
|
+
return inspected
|
|
143
|
+
try:
|
|
144
|
+
expected = {s.asset_id: s for s in manifest.sources}
|
|
145
|
+
if set(inspected.sources) != set(expected) or set(inspected.files) != {_ASSET}:
|
|
146
|
+
raise ValueError("Built catalogue source/output inventory disagrees")
|
|
147
|
+
for name, source in expected.items():
|
|
148
|
+
recorded = inspected.sources[name]
|
|
149
|
+
if (recorded["sha256"] != source.sha256 or recorded["size"] != source.size
|
|
150
|
+
or recorded["verified"] is not True):
|
|
151
|
+
raise ValueError("Built catalogue source integrity pins disagree")
|
|
152
|
+
if inspected.transform != dict(version=manifest.schema, options=manifest.to_dict()):
|
|
153
|
+
raise ValueError("Built catalogue transform/manifest disagrees")
|
|
154
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
155
|
+
return replace(inspected, status="invalid", verified=False, error=exc)
|
|
156
|
+
return inspected
|
|
157
|
+
|
|
158
|
+
def open_build(self, name, version=None, *, verify_files=True):
|
|
159
|
+
"""Open the local source build explicitly; never fall back to a download."""
|
|
160
|
+
inspected = self.inspect_build(name, version, verify_files=verify_files)
|
|
161
|
+
if inspected.status == "missing":
|
|
162
|
+
raise FileNotFoundError(inspected.path)
|
|
163
|
+
if inspected.status != "available":
|
|
164
|
+
raise _datacache().FileValidationError(inspected.path, inspected.status) from inspected.error
|
|
165
|
+
return _open(inspected.files[_ASSET].path, self.info(name, version))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def build_catalog(manifest, builder, *, cache_root=None, source_paths=None, force=False, download_options=None):
|
|
169
|
+
"""Materialize pinned sources with builder(inputs, output_path), atomically.
|
|
170
|
+
|
|
171
|
+
The output is a portable ORF catalogue with its exact CatalogManifest in
|
|
172
|
+
metadata['catalog_manifest']. Source and output hashes live in DataCache's
|
|
173
|
+
receipt. Building does not register or publish a released artifact.
|
|
174
|
+
"""
|
|
175
|
+
if not isinstance(manifest, CatalogManifest):
|
|
176
|
+
raise TypeError("manifest must be a CatalogManifest")
|
|
177
|
+
source_paths = {} if source_paths is None else dict(source_paths)
|
|
178
|
+
if set(source_paths) - {s.asset_id for s in manifest.sources}:
|
|
179
|
+
raise ValueError("Unknown local catalogue source asset override")
|
|
180
|
+
sources = {}
|
|
181
|
+
for asset in manifest.sources:
|
|
182
|
+
source = dict(sha256=asset.sha256, size=asset.size)
|
|
183
|
+
if asset.asset_id in source_paths:
|
|
184
|
+
source["path"] = str(source_paths[asset.asset_id])
|
|
185
|
+
else:
|
|
186
|
+
source["url"] = asset.url
|
|
187
|
+
sources[asset.asset_id] = source
|
|
188
|
+
|
|
189
|
+
def build(inputs, outputs):
|
|
190
|
+
builder(inputs, outputs[_ASSET])
|
|
191
|
+
_open(outputs[_ASSET], manifest)
|
|
192
|
+
|
|
193
|
+
paths = _datacache().materialize(
|
|
194
|
+
_build_store(manifest, cache_root), sources,
|
|
195
|
+
transform=dict(version=manifest.schema, options=manifest.to_dict()),
|
|
196
|
+
outputs={_ASSET: {}}, builder=build, force=force, download_options=download_options)
|
|
197
|
+
return _open(paths[_ASSET], manifest)
|
|
@@ -12,16 +12,29 @@ from .catalog import EVIDENCE_METHODS, UORFCatalog, load_catalog
|
|
|
12
12
|
def run(argv=None, prog=None, *, catalog_loader=load_catalog):
|
|
13
13
|
"""Export reference records as JSON, TSV or FASTA."""
|
|
14
14
|
argv = list(sys.argv[1:] if argv is None else argv)
|
|
15
|
+
if argv and argv[0] == "catalogs":
|
|
16
|
+
from .catalog_cli import run as catalog_run
|
|
17
|
+
|
|
18
|
+
return catalog_run(argv[1:], prog=(prog or "orfdata") + " catalogs")
|
|
15
19
|
if argv and argv[0] == "datasets":
|
|
16
20
|
from .dataset_cli import run as dataset_run
|
|
17
21
|
|
|
18
22
|
return dataset_run(argv[1:], prog=(prog or "orfdata") + " datasets")
|
|
19
23
|
parser = argparse.ArgumentParser(
|
|
20
24
|
description=__doc__, prog=prog,
|
|
21
|
-
epilog="
|
|
22
|
-
parser
|
|
23
|
-
parser.
|
|
24
|
-
|
|
25
|
+
epilog="Manage optional ORF catalogues with 'orfdata catalogs --help'; quantitative data with 'orfdata datasets --help'.")
|
|
26
|
+
_add_query_arguments(parser)
|
|
27
|
+
args = parser.parse_args(argv)
|
|
28
|
+
return _query(args, parser, catalog_loader)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _add_query_arguments(parser, *, catalog_path=True):
|
|
32
|
+
if catalog_path:
|
|
33
|
+
parser.add_argument("--catalog", help="Use a compatible local JSON/JSON.gz catalogue")
|
|
34
|
+
else:
|
|
35
|
+
parser.set_defaults(catalog=None)
|
|
36
|
+
parser.add_argument("--gene", help="Exact source gene symbol or identifier")
|
|
37
|
+
parser.add_argument("--transcript", help="Exact source transcript ID, including its version when supplied")
|
|
25
38
|
parser.add_argument("--orf-id", help="Exact catalogue ORF identifier")
|
|
26
39
|
parser.add_argument("--evidence", choices=EVIDENCE_METHODS)
|
|
27
40
|
parser.add_argument("--require-resolved", action="store_true",
|
|
@@ -47,10 +60,12 @@ def run(argv=None, prog=None, *, catalog_loader=load_catalog):
|
|
|
47
60
|
parser.add_argument("--ref", help="Forward-genomic SNV REF; requires --alt and --position")
|
|
48
61
|
parser.add_argument("--alt", help="Forward-genomic SNV ALT; compare initiation contexts")
|
|
49
62
|
parser.add_argument(
|
|
50
|
-
"--genome-build", help="Required for genomic queries;
|
|
63
|
+
"--genome-build", help="Required for genomic queries; must match the selected catalogue"
|
|
51
64
|
)
|
|
52
65
|
parser.add_argument("--format", choices=("json", "tsv", "fasta"), default="json")
|
|
53
|
-
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _query(args, parser, catalog_loader):
|
|
54
69
|
if args.region and args.position:
|
|
55
70
|
parser.error("Choose --region or --position")
|
|
56
71
|
if args.position and args.format != "json":
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.18.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
|
|
@@ -5,6 +5,7 @@ deploy.sh
|
|
|
5
5
|
lint.sh
|
|
6
6
|
pyproject.toml
|
|
7
7
|
test.sh
|
|
8
|
+
docs/catalogs.md
|
|
8
9
|
docs/copy-yeast.md
|
|
9
10
|
docs/coverage.md
|
|
10
11
|
docs/design.md
|
|
@@ -22,6 +23,9 @@ docs/storage.md
|
|
|
22
23
|
orfdata/__init__.py
|
|
23
24
|
orfdata/build.py
|
|
24
25
|
orfdata/catalog.py
|
|
26
|
+
orfdata/catalog_cli.py
|
|
27
|
+
orfdata/catalog_manifest.py
|
|
28
|
+
orfdata/catalogs.py
|
|
25
29
|
orfdata/cli.py
|
|
26
30
|
orfdata/dataset.py
|
|
27
31
|
orfdata/dataset_cli.py
|
|
@@ -42,6 +46,7 @@ orfdata.egg-info/dependency_links.txt
|
|
|
42
46
|
orfdata.egg-info/entry_points.txt
|
|
43
47
|
orfdata.egg-info/requires.txt
|
|
44
48
|
orfdata.egg-info/top_level.txt
|
|
49
|
+
orfdata/data/catalogs.json
|
|
45
50
|
orfdata/data/datasets.json
|
|
46
51
|
orfdata/data/orf-evidence/AUTHOR-LICENSE.txt
|
|
47
52
|
orfdata/data/orf-evidence/NOTICE.txt
|
|
@@ -58,6 +63,7 @@ orfdata/sources/pfizer.py
|
|
|
58
63
|
orfdata/sources/pfizer_rna.py
|
|
59
64
|
orfdata/sources/wang.py
|
|
60
65
|
tests/conftest.py
|
|
66
|
+
tests/test_catalog_registry.py
|
|
61
67
|
tests/test_copy.py
|
|
62
68
|
tests/test_cross_reference_pairing.py
|
|
63
69
|
tests/test_ensembl_search_reference.py
|
|
@@ -29,7 +29,7 @@ version = {attr = "orfdata.version.__version__"}
|
|
|
29
29
|
include = ["orfdata*"]
|
|
30
30
|
|
|
31
31
|
[tool.setuptools.package-data]
|
|
32
|
-
orfdata = ["data/uorf-evidence/*", "data/orf-evidence/*", "data/datasets.json"]
|
|
32
|
+
orfdata = ["data/uorf-evidence/*", "data/orf-evidence/*", "data/datasets.json", "data/catalogs.json"]
|
|
33
33
|
|
|
34
34
|
[tool.ruff]
|
|
35
35
|
target-version = "py39"
|