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.
Files changed (182) hide show
  1. {orfdata-0.17.0/orfdata.egg-info → orfdata-0.18.0}/PKG-INFO +4 -1
  2. {orfdata-0.17.0 → orfdata-0.18.0}/README.md +3 -0
  3. orfdata-0.18.0/docs/catalogs.md +89 -0
  4. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/__init__.py +5 -0
  5. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/catalog.py +2 -2
  6. orfdata-0.18.0/orfdata/catalog_cli.py +63 -0
  7. orfdata-0.18.0/orfdata/catalog_manifest.py +101 -0
  8. orfdata-0.18.0/orfdata/catalogs.py +197 -0
  9. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/cli.py +21 -6
  10. orfdata-0.18.0/orfdata/data/catalogs.json +1 -0
  11. orfdata-0.18.0/orfdata/version.py +1 -0
  12. {orfdata-0.17.0 → orfdata-0.18.0/orfdata.egg-info}/PKG-INFO +4 -1
  13. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/SOURCES.txt +6 -0
  14. {orfdata-0.17.0 → orfdata-0.18.0}/pyproject.toml +1 -1
  15. orfdata-0.18.0/tests/test_catalog_registry.py +235 -0
  16. orfdata-0.17.0/orfdata/version.py +0 -1
  17. {orfdata-0.17.0 → orfdata-0.18.0}/LICENSE +0 -0
  18. {orfdata-0.17.0 → orfdata-0.18.0}/MANIFEST.in +0 -0
  19. {orfdata-0.17.0 → orfdata-0.18.0}/deploy.sh +0 -0
  20. {orfdata-0.17.0 → orfdata-0.18.0}/docs/copy-yeast.md +0 -0
  21. {orfdata-0.17.0 → orfdata-0.18.0}/docs/coverage.md +0 -0
  22. {orfdata-0.17.0 → orfdata-0.18.0}/docs/design.md +0 -0
  23. {orfdata-0.17.0 → orfdata-0.18.0}/docs/human-tissue.md +0 -0
  24. {orfdata-0.17.0 → orfdata-0.18.0}/docs/ingolia-yeast.md +0 -0
  25. {orfdata-0.17.0 → orfdata-0.18.0}/docs/measurements.md +0 -0
  26. {orfdata-0.17.0 → orfdata-0.18.0}/docs/pairing.md +0 -0
  27. {orfdata-0.17.0 → orfdata-0.18.0}/docs/pfizer-protein.md +0 -0
  28. {orfdata-0.17.0 → orfdata-0.18.0}/docs/pfizer-rna.md +0 -0
  29. {orfdata-0.17.0 → orfdata-0.18.0}/docs/reference.md +0 -0
  30. {orfdata-0.17.0 → orfdata-0.18.0}/docs/search-references.md +0 -0
  31. {orfdata-0.17.0 → orfdata-0.18.0}/docs/source-references.md +0 -0
  32. {orfdata-0.17.0 → orfdata-0.18.0}/docs/species.md +0 -0
  33. {orfdata-0.17.0 → orfdata-0.18.0}/docs/storage.md +0 -0
  34. {orfdata-0.17.0 → orfdata-0.18.0}/lint.sh +0 -0
  35. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/build.py +0 -0
  36. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/datasets.json +0 -0
  37. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/AUTHOR-LICENSE.txt +0 -0
  38. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/NOTICE.txt +0 -0
  39. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/catalog.json.gz +0 -0
  40. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/orf-evidence/manifest.json +0 -0
  41. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/AUTHOR-LICENSE.txt +0 -0
  42. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/NOTICE.txt +0 -0
  43. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/catalog.json.gz +0 -0
  44. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/data/uorf-evidence/manifest.json +0 -0
  45. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/dataset.py +0 -0
  46. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/dataset_cli.py +0 -0
  47. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/datasets.py +0 -0
  48. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/evidence_summary.py +0 -0
  49. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/initiation_context.py +0 -0
  50. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/measurements.py +0 -0
  51. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/model.py +0 -0
  52. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/pairing.py +0 -0
  53. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/reference.py +0 -0
  54. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/reference_mapping.py +0 -0
  55. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/search_reference.py +0 -0
  56. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sequence.py +0 -0
  57. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/__init__.py +0 -0
  58. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/copy.py +0 -0
  59. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/ingolia.py +0 -0
  60. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/pfizer.py +0 -0
  61. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/pfizer_rna.py +0 -0
  62. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata/sources/wang.py +0 -0
  63. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/dependency_links.txt +0 -0
  64. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/entry_points.txt +0 -0
  65. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/requires.txt +0 -0
  66. {orfdata-0.17.0 → orfdata-0.18.0}/orfdata.egg-info/top_level.txt +0 -0
  67. {orfdata-0.17.0 → orfdata-0.18.0}/setup.cfg +0 -0
  68. {orfdata-0.17.0 → orfdata-0.18.0}/test.sh +0 -0
  69. {orfdata-0.17.0 → orfdata-0.18.0}/tests/conftest.py +0 -0
  70. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/aggregate_evidence.json +0 -0
  71. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905474.soft +0 -0
  72. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905474_KL9_fpkm.txt.gz +0 -0
  73. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905476.soft +0 -0
  74. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905476_KL10_fpkm.txt.gz +0 -0
  75. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905478.soft +0 -0
  76. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905478_KL11_fpkm.txt.gz +0 -0
  77. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905479.soft +0 -0
  78. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/GSM1905479_KL12_fpkm.txt.gz +0 -0
  79. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/NOTICE.md +0 -0
  80. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/expected.json +0 -0
  81. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/geo-series.soft +0 -0
  82. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/manifest.json +0 -0
  83. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/methods.pdf +0 -0
  84. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/paper.xml +0 -0
  85. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/pmc-metadata.json +0 -0
  86. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/qconcat.fasta +0 -0
  87. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/copy/workbook.xlsx +0 -0
  88. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346111.metadata.txt +0 -0
  89. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346111.quant.txt.gz +0 -0
  90. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346114.metadata.txt +0 -0
  91. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346114.quant.txt.gz +0 -0
  92. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346115.metadata.txt +0 -0
  93. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346115.quant.txt.gz +0 -0
  94. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346116.metadata.txt +0 -0
  95. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346116.quant.txt.gz +0 -0
  96. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346117.metadata.txt +0 -0
  97. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346117.quant.txt.gz +0 -0
  98. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346118.metadata.txt +0 -0
  99. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346118.quant.txt.gz +0 -0
  100. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346120.metadata.txt +0 -0
  101. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346120.quant.txt.gz +0 -0
  102. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346122.metadata.txt +0 -0
  103. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/GSM346122.quant.txt.gz +0 -0
  104. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/NOTICE.md +0 -0
  105. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/expected.json +0 -0
  106. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/manifest.json +0 -0
  107. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/ingolia/readMe.txt.gz +0 -0
  108. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/NOTICE.md +0 -0
  109. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/dog/expected.json +0 -0
  110. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/dog/manifest.json +0 -0
  111. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/dog/protein.tsv +0 -0
  112. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/dog/search.fasta +0 -0
  113. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/human/expected.json +0 -0
  114. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/human/manifest.json +0 -0
  115. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/human/protein.tsv +0 -0
  116. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/human/search.fasta +0 -0
  117. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/expected.json +0 -0
  118. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/manifest.json +0 -0
  119. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/protein.tsv +0 -0
  120. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer/mouse/search.fasta +0 -0
  121. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325522_family.soft.gz +0 -0
  122. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325523_family.soft.gz +0 -0
  123. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/GSE325525_family.soft.gz +0 -0
  124. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/NOTICE.md +0 -0
  125. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/counts.csv.gz +0 -0
  126. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-annotation.gtf.gz +0 -0
  127. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-expected.json +0 -0
  128. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/dog-manifest.json +0 -0
  129. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-annotation.gtf.gz +0 -0
  130. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-expected.json +0 -0
  131. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/mouse-manifest.json +0 -0
  132. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/pfizer_rna/samples.xlsx +0 -0
  133. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/README.md +0 -0
  134. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/canis_lupus_familiaris.json +0 -0
  135. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/context-keys-v0.4.json +0 -0
  136. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/homo_sapiens.json +0 -0
  137. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/mus_musculus.json +0 -0
  138. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/references/saccharomyces_cerevisiae.json +0 -0
  139. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/regions/NOTICE.md +0 -0
  140. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/regions/ingolia.json +0 -0
  141. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/NOTICE.md +0 -0
  142. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/dog.fasta +0 -0
  143. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/ensembl83-expected.json +0 -0
  144. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/ensembl83-human.fasta.gz +0 -0
  145. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/search_reference/expected.json +0 -0
  146. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/README.md +0 -0
  147. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/generate.py +0 -0
  148. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v1.json +0 -0
  149. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v1.sqlite +0 -0
  150. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v2.json +0 -0
  151. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v2.sqlite +0 -0
  152. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v3.json +0 -0
  153. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/storage/v3.sqlite +0 -0
  154. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/NOTICE.md +0 -0
  155. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/annotation.gtf.gz +0 -0
  156. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/copies.zip +0 -0
  157. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/expected.json +0 -0
  158. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/manifest.json +0 -0
  159. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/protein.zip +0 -0
  160. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/rna.zip +0 -0
  161. {orfdata-0.17.0 → orfdata-0.18.0}/tests/data/wang/search.fasta.gz +0 -0
  162. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_copy.py +0 -0
  163. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_cross_reference_pairing.py +0 -0
  164. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_ensembl_search_reference.py +0 -0
  165. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_evidence_summary.py +0 -0
  166. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_ingolia.py +0 -0
  167. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_initiation_context.py +0 -0
  168. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_local_build_queries.py +0 -0
  169. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_measurements.py +0 -0
  170. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_paired_selectors.py +0 -0
  171. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_pairing.py +0 -0
  172. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_pfizer.py +0 -0
  173. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_pfizer_rna.py +0 -0
  174. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_reference_context.py +0 -0
  175. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_reference_mapping.py +0 -0
  176. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_reference_orf.py +0 -0
  177. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_region_targets.py +0 -0
  178. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_search_reference.py +0 -0
  179. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_selectors.py +0 -0
  180. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_storage.py +0 -0
  181. {orfdata-0.17.0 → orfdata-0.18.0}/tests/test_uorf_catalog.py +0 -0
  182. {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.17.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; these
520
- are unversioned identifiers. Empty results do not prove no translation.
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="For quantitative data management, use 'orfdata datasets --help'.")
22
- parser.add_argument("--catalog", help="Use a compatible local JSON/JSON.gz catalogue")
23
- parser.add_argument("--gene", help="Exact gene symbol or Ensembl gene ID")
24
- parser.add_argument("--transcript", help="Exact source transcript ID (unversioned)")
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; packaged data use GRCh38"
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
- args = parser.parse_args(argv)
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.17.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"