reader-workbench 1.0.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 (301) hide show
  1. reader_workbench-1.0.0/CITATION.cff +10 -0
  2. reader_workbench-1.0.0/LICENSE +21 -0
  3. reader_workbench-1.0.0/MANIFEST.in +2 -0
  4. reader_workbench-1.0.0/PKG-INFO +91 -0
  5. reader_workbench-1.0.0/README.md +55 -0
  6. reader_workbench-1.0.0/assets/reader-banner.png +0 -0
  7. reader_workbench-1.0.0/pyproject.toml +128 -0
  8. reader_workbench-1.0.0/setup.cfg +4 -0
  9. reader_workbench-1.0.0/src/reader_workbench/__init__.py +22 -0
  10. reader_workbench-1.0.0/src/reader_workbench/__main__.py +4 -0
  11. reader_workbench-1.0.0/src/reader_workbench/_version.py +17 -0
  12. reader_workbench-1.0.0/src/reader_workbench/api/__init__.py +74 -0
  13. reader_workbench-1.0.0/src/reader_workbench/api/_record_reads.py +75 -0
  14. reader_workbench-1.0.0/src/reader_workbench/api/artifacts.py +79 -0
  15. reader_workbench-1.0.0/src/reader_workbench/api/facade.py +538 -0
  16. reader_workbench-1.0.0/src/reader_workbench/api/models.py +285 -0
  17. reader_workbench-1.0.0/src/reader_workbench/api/notebooks.py +63 -0
  18. reader_workbench-1.0.0/src/reader_workbench/contracts/__init__.py +18 -0
  19. reader_workbench-1.0.0/src/reader_workbench/contracts/builtins/__init__.py +36 -0
  20. reader_workbench-1.0.0/src/reader_workbench/contracts/builtins/cytometry.py +140 -0
  21. reader_workbench-1.0.0/src/reader_workbench/contracts/builtins/four_state_event_window.py +214 -0
  22. reader_workbench-1.0.0/src/reader_workbench/contracts/builtins/generic.py +21 -0
  23. reader_workbench-1.0.0/src/reader_workbench/contracts/builtins/logic.py +149 -0
  24. reader_workbench-1.0.0/src/reader_workbench/contracts/builtins/plate_reader.py +47 -0
  25. reader_workbench-1.0.0/src/reader_workbench/contracts/catalog.py +257 -0
  26. reader_workbench-1.0.0/src/reader_workbench/contracts/model.py +109 -0
  27. reader_workbench-1.0.0/src/reader_workbench/domains/__init__.py +1 -0
  28. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/__init__.py +3 -0
  29. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/analysis/__init__.py +29 -0
  30. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/analysis/events.py +182 -0
  31. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/analysis/gating.py +175 -0
  32. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/analysis/workflow.py +248 -0
  33. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/io/__init__.py +3 -0
  34. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/io/fcs.py +135 -0
  35. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/plots/__init__.py +5 -0
  36. reader_workbench-1.0.0/src/reader_workbench/domains/cytometry/plots/diagnostic.py +155 -0
  37. reader_workbench-1.0.0/src/reader_workbench/domains/logic/__init__.py +3 -0
  38. reader_workbench-1.0.0/src/reader_workbench/domains/logic/crosstalk/__init__.py +3 -0
  39. reader_workbench-1.0.0/src/reader_workbench/domains/logic/crosstalk/pairs.py +661 -0
  40. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/__init__.py +7 -0
  41. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/builder.py +321 -0
  42. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/collection/__init__.py +20 -0
  43. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/collection/checks.py +49 -0
  44. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/collection/constants.py +29 -0
  45. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/collection/model.py +19 -0
  46. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/collection/render.py +383 -0
  47. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/collection/sources.py +185 -0
  48. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/config.py +214 -0
  49. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/diagnostic.py +361 -0
  50. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/heatmap.py +86 -0
  51. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/math.py +191 -0
  52. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/reference.py +85 -0
  53. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/selection.py +228 -0
  54. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/treatment_semantics.py +51 -0
  55. reader_workbench-1.0.0/src/reader_workbench/domains/logic/four_state_vector/validation.py +19 -0
  56. reader_workbench-1.0.0/src/reader_workbench/domains/logic/logic_symmetry/__init__.py +3 -0
  57. reader_workbench-1.0.0/src/reader_workbench/domains/logic/logic_symmetry/encodings.py +93 -0
  58. reader_workbench-1.0.0/src/reader_workbench/domains/logic/logic_symmetry/extract_corners.py +192 -0
  59. reader_workbench-1.0.0/src/reader_workbench/domains/logic/logic_symmetry/main.py +236 -0
  60. reader_workbench-1.0.0/src/reader_workbench/domains/logic/logic_symmetry/metrics.py +96 -0
  61. reader_workbench-1.0.0/src/reader_workbench/domains/logic/logic_symmetry/overlay.py +129 -0
  62. reader_workbench-1.0.0/src/reader_workbench/domains/logic/logic_symmetry/prep.py +138 -0
  63. reader_workbench-1.0.0/src/reader_workbench/domains/logic/logic_symmetry/render.py +356 -0
  64. reader_workbench-1.0.0/src/reader_workbench/domains/logic/treatment_columns.py +42 -0
  65. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/__init__.py +1 -0
  66. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/__init__.py +14 -0
  67. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/fold_change.py +474 -0
  68. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/__init__.py +21 -0
  69. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/aggregation.py +191 -0
  70. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/contract_fields.py +50 -0
  71. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/contracts.py +320 -0
  72. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/design_dispositions.py +54 -0
  73. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/disposition_records.py +140 -0
  74. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/event_sensitivity.py +27 -0
  75. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/materialize.py +274 -0
  76. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/observation_resampling.py +97 -0
  77. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/reduction.py +62 -0
  78. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/seeds.py +15 -0
  79. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/sources.py +308 -0
  80. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/well_exclusion_validation.py +94 -0
  81. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/four_state_event_window/well_exclusions.py +54 -0
  82. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/analysis/timepoints.py +76 -0
  83. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/io/__init__.py +6 -0
  84. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/io/sample_map.py +65 -0
  85. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/io/synergy_h1/__init__.py +6 -0
  86. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/io/synergy_h1/_kinetic.py +141 -0
  87. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/io/synergy_h1/_parser.py +295 -0
  88. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/io/synergy_h1/_shared.py +195 -0
  89. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/io/synergy_h1/_snapshot.py +130 -0
  90. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/ordering.py +59 -0
  91. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/__init__.py +15 -0
  92. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/_data.py +29 -0
  93. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/common.py +346 -0
  94. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/distributions.py +324 -0
  95. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/dual_reporter_triptych.py +525 -0
  96. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/dual_reporter_triptych_render.py +195 -0
  97. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/four_state_event_window/__init__.py +26 -0
  98. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic.py +295 -0
  99. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_components.py +149 -0
  100. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_render.py +308 -0
  101. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_style.py +51 -0
  102. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/four_state_event_window/schema.py +8 -0
  103. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/four_state_event_window/summary.py +140 -0
  104. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/grouping.py +53 -0
  105. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/panels/__init__.py +12 -0
  106. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/panels/snapshot.py +161 -0
  107. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/panels/snapshot_data.py +91 -0
  108. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/panels/time_series.py +296 -0
  109. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/single_reporter_diagnostic.py +435 -0
  110. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/single_reporter_diagnostic_render.py +300 -0
  111. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/snapshot_barplot/__init__.py +315 -0
  112. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/snapshot_barplot/planning.py +168 -0
  113. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/snapshot_heatmap/__init__.py +205 -0
  114. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/snapshot_heatmap/inputs.py +103 -0
  115. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/time_series.py +317 -0
  116. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/ts_and_snap/__init__.py +479 -0
  117. reader_workbench-1.0.0/src/reader_workbench/domains/plate_reader/plots/ts_and_snap/planning.py +283 -0
  118. reader_workbench-1.0.0/src/reader_workbench/domains/time_series/__init__.py +29 -0
  119. reader_workbench-1.0.0/src/reader_workbench/domains/time_series/aggregation.py +60 -0
  120. reader_workbench-1.0.0/src/reader_workbench/domains/time_series/contracts.py +368 -0
  121. reader_workbench-1.0.0/src/reader_workbench/domains/time_series/reduction.py +395 -0
  122. reader_workbench-1.0.0/src/reader_workbench/errors.py +55 -0
  123. reader_workbench-1.0.0/src/reader_workbench/maintenance/__init__.py +6 -0
  124. reader_workbench-1.0.0/src/reader_workbench/maintenance/docs.py +335 -0
  125. reader_workbench-1.0.0/src/reader_workbench/maintenance/model.py +28 -0
  126. reader_workbench-1.0.0/src/reader_workbench/maintenance/release.py +39 -0
  127. reader_workbench-1.0.0/src/reader_workbench/maintenance/skills.py +124 -0
  128. reader_workbench-1.0.0/src/reader_workbench/plotting/__init__.py +20 -0
  129. reader_workbench-1.0.0/src/reader_workbench/plotting/mpl.py +56 -0
  130. reader_workbench-1.0.0/src/reader_workbench/plotting/sinks.py +69 -0
  131. reader_workbench-1.0.0/src/reader_workbench/plotting/style.py +175 -0
  132. reader_workbench-1.0.0/src/reader_workbench/plotting/utils.py +27 -0
  133. reader_workbench-1.0.0/src/reader_workbench/plugins/__init__.py +1 -0
  134. reader_workbench-1.0.0/src/reader_workbench/plugins/catalog.py +33 -0
  135. reader_workbench-1.0.0/src/reader_workbench/plugins/export/__init__.py +0 -0
  136. reader_workbench-1.0.0/src/reader_workbench/plugins/export/_paths.py +21 -0
  137. reader_workbench-1.0.0/src/reader_workbench/plugins/export/csv.py +41 -0
  138. reader_workbench-1.0.0/src/reader_workbench/plugins/export/xlsx.py +44 -0
  139. reader_workbench-1.0.0/src/reader_workbench/plugins/ingest/__init__.py +0 -0
  140. reader_workbench-1.0.0/src/reader_workbench/plugins/ingest/_discovery.py +58 -0
  141. reader_workbench-1.0.0/src/reader_workbench/plugins/ingest/discovery_policy.py +66 -0
  142. reader_workbench-1.0.0/src/reader_workbench/plugins/ingest/flow_cytometer.py +139 -0
  143. reader_workbench-1.0.0/src/reader_workbench/plugins/ingest/synergy_h1.py +234 -0
  144. reader_workbench-1.0.0/src/reader_workbench/plugins/manifests/__init__.py +1 -0
  145. reader_workbench-1.0.0/src/reader_workbench/plugins/manifests/export.py +29 -0
  146. reader_workbench-1.0.0/src/reader_workbench/plugins/manifests/ingest.py +29 -0
  147. reader_workbench-1.0.0/src/reader_workbench/plugins/manifests/plot.py +161 -0
  148. reader_workbench-1.0.0/src/reader_workbench/plugins/manifests/transform.py +172 -0
  149. reader_workbench-1.0.0/src/reader_workbench/plugins/manifests/validator.py +18 -0
  150. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/__init__.py +0 -0
  151. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/_shared.py +55 -0
  152. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/cytometry_diagnostic.py +54 -0
  153. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/distributions.py +58 -0
  154. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/dual_reporter_triptych.py +224 -0
  155. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/four_state_event_window_diagnostic.py +133 -0
  156. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/four_state_event_window_summary.py +53 -0
  157. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/four_state_vector_collection.py +45 -0
  158. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/four_state_vector_diagnostic.py +105 -0
  159. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/four_state_vector_heatmap.py +63 -0
  160. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/logic_symmetry.py +56 -0
  161. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/single_reporter_diagnostic.py +279 -0
  162. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/snapshot_barplot.py +65 -0
  163. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/snapshot_heatmap.py +104 -0
  164. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/time_series.py +114 -0
  165. reader_workbench-1.0.0/src/reader_workbench/plugins/plot/ts_and_snap.py +210 -0
  166. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/__init__.py +0 -0
  167. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/_four_state_vector.py +204 -0
  168. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/_labeling.py +109 -0
  169. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/alias.py +70 -0
  170. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/assay_labels.py +62 -0
  171. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/blank.py +79 -0
  172. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/crosstalk_pairs.py +180 -0
  173. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/cytometry_gating.py +120 -0
  174. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/fold_change.py +79 -0
  175. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/four_state_event_window.py +93 -0
  176. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/four_state_vector.py +62 -0
  177. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/four_state_vector_collection.py +41 -0
  178. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/logic_symmetry.py +67 -0
  179. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/outlier_filter.py +60 -0
  180. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/overflow.py +197 -0
  181. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/ratio.py +237 -0
  182. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/sample_map.py +170 -0
  183. reader_workbench-1.0.0/src/reader_workbench/plugins/transform/sample_metadata.py +94 -0
  184. reader_workbench-1.0.0/src/reader_workbench/plugins/validator/__init__.py +1 -0
  185. reader_workbench-1.0.0/src/reader_workbench/plugins/validator/to_tidy_plus_map.py +155 -0
  186. reader_workbench-1.0.0/src/reader_workbench/protocols/__init__.py +80 -0
  187. reader_workbench-1.0.0/src/reader_workbench/protocols/_builtins_plate_reader_growth.py +179 -0
  188. reader_workbench-1.0.0/src/reader_workbench/protocols/_builtins_plate_reader_variants.py +274 -0
  189. reader_workbench-1.0.0/src/reader_workbench/protocols/builtins.py +1656 -0
  190. reader_workbench-1.0.0/src/reader_workbench/protocols/compiler.py +22 -0
  191. reader_workbench-1.0.0/src/reader_workbench/protocols/compilers/__init__.py +1 -0
  192. reader_workbench-1.0.0/src/reader_workbench/protocols/compilers/common.py +100 -0
  193. reader_workbench-1.0.0/src/reader_workbench/protocols/compilers/cytometry.py +87 -0
  194. reader_workbench-1.0.0/src/reader_workbench/protocols/compilers/generic.py +14 -0
  195. reader_workbench-1.0.0/src/reader_workbench/protocols/compilers/logic.py +245 -0
  196. reader_workbench-1.0.0/src/reader_workbench/protocols/compilers/plate_reader.py +937 -0
  197. reader_workbench-1.0.0/src/reader_workbench/protocols/compilers/plate_reader_pipeline.py +197 -0
  198. reader_workbench-1.0.0/src/reader_workbench/protocols/model.py +1486 -0
  199. reader_workbench-1.0.0/src/reader_workbench/protocols/semantic_coverage.py +234 -0
  200. reader_workbench-1.0.0/src/reader_workbench/runtime/__init__.py +12 -0
  201. reader_workbench-1.0.0/src/reader_workbench/runtime/builtin.py +23 -0
  202. reader_workbench-1.0.0/src/reader_workbench/runtime/model.py +42 -0
  203. reader_workbench-1.0.0/src/reader_workbench/workbench/__init__.py +60 -0
  204. reader_workbench-1.0.0/src/reader_workbench/workbench/assets/__init__.py +22 -0
  205. reader_workbench-1.0.0/src/reader_workbench/workbench/assets/types.py +118 -0
  206. reader_workbench-1.0.0/src/reader_workbench/workbench/audit/__init__.py +5 -0
  207. reader_workbench-1.0.0/src/reader_workbench/workbench/audit/experiments.py +307 -0
  208. reader_workbench-1.0.0/src/reader_workbench/workbench/audit/staging.py +187 -0
  209. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/__init__.py +51 -0
  210. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/_lazy.py +9 -0
  211. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/_records_view.py +150 -0
  212. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/_surface_execution.py +443 -0
  213. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/audit.py +95 -0
  214. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/automation.py +229 -0
  215. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/demo.py +46 -0
  216. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/dop.py +91 -0
  217. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/experiments.py +635 -0
  218. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/helpers.py +232 -0
  219. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/main.py +59 -0
  220. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/maintenance.py +82 -0
  221. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/notebooks.py +260 -0
  222. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/pagination.py +117 -0
  223. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/protocols.py +336 -0
  224. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/shared.py +309 -0
  225. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/surfaces.py +534 -0
  226. reader_workbench-1.0.0/src/reader_workbench/workbench/cli/verification.py +128 -0
  227. reader_workbench-1.0.0/src/reader_workbench/workbench/commands.py +10 -0
  228. reader_workbench-1.0.0/src/reader_workbench/workbench/config/__init__.py +47 -0
  229. reader_workbench-1.0.0/src/reader_workbench/workbench/config/identity.py +13 -0
  230. reader_workbench-1.0.0/src/reader_workbench/workbench/config/load.py +405 -0
  231. reader_workbench-1.0.0/src/reader_workbench/workbench/config/model.py +274 -0
  232. reader_workbench-1.0.0/src/reader_workbench/workbench/context.py +26 -0
  233. reader_workbench-1.0.0/src/reader_workbench/workbench/decl/__init__.py +31 -0
  234. reader_workbench-1.0.0/src/reader_workbench/workbench/decl/build.py +190 -0
  235. reader_workbench-1.0.0/src/reader_workbench/workbench/decl/model.py +81 -0
  236. reader_workbench-1.0.0/src/reader_workbench/workbench/dop/__init__.py +12 -0
  237. reader_workbench-1.0.0/src/reader_workbench/workbench/dop/builtins.py +261 -0
  238. reader_workbench-1.0.0/src/reader_workbench/workbench/dop/model.py +209 -0
  239. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/__init__.py +42 -0
  240. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/_shared.py +76 -0
  241. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/contracts.py +283 -0
  242. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/execution.py +326 -0
  243. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/file_outputs.py +260 -0
  244. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/inputs.py +161 -0
  245. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/invocations.py +507 -0
  246. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/planning.py +72 -0
  247. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/runtime.py +464 -0
  248. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/setup.py +149 -0
  249. reader_workbench-1.0.0/src/reader_workbench/workbench/engine/validation.py +684 -0
  250. reader_workbench-1.0.0/src/reader_workbench/workbench/experiment/__init__.py +47 -0
  251. reader_workbench-1.0.0/src/reader_workbench/workbench/experiment/model.py +381 -0
  252. reader_workbench-1.0.0/src/reader_workbench/workbench/experiments.py +133 -0
  253. reader_workbench-1.0.0/src/reader_workbench/workbench/graph/__init__.py +47 -0
  254. reader_workbench-1.0.0/src/reader_workbench/workbench/graph/nodes.py +102 -0
  255. reader_workbench-1.0.0/src/reader_workbench/workbench/graph/normalize.py +177 -0
  256. reader_workbench-1.0.0/src/reader_workbench/workbench/graph/refs.py +148 -0
  257. reader_workbench-1.0.0/src/reader_workbench/workbench/input_discovery.py +19 -0
  258. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/__init__.py +3 -0
  259. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/catalogs.py +128 -0
  260. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/common.py +92 -0
  261. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/dop.py +64 -0
  262. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/experiments.py +449 -0
  263. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/inventory.py +68 -0
  264. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/protocols.py +368 -0
  265. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/readiness.py +333 -0
  266. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/reports.py +367 -0
  267. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/results.py +166 -0
  268. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/runtime.py +287 -0
  269. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/semantics.py +192 -0
  270. reader_workbench-1.0.0/src/reader_workbench/workbench/inspection/validation.py +30 -0
  271. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/__init__.py +17 -0
  272. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/_launch_registry.py +112 -0
  273. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/_launch_runtime.py +104 -0
  274. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/components/__init__.py +21 -0
  275. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/components/deliverables.py +403 -0
  276. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/components/overview.py +119 -0
  277. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/eda.marimo.py.txt +153 -0
  278. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/launch.py +274 -0
  279. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/presentation.py +136 -0
  280. reader_workbench-1.0.0/src/reader_workbench/workbench/notebooks/scaffold.py +60 -0
  281. reader_workbench-1.0.0/src/reader_workbench/workbench/ontology.py +78 -0
  282. reader_workbench-1.0.0/src/reader_workbench/workbench/paths.py +44 -0
  283. reader_workbench-1.0.0/src/reader_workbench/workbench/ports/__init__.py +31 -0
  284. reader_workbench-1.0.0/src/reader_workbench/workbench/ports/model.py +168 -0
  285. reader_workbench-1.0.0/src/reader_workbench/workbench/records/__init__.py +44 -0
  286. reader_workbench-1.0.0/src/reader_workbench/workbench/records/epoch.py +329 -0
  287. reader_workbench-1.0.0/src/reader_workbench/workbench/records/evidence.py +247 -0
  288. reader_workbench-1.0.0/src/reader_workbench/workbench/records/identity.py +87 -0
  289. reader_workbench-1.0.0/src/reader_workbench/workbench/records/locking.py +185 -0
  290. reader_workbench-1.0.0/src/reader_workbench/workbench/records/model.py +711 -0
  291. reader_workbench-1.0.0/src/reader_workbench/workbench/records/sources.py +73 -0
  292. reader_workbench-1.0.0/src/reader_workbench/workbench/records/store.py +1022 -0
  293. reader_workbench-1.0.0/src/reader_workbench/workbench/records/verification.py +998 -0
  294. reader_workbench-1.0.0/src/reader_workbench/workbench/registry.py +333 -0
  295. reader_workbench-1.0.0/src/reader_workbench/workbench/spec_overrides.py +215 -0
  296. reader_workbench-1.0.0/src/reader_workbench.egg-info/PKG-INFO +91 -0
  297. reader_workbench-1.0.0/src/reader_workbench.egg-info/SOURCES.txt +299 -0
  298. reader_workbench-1.0.0/src/reader_workbench.egg-info/dependency_links.txt +1 -0
  299. reader_workbench-1.0.0/src/reader_workbench.egg-info/entry_points.txt +2 -0
  300. reader_workbench-1.0.0/src/reader_workbench.egg-info/requires.txt +15 -0
  301. reader_workbench-1.0.0/src/reader_workbench.egg-info/top_level.txt +1 -0
