orfdata 0.16.0__tar.gz → 0.18.0__tar.gz

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