@@ -0,0 +1,10 @@
1
+ cff-version: 1.2.0
2
+ message: "Cite Reader and record the exact version, source revision and artifact checksum used."
3
+ type: software
4
+ title: "Reader: validated, traceable analysis of experimental instrument data"
5
+ authors:
6
+ - family-names: South
7
+ given-names: Eric J.
8
+ repository-code: "https://github.com/e-south/reader"
9
+ license: MIT
10
+ # pyproject.toml owns the version; the release workflow adds it to the bundle citation.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Eric South
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include CITATION.cff
2
+ include assets/reader-banner.png
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.4
2
+ Name: reader-workbench
3
+ Version: 1.0.0
4
+ Summary: Validated, traceable analysis workflows for experimental instrument data
5
+ Author: Eric J. South
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://github.com/e-south/reader/tree/main/docs
8
+ Project-URL: Issues, https://github.com/e-south/reader/issues
9
+ Project-URL: Repository, https://github.com/e-south/reader
10
+ Keywords: data-analysis,data-pipelines,scientific-computing,workbench
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Requires-Python: <3.13,>=3.12
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: numpy>=1.26
21
+ Requires-Dist: matplotlib>=3.8
22
+ Requires-Dist: pandas>=2.1
23
+ Requires-Dist: pydantic>=2.6
24
+ Requires-Dist: pyyaml>=6.0.2
25
+ Requires-Dist: typer>=0.16.0
26
+ Requires-Dist: click>=8.3.3
27
+ Requires-Dist: rich>=13.7
28
+ Requires-Dist: pyarrow>=23.0.1
29
+ Requires-Dist: openpyxl>=3.1
30
+ Requires-Dist: seaborn>=0.13
31
+ Requires-Dist: pillow>=12.3.0
32
+ Requires-Dist: polars>=1.36.1
33
+ Requires-Dist: flowio>=1.4.0
34
+ Requires-Dist: filelock<4,>=3.20.3
35
+ Dynamic: license-file
36
+
37
+ # ![Reader data workbench](https://raw.githubusercontent.com/e-south/reader/v1.0.0/assets/reader-banner.png)
38
+
39
+ [![Checks](https://github.com/e-south/reader/actions/workflows/checks.yaml/badge.svg?branch=main)](https://github.com/e-south/reader/actions/workflows/checks.yaml)
40
+ [![Python 3.12](https://img.shields.io/badge/python-3.12-3776AB.svg)](https://www.python.org/downloads/release/python-3120/)
41
+ [![MIT license](https://img.shields.io/badge/license-MIT-3D8068.svg)](https://github.com/e-south/reader/blob/main/LICENSE)
42
+
43
+ Reader ingests instrument files and experiment metadata, applies declared
44
+ transformations, and writes validated records, plots, exports, and notebooks.
45
+ Every unit of work—including a cross-experiment aggregate—has an owned directory
46
+ beneath `experiments/`, with source material in `inputs/`, a `reader/v8`
47
+ contract in `config.yaml`, and generated artifacts in `outputs/`.
48
+ The distribution and import package are named `reader-workbench` and
49
+ `reader_workbench`; the installed command remains `reader`.
50
+
51
+ ## Install
52
+
53
+ Install Reader as a command-line tool:
54
+
55
+ ```bash
56
+ uv tool install reader-workbench
57
+ ```
58
+
59
+ For development, install from a checkout:
60
+
61
+ ```bash
62
+ git clone https://github.com/e-south/reader.git
63
+ cd reader
64
+ uv sync --locked --group dev
65
+ uv run reader demo
66
+ ```
67
+
68
+ The installed command is `reader`:
69
+
70
+ ```bash
71
+ reader demo
72
+ reader protocols
73
+ reader init ./experiments/my_experiment --protocol plate_reader/single_reporter_screen
74
+ ```
75
+
76
+ The demo prints a guided command tour. It does not execute a pipeline or write
77
+ files. Every generated starter can be inspected and validated before data is
78
+ added.
79
+
80
+ ## Learn more
81
+
82
+ - [Getting started](https://github.com/e-south/reader/blob/main/docs/guides/getting_started.md) — install Reader, run the
83
+ demo, and scaffold a first experiment.
84
+ - [Common tasks](https://github.com/e-south/reader/blob/main/docs/guides/common_routes.md) — shortest commands for
85
+ discovery, validation, execution, and automation.
86
+ - [Python API](https://github.com/e-south/reader/blob/main/docs/core/python_api.md) — typed, task-oriented experiment and
87
+ plugin interfaces for integrations.
88
+ - [Documentation index](https://github.com/e-south/reader/blob/main/docs/README.md) — complete user, reference, and
89
+ maintainer documentation.
90
+
91
+ Reader is available under the [MIT license](https://github.com/e-south/reader/blob/main/LICENSE).
@@ -0,0 +1,55 @@
1
+ # ![Reader data workbench](https://raw.githubusercontent.com/e-south/reader/v1.0.0/assets/reader-banner.png)
2
+
3
+ [![Checks](https://github.com/e-south/reader/actions/workflows/checks.yaml/badge.svg?branch=main)](https://github.com/e-south/reader/actions/workflows/checks.yaml)
4
+ [![Python 3.12](https://img.shields.io/badge/python-3.12-3776AB.svg)](https://www.python.org/downloads/release/python-3120/)
5
+ [![MIT license](https://img.shields.io/badge/license-MIT-3D8068.svg)](https://github.com/e-south/reader/blob/main/LICENSE)
6
+
7
+ Reader ingests instrument files and experiment metadata, applies declared
8
+ transformations, and writes validated records, plots, exports, and notebooks.
9
+ Every unit of work—including a cross-experiment aggregate—has an owned directory
10
+ beneath `experiments/`, with source material in `inputs/`, a `reader/v8`
11
+ contract in `config.yaml`, and generated artifacts in `outputs/`.
12
+ The distribution and import package are named `reader-workbench` and
13
+ `reader_workbench`; the installed command remains `reader`.
14
+
15
+ ## Install
16
+
17
+ Install Reader as a command-line tool:
18
+
19
+ ```bash
20
+ uv tool install reader-workbench
21
+ ```
22
+
23
+ For development, install from a checkout:
24
+
25
+ ```bash
26
+ git clone https://github.com/e-south/reader.git
27
+ cd reader
28
+ uv sync --locked --group dev
29
+ uv run reader demo
30
+ ```
31
+
32
+ The installed command is `reader`:
33
+
34
+ ```bash
35
+ reader demo
36
+ reader protocols
37
+ reader init ./experiments/my_experiment --protocol plate_reader/single_reporter_screen
38
+ ```
39
+
40
+ The demo prints a guided command tour. It does not execute a pipeline or write
41
+ files. Every generated starter can be inspected and validated before data is
42
+ added.
43
+
44
+ ## Learn more
45
+
46
+ - [Getting started](https://github.com/e-south/reader/blob/main/docs/guides/getting_started.md) — install Reader, run the
47
+ demo, and scaffold a first experiment.
48
+ - [Common tasks](https://github.com/e-south/reader/blob/main/docs/guides/common_routes.md) — shortest commands for
49
+ discovery, validation, execution, and automation.
50
+ - [Python API](https://github.com/e-south/reader/blob/main/docs/core/python_api.md) — typed, task-oriented experiment and
51
+ plugin interfaces for integrations.
52
+ - [Documentation index](https://github.com/e-south/reader/blob/main/docs/README.md) — complete user, reference, and
53
+ maintainer documentation.
54
+
55
+ Reader is available under the [MIT license](https://github.com/e-south/reader/blob/main/LICENSE).
@@ -0,0 +1,128 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "reader-workbench"
7
+ version = "1.0.0"
8
+ description = "Validated, traceable analysis workflows for experimental instrument data"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12,<3.13"
11
+ authors = [{ name = "Eric J. South" }]
12
+ keywords = ["data-analysis", "data-pipelines", "scientific-computing", "workbench"]
13
+ license = "MIT"
14
+ license-files = ["LICENSE"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Science/Research",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Topic :: Scientific/Engineering",
22
+ ]
23
+
24
+
25
+ dependencies = [
26
+ "numpy>=1.26",
27
+ "matplotlib>=3.8",
28
+ "pandas>=2.1",
29
+ "pydantic>=2.6",
30
+ "pyyaml>=6.0.2",
31
+ "typer>=0.16.0",
32
+ "click>=8.3.3",
33
+ "rich>=13.7",
34
+ "pyarrow>=23.0.1",
35
+ "openpyxl>=3.1",
36
+ "seaborn>=0.13",
37
+ "pillow>=12.3.0",
38
+ "polars>=1.36.1",
39
+ "flowio>=1.4.0",
40
+ "filelock>=3.20.3,<4",
41
+ ]
42
+
43
+ [project.scripts]
44
+ reader = "reader_workbench.workbench.cli:main"
45
+
46
+ [project.urls]
47
+ Documentation = "https://github.com/e-south/reader/tree/main/docs"
48
+ Issues = "https://github.com/e-south/reader/issues"
49
+ Repository = "https://github.com/e-south/reader"
50
+
51
+ [tool.setuptools.package-dir]
52
+ "" = "src"
53
+
54
+ [tool.setuptools.packages.find]
55
+ where = ["src"]
56
+ include = ["reader_workbench*"]
57
+ exclude = ["reader_workbench.tests", "reader_workbench.tests.*"]
58
+ namespaces = false
59
+
60
+ [tool.setuptools.package-data]
61
+ "reader_workbench.workbench.notebooks" = ["*.marimo.py.txt"]
62
+
63
+ [dependency-groups]
64
+ dev = [
65
+ "pytest>=8.2",
66
+ "pytest-cov>=7.0",
67
+ "pip-audit>=2.10.1,<3",
68
+ "twine>=7,<8",
69
+ "ruff>=0.4",
70
+ "pre-commit>=3.7",
71
+ ]
72
+
73
+ [tool.uv]
74
+ required-version = ">=0.12.3,<0.13"
75
+
76
+ [tool.ruff]
77
+ target-version = "py312"
78
+ line-length = 120
79
+ src = ["src"]
80
+ extend-exclude = [".venv", "build", "dist", ".eggs", "*.egg-info"]
81
+
82
+ [tool.ruff.lint]
83
+ select = ["E","F","W","I","UP","B","SIM","C4","PLC","PLE"]
84
+ ignore = ["E501"]
85
+
86
+ [tool.ruff.lint.per-file-ignores]
87
+ "tests/**" = ["S101","PLR2004","B011"]
88
+ "**/*.ipynb" = ["E402"]
89
+
90
+ [tool.ruff.format]
91
+ quote-style = "double"
92
+ indent-style = "space"
93
+ line-ending = "auto"
94
+ skip-magic-trailing-comma = false
95
+ docstring-code-format = true
96
+
97
+ [tool.pytest.ini_options]
98
+ testpaths = [
99
+ "src/reader_workbench/tests",
100
+ "tests"
101
+ ]
102
+
103
+ python_files = ["test_*.py", "*_test.py"]
104
+ python_classes = ["Test*", "*Tests"]
105
+ python_functions = ["test_*"]
106
+
107
+ addopts = [
108
+ "--strict-config",
109
+ "--strict-markers",
110
+ "-ra",
111
+ ]
112
+
113
+ markers = [
114
+ "integration: cross-surface integration coverage",
115
+ ]
116
+
117
+ norecursedirs = [
118
+ ".venv", "build", "dist", ".eggs", "*.egg-info",
119
+ "outputs", "raw_data", "_scratch", ".tmp"
120
+ ]
121
+
122
+ [tool.coverage.run]
123
+ branch = true
124
+ source = ["reader_workbench"]
125
+
126
+ [tool.coverage.report]
127
+ show_missing = true
128
+ skip_covered = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ """Reader's narrow, task-oriented public entrypoint."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ if TYPE_CHECKING:
9
+ from reader_workbench.api import Experiment, open_experiment
10
+
11
+ __all__ = ["Experiment", "__version__", "open_experiment"]
12
+
13
+
14
+ def __getattr__(name: str) -> Any:
15
+ if name == "__version__":
16
+ from reader_workbench._version import package_version # noqa: PLC0415
17
+
18
+ return package_version()
19
+ if name not in __all__:
20
+ raise AttributeError(f"module 'reader_workbench' has no attribute {name!r}")
21
+ api = import_module("reader_workbench.api")
22
+ return getattr(api, name)
@@ -0,0 +1,4 @@
1
+ from reader_workbench.workbench.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ _DISTRIBUTION_NAME = "reader-workbench"
6
+
7
+
8
+ def package_version() -> str:
9
+ """Return the installed Reader distribution version."""
10
+
11
+ try:
12
+ return version(_DISTRIBUTION_NAME)
13
+ except PackageNotFoundError:
14
+ return "0+uninstalled"
15
+
16
+
17
+ __all__ = ["package_version"]
@@ -0,0 +1,74 @@
1
+ """Stable, task-oriented interfaces for Reader experiments and plugins."""
2
+
3
+ from .artifacts import ArtifactFileResult, read_artifact
4
+ from .facade import (
5
+ describe_plugin,
6
+ inspect,
7
+ notebook,
8
+ open_experiment,
9
+ plan,
10
+ plots,
11
+ plugins,
12
+ read_dataframe,
13
+ records,
14
+ run,
15
+ validate,
16
+ verify,
17
+ )
18
+ from .models import (
19
+ ApiResult,
20
+ DataFrameRecordResult,
21
+ Experiment,
22
+ ExperimentEvidence,
23
+ ExperimentIdentity,
24
+ InspectionResult,
25
+ NotebookResult,
26
+ PlanResult,
27
+ PluginCatalogResult,
28
+ PluginDescriptorResult,
29
+ PluginPort,
30
+ PluginSummary,
31
+ RecordCatalogResult,
32
+ RecordRevision,
33
+ RunResult,
34
+ SelectedSteps,
35
+ SurfaceCatalogResult,
36
+ ValidationResult,
37
+ VerificationResult,
38
+ )
39
+
40
+ __all__ = [
41
+ "ApiResult",
42
+ "ArtifactFileResult",
43
+ "DataFrameRecordResult",
44
+ "Experiment",
45
+ "ExperimentEvidence",
46
+ "ExperimentIdentity",
47
+ "InspectionResult",
48
+ "NotebookResult",
49
+ "PlanResult",
50
+ "PluginCatalogResult",
51
+ "PluginDescriptorResult",
52
+ "PluginPort",
53
+ "PluginSummary",
54
+ "RecordCatalogResult",
55
+ "RecordRevision",
56
+ "RunResult",
57
+ "SelectedSteps",
58
+ "SurfaceCatalogResult",
59
+ "ValidationResult",
60
+ "VerificationResult",
61
+ "describe_plugin",
62
+ "inspect",
63
+ "notebook",
64
+ "open_experiment",
65
+ "plan",
66
+ "plots",
67
+ "plugins",
68
+ "read_artifact",
69
+ "read_dataframe",
70
+ "records",
71
+ "run",
72
+ "validate",
73
+ "verify",
74
+ ]
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from reader_workbench.errors import RecordError
4
+ from reader_workbench.workbench.records import (
5
+ DataFrameArtifactRecord,
6
+ FileBundleRecord,
7
+ RecordStore,
8
+ record_revision_digest,
9
+ )
10
+ from reader_workbench.workbench.records.identity import is_sha256_digest
11
+
12
+ from .models import Experiment, RecordRevision
13
+
14
+
15
+ def resolve_record_revision(
16
+ experiment: Experiment,
17
+ record_id: str,
18
+ *,
19
+ revision: int | None,
20
+ revision_digest: str | None,
21
+ missing_label: str = "Record",
22
+ ) -> tuple[RecordStore, DataFrameArtifactRecord | FileBundleRecord, RecordRevision]:
23
+ """Resolve one catalog revision and prove its caller-supplied identity."""
24
+
25
+ if not isinstance(record_id, str) or not record_id.strip():
26
+ raise RecordError("record_id must be a non-empty string")
27
+ if record_id != record_id.strip():
28
+ raise RecordError("record_id must not contain leading or trailing whitespace")
29
+ if (revision is None) != (revision_digest is None):
30
+ raise RecordError("revision and revision_digest must be provided together")
31
+ if revision is not None and (isinstance(revision, bool) or not isinstance(revision, int) or revision < 1):
32
+ raise RecordError("revision must be a positive integer")
33
+ if revision_digest is not None and not is_sha256_digest(revision_digest):
34
+ raise RecordError("revision_digest must be a sha256 digest")
35
+
36
+ declaration = experiment._declaration
37
+ layout = declaration.experiment_semantics.layout
38
+ store = experiment._runtime.record_store(
39
+ layout.outputs_dir,
40
+ plots_subdir=layout.plots_subdir,
41
+ exports_subdir=layout.exports_subdir,
42
+ experiment_root=declaration.experiment.root,
43
+ create=False,
44
+ )
45
+ if not store.catalog_exists():
46
+ raise RecordError(
47
+ f"Record catalog is missing for experiment {declaration.experiment.id!r}: {store.records_path}"
48
+ )
49
+
50
+ history = store.record_history(record_id)
51
+ if not history:
52
+ raise RecordError(f"{missing_label} {record_id!r} is missing; produce it in an earlier step.")
53
+ resolved_revision = len(history) if revision is None else revision
54
+ if resolved_revision > len(history):
55
+ raise RecordError(
56
+ f"Record {record_id!r} revision {resolved_revision} is unavailable; refresh the record catalog."
57
+ )
58
+ record = history[resolved_revision - 1]
59
+ actual_revision_digest = record_revision_digest(record, outputs_dir=store.root)
60
+ if revision_digest is not None and actual_revision_digest != revision_digest:
61
+ raise RecordError(
62
+ f"Record {record_id!r} revision digest mismatch; refresh the record catalog before reading it."
63
+ )
64
+ return (
65
+ store,
66
+ record,
67
+ RecordRevision(
68
+ record_id=record.record_id,
69
+ revision=resolved_revision,
70
+ revision_digest=actual_revision_digest,
71
+ ),
72
+ )
73
+
74
+
75
+ __all__ = ["resolve_record_revision"]
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ from mimetypes import guess_type
5
+ from pathlib import Path
6
+
7
+ from reader_workbench.errors import RecordError
8
+ from reader_workbench.workbench.records import (
9
+ FileBundleRecord,
10
+ record_to_dict,
11
+ verify_record_artifact_integrity,
12
+ )
13
+
14
+ from ._record_reads import resolve_record_revision
15
+ from .models import ArtifactFileResult, Experiment
16
+
17
+
18
+ def read_artifact(
19
+ experiment: Experiment,
20
+ record_id: str,
21
+ *,
22
+ revision: int,
23
+ revision_digest: str,
24
+ path: str,
25
+ ) -> ArtifactFileResult:
26
+ """Read verified bytes for one file in an exact file-bundle record revision."""
27
+
28
+ if revision is None or revision_digest is None:
29
+ raise RecordError("read_artifact requires both revision and revision_digest")
30
+ if not isinstance(path, str) or not path.strip() or path != path.strip():
31
+ raise RecordError("path must be one non-empty outputs-relative artifact path")
32
+ requested_path = Path(path)
33
+ if requested_path.is_absolute() or ".." in requested_path.parts or requested_path == Path("."):
34
+ raise RecordError("path must be one confined outputs-relative artifact path")
35
+ normalized_path = requested_path.as_posix()
36
+ if normalized_path != path:
37
+ raise RecordError("path must use the canonical outputs-relative artifact path from records()")
38
+
39
+ store, record, bound_revision = resolve_record_revision(
40
+ experiment,
41
+ record_id,
42
+ revision=revision,
43
+ revision_digest=revision_digest,
44
+ missing_label="File-bundle record",
45
+ )
46
+ if not isinstance(record, FileBundleRecord):
47
+ raise RecordError(f"Record {record_id!r} exists but is not a file bundle")
48
+ payload = record_to_dict(record, outputs_dir=store.root)
49
+ raw_files = payload.get("files")
50
+ encoded_files = raw_files if isinstance(raw_files, list) else []
51
+ try:
52
+ file_index = encoded_files.index(normalized_path)
53
+ except ValueError as exc:
54
+ raise RecordError(
55
+ f"Artifact {normalized_path!r} is not part of record {record_id!r} revision {revision}."
56
+ ) from exc
57
+
58
+ verify_record_artifact_integrity(record, outputs_dir=store.root)
59
+ evidence_by_path = {item.relative_path.as_posix(): item for item in record.file_evidence}
60
+ evidence = evidence_by_path[normalized_path]
61
+ try:
62
+ content = record.files[file_index].read_bytes()
63
+ except OSError as exc:
64
+ raise RecordError(f"Artifact {normalized_path!r} could not be read after verification.") from exc
65
+ actual_digest = "sha256:" + hashlib.sha256(content).hexdigest()
66
+ if len(content) != evidence.size_bytes or actual_digest != evidence.content_digest:
67
+ raise RecordError(f"Artifact {normalized_path!r} changed while it was being read; refresh and retry.")
68
+ return ArtifactFileResult(
69
+ experiment=experiment.identity,
70
+ record=bound_revision,
71
+ relative_path=normalized_path,
72
+ media_type=guess_type(normalized_path)[0] or "application/octet-stream",
73
+ content_digest=evidence.content_digest,
74
+ size_bytes=evidence.size_bytes,
75
+ content=content,
76
+ )
77
+
78
+
79
+ __all__ = ["ArtifactFileResult", "read_artifact"]