reader-workbench 1.0.0__py3-none-any.whl

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 (293) hide show
  1. reader_workbench/__init__.py +22 -0
  2. reader_workbench/__main__.py +4 -0
  3. reader_workbench/_version.py +17 -0
  4. reader_workbench/api/__init__.py +74 -0
  5. reader_workbench/api/_record_reads.py +75 -0
  6. reader_workbench/api/artifacts.py +79 -0
  7. reader_workbench/api/facade.py +538 -0
  8. reader_workbench/api/models.py +285 -0
  9. reader_workbench/api/notebooks.py +63 -0
  10. reader_workbench/contracts/__init__.py +18 -0
  11. reader_workbench/contracts/builtins/__init__.py +36 -0
  12. reader_workbench/contracts/builtins/cytometry.py +140 -0
  13. reader_workbench/contracts/builtins/four_state_event_window.py +214 -0
  14. reader_workbench/contracts/builtins/generic.py +21 -0
  15. reader_workbench/contracts/builtins/logic.py +149 -0
  16. reader_workbench/contracts/builtins/plate_reader.py +47 -0
  17. reader_workbench/contracts/catalog.py +257 -0
  18. reader_workbench/contracts/model.py +109 -0
  19. reader_workbench/domains/__init__.py +1 -0
  20. reader_workbench/domains/cytometry/__init__.py +3 -0
  21. reader_workbench/domains/cytometry/analysis/__init__.py +29 -0
  22. reader_workbench/domains/cytometry/analysis/events.py +182 -0
  23. reader_workbench/domains/cytometry/analysis/gating.py +175 -0
  24. reader_workbench/domains/cytometry/analysis/workflow.py +248 -0
  25. reader_workbench/domains/cytometry/io/__init__.py +3 -0
  26. reader_workbench/domains/cytometry/io/fcs.py +135 -0
  27. reader_workbench/domains/cytometry/plots/__init__.py +5 -0
  28. reader_workbench/domains/cytometry/plots/diagnostic.py +155 -0
  29. reader_workbench/domains/logic/__init__.py +3 -0
  30. reader_workbench/domains/logic/crosstalk/__init__.py +3 -0
  31. reader_workbench/domains/logic/crosstalk/pairs.py +661 -0
  32. reader_workbench/domains/logic/four_state_vector/__init__.py +7 -0
  33. reader_workbench/domains/logic/four_state_vector/builder.py +321 -0
  34. reader_workbench/domains/logic/four_state_vector/collection/__init__.py +20 -0
  35. reader_workbench/domains/logic/four_state_vector/collection/checks.py +49 -0
  36. reader_workbench/domains/logic/four_state_vector/collection/constants.py +29 -0
  37. reader_workbench/domains/logic/four_state_vector/collection/model.py +19 -0
  38. reader_workbench/domains/logic/four_state_vector/collection/render.py +383 -0
  39. reader_workbench/domains/logic/four_state_vector/collection/sources.py +185 -0
  40. reader_workbench/domains/logic/four_state_vector/config.py +214 -0
  41. reader_workbench/domains/logic/four_state_vector/diagnostic.py +361 -0
  42. reader_workbench/domains/logic/four_state_vector/heatmap.py +86 -0
  43. reader_workbench/domains/logic/four_state_vector/math.py +191 -0
  44. reader_workbench/domains/logic/four_state_vector/reference.py +85 -0
  45. reader_workbench/domains/logic/four_state_vector/selection.py +228 -0
  46. reader_workbench/domains/logic/four_state_vector/treatment_semantics.py +51 -0
  47. reader_workbench/domains/logic/four_state_vector/validation.py +19 -0
  48. reader_workbench/domains/logic/logic_symmetry/__init__.py +3 -0
  49. reader_workbench/domains/logic/logic_symmetry/encodings.py +93 -0
  50. reader_workbench/domains/logic/logic_symmetry/extract_corners.py +192 -0
  51. reader_workbench/domains/logic/logic_symmetry/main.py +236 -0
  52. reader_workbench/domains/logic/logic_symmetry/metrics.py +96 -0
  53. reader_workbench/domains/logic/logic_symmetry/overlay.py +129 -0
  54. reader_workbench/domains/logic/logic_symmetry/prep.py +138 -0
  55. reader_workbench/domains/logic/logic_symmetry/render.py +356 -0
  56. reader_workbench/domains/logic/treatment_columns.py +42 -0
  57. reader_workbench/domains/plate_reader/__init__.py +1 -0
  58. reader_workbench/domains/plate_reader/analysis/__init__.py +14 -0
  59. reader_workbench/domains/plate_reader/analysis/fold_change.py +474 -0
  60. reader_workbench/domains/plate_reader/analysis/four_state_event_window/__init__.py +21 -0
  61. reader_workbench/domains/plate_reader/analysis/four_state_event_window/aggregation.py +191 -0
  62. reader_workbench/domains/plate_reader/analysis/four_state_event_window/contract_fields.py +50 -0
  63. reader_workbench/domains/plate_reader/analysis/four_state_event_window/contracts.py +320 -0
  64. reader_workbench/domains/plate_reader/analysis/four_state_event_window/design_dispositions.py +54 -0
  65. reader_workbench/domains/plate_reader/analysis/four_state_event_window/disposition_records.py +140 -0
  66. reader_workbench/domains/plate_reader/analysis/four_state_event_window/event_sensitivity.py +27 -0
  67. reader_workbench/domains/plate_reader/analysis/four_state_event_window/materialize.py +274 -0
  68. reader_workbench/domains/plate_reader/analysis/four_state_event_window/observation_resampling.py +97 -0
  69. reader_workbench/domains/plate_reader/analysis/four_state_event_window/reduction.py +62 -0
  70. reader_workbench/domains/plate_reader/analysis/four_state_event_window/seeds.py +15 -0
  71. reader_workbench/domains/plate_reader/analysis/four_state_event_window/sources.py +308 -0
  72. reader_workbench/domains/plate_reader/analysis/four_state_event_window/well_exclusion_validation.py +94 -0
  73. reader_workbench/domains/plate_reader/analysis/four_state_event_window/well_exclusions.py +54 -0
  74. reader_workbench/domains/plate_reader/analysis/timepoints.py +76 -0
  75. reader_workbench/domains/plate_reader/io/__init__.py +6 -0
  76. reader_workbench/domains/plate_reader/io/sample_map.py +65 -0
  77. reader_workbench/domains/plate_reader/io/synergy_h1/__init__.py +6 -0
  78. reader_workbench/domains/plate_reader/io/synergy_h1/_kinetic.py +141 -0
  79. reader_workbench/domains/plate_reader/io/synergy_h1/_parser.py +295 -0
  80. reader_workbench/domains/plate_reader/io/synergy_h1/_shared.py +195 -0
  81. reader_workbench/domains/plate_reader/io/synergy_h1/_snapshot.py +130 -0
  82. reader_workbench/domains/plate_reader/ordering.py +59 -0
  83. reader_workbench/domains/plate_reader/plots/__init__.py +15 -0
  84. reader_workbench/domains/plate_reader/plots/_data.py +29 -0
  85. reader_workbench/domains/plate_reader/plots/common.py +346 -0
  86. reader_workbench/domains/plate_reader/plots/distributions.py +324 -0
  87. reader_workbench/domains/plate_reader/plots/dual_reporter_triptych.py +525 -0
  88. reader_workbench/domains/plate_reader/plots/dual_reporter_triptych_render.py +195 -0
  89. reader_workbench/domains/plate_reader/plots/four_state_event_window/__init__.py +26 -0
  90. reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic.py +295 -0
  91. reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_components.py +149 -0
  92. reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_render.py +308 -0
  93. reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_style.py +51 -0
  94. reader_workbench/domains/plate_reader/plots/four_state_event_window/schema.py +8 -0
  95. reader_workbench/domains/plate_reader/plots/four_state_event_window/summary.py +140 -0
  96. reader_workbench/domains/plate_reader/plots/grouping.py +53 -0
  97. reader_workbench/domains/plate_reader/plots/panels/__init__.py +12 -0
  98. reader_workbench/domains/plate_reader/plots/panels/snapshot.py +161 -0
  99. reader_workbench/domains/plate_reader/plots/panels/snapshot_data.py +91 -0
  100. reader_workbench/domains/plate_reader/plots/panels/time_series.py +296 -0
  101. reader_workbench/domains/plate_reader/plots/single_reporter_diagnostic.py +435 -0
  102. reader_workbench/domains/plate_reader/plots/single_reporter_diagnostic_render.py +300 -0
  103. reader_workbench/domains/plate_reader/plots/snapshot_barplot/__init__.py +315 -0
  104. reader_workbench/domains/plate_reader/plots/snapshot_barplot/planning.py +168 -0
  105. reader_workbench/domains/plate_reader/plots/snapshot_heatmap/__init__.py +205 -0
  106. reader_workbench/domains/plate_reader/plots/snapshot_heatmap/inputs.py +103 -0
  107. reader_workbench/domains/plate_reader/plots/time_series.py +317 -0
  108. reader_workbench/domains/plate_reader/plots/ts_and_snap/__init__.py +479 -0
  109. reader_workbench/domains/plate_reader/plots/ts_and_snap/planning.py +283 -0
  110. reader_workbench/domains/time_series/__init__.py +29 -0
  111. reader_workbench/domains/time_series/aggregation.py +60 -0
  112. reader_workbench/domains/time_series/contracts.py +368 -0
  113. reader_workbench/domains/time_series/reduction.py +395 -0
  114. reader_workbench/errors.py +55 -0
  115. reader_workbench/maintenance/__init__.py +6 -0
  116. reader_workbench/maintenance/docs.py +335 -0
  117. reader_workbench/maintenance/model.py +28 -0
  118. reader_workbench/maintenance/release.py +39 -0
  119. reader_workbench/maintenance/skills.py +124 -0
  120. reader_workbench/plotting/__init__.py +20 -0
  121. reader_workbench/plotting/mpl.py +56 -0
  122. reader_workbench/plotting/sinks.py +69 -0
  123. reader_workbench/plotting/style.py +175 -0
  124. reader_workbench/plotting/utils.py +27 -0
  125. reader_workbench/plugins/__init__.py +1 -0
  126. reader_workbench/plugins/catalog.py +33 -0
  127. reader_workbench/plugins/export/__init__.py +0 -0
  128. reader_workbench/plugins/export/_paths.py +21 -0
  129. reader_workbench/plugins/export/csv.py +41 -0
  130. reader_workbench/plugins/export/xlsx.py +44 -0
  131. reader_workbench/plugins/ingest/__init__.py +0 -0
  132. reader_workbench/plugins/ingest/_discovery.py +58 -0
  133. reader_workbench/plugins/ingest/discovery_policy.py +66 -0
  134. reader_workbench/plugins/ingest/flow_cytometer.py +139 -0
  135. reader_workbench/plugins/ingest/synergy_h1.py +234 -0
  136. reader_workbench/plugins/manifests/__init__.py +1 -0
  137. reader_workbench/plugins/manifests/export.py +29 -0
  138. reader_workbench/plugins/manifests/ingest.py +29 -0
  139. reader_workbench/plugins/manifests/plot.py +161 -0
  140. reader_workbench/plugins/manifests/transform.py +172 -0
  141. reader_workbench/plugins/manifests/validator.py +18 -0
  142. reader_workbench/plugins/plot/__init__.py +0 -0
  143. reader_workbench/plugins/plot/_shared.py +55 -0
  144. reader_workbench/plugins/plot/cytometry_diagnostic.py +54 -0
  145. reader_workbench/plugins/plot/distributions.py +58 -0
  146. reader_workbench/plugins/plot/dual_reporter_triptych.py +224 -0
  147. reader_workbench/plugins/plot/four_state_event_window_diagnostic.py +133 -0
  148. reader_workbench/plugins/plot/four_state_event_window_summary.py +53 -0
  149. reader_workbench/plugins/plot/four_state_vector_collection.py +45 -0
  150. reader_workbench/plugins/plot/four_state_vector_diagnostic.py +105 -0
  151. reader_workbench/plugins/plot/four_state_vector_heatmap.py +63 -0
  152. reader_workbench/plugins/plot/logic_symmetry.py +56 -0
  153. reader_workbench/plugins/plot/single_reporter_diagnostic.py +279 -0
  154. reader_workbench/plugins/plot/snapshot_barplot.py +65 -0
  155. reader_workbench/plugins/plot/snapshot_heatmap.py +104 -0
  156. reader_workbench/plugins/plot/time_series.py +114 -0
  157. reader_workbench/plugins/plot/ts_and_snap.py +210 -0
  158. reader_workbench/plugins/transform/__init__.py +0 -0
  159. reader_workbench/plugins/transform/_four_state_vector.py +204 -0
  160. reader_workbench/plugins/transform/_labeling.py +109 -0
  161. reader_workbench/plugins/transform/alias.py +70 -0
  162. reader_workbench/plugins/transform/assay_labels.py +62 -0
  163. reader_workbench/plugins/transform/blank.py +79 -0
  164. reader_workbench/plugins/transform/crosstalk_pairs.py +180 -0
  165. reader_workbench/plugins/transform/cytometry_gating.py +120 -0
  166. reader_workbench/plugins/transform/fold_change.py +79 -0
  167. reader_workbench/plugins/transform/four_state_event_window.py +93 -0
  168. reader_workbench/plugins/transform/four_state_vector.py +62 -0
  169. reader_workbench/plugins/transform/four_state_vector_collection.py +41 -0
  170. reader_workbench/plugins/transform/logic_symmetry.py +67 -0
  171. reader_workbench/plugins/transform/outlier_filter.py +60 -0
  172. reader_workbench/plugins/transform/overflow.py +197 -0
  173. reader_workbench/plugins/transform/ratio.py +237 -0
  174. reader_workbench/plugins/transform/sample_map.py +170 -0
  175. reader_workbench/plugins/transform/sample_metadata.py +94 -0
  176. reader_workbench/plugins/validator/__init__.py +1 -0
  177. reader_workbench/plugins/validator/to_tidy_plus_map.py +155 -0
  178. reader_workbench/protocols/__init__.py +80 -0
  179. reader_workbench/protocols/_builtins_plate_reader_growth.py +179 -0
  180. reader_workbench/protocols/_builtins_plate_reader_variants.py +274 -0
  181. reader_workbench/protocols/builtins.py +1656 -0
  182. reader_workbench/protocols/compiler.py +22 -0
  183. reader_workbench/protocols/compilers/__init__.py +1 -0
  184. reader_workbench/protocols/compilers/common.py +100 -0
  185. reader_workbench/protocols/compilers/cytometry.py +87 -0
  186. reader_workbench/protocols/compilers/generic.py +14 -0
  187. reader_workbench/protocols/compilers/logic.py +245 -0
  188. reader_workbench/protocols/compilers/plate_reader.py +937 -0
  189. reader_workbench/protocols/compilers/plate_reader_pipeline.py +197 -0
  190. reader_workbench/protocols/model.py +1486 -0
  191. reader_workbench/protocols/semantic_coverage.py +234 -0
  192. reader_workbench/runtime/__init__.py +12 -0
  193. reader_workbench/runtime/builtin.py +23 -0
  194. reader_workbench/runtime/model.py +42 -0
  195. reader_workbench/workbench/__init__.py +60 -0
  196. reader_workbench/workbench/assets/__init__.py +22 -0
  197. reader_workbench/workbench/assets/types.py +118 -0
  198. reader_workbench/workbench/audit/__init__.py +5 -0
  199. reader_workbench/workbench/audit/experiments.py +307 -0
  200. reader_workbench/workbench/audit/staging.py +187 -0
  201. reader_workbench/workbench/cli/__init__.py +51 -0
  202. reader_workbench/workbench/cli/_lazy.py +9 -0
  203. reader_workbench/workbench/cli/_records_view.py +150 -0
  204. reader_workbench/workbench/cli/_surface_execution.py +443 -0
  205. reader_workbench/workbench/cli/audit.py +95 -0
  206. reader_workbench/workbench/cli/automation.py +229 -0
  207. reader_workbench/workbench/cli/demo.py +46 -0
  208. reader_workbench/workbench/cli/dop.py +91 -0
  209. reader_workbench/workbench/cli/experiments.py +635 -0
  210. reader_workbench/workbench/cli/helpers.py +232 -0
  211. reader_workbench/workbench/cli/main.py +59 -0
  212. reader_workbench/workbench/cli/maintenance.py +82 -0
  213. reader_workbench/workbench/cli/notebooks.py +260 -0
  214. reader_workbench/workbench/cli/pagination.py +117 -0
  215. reader_workbench/workbench/cli/protocols.py +336 -0
  216. reader_workbench/workbench/cli/shared.py +309 -0
  217. reader_workbench/workbench/cli/surfaces.py +534 -0
  218. reader_workbench/workbench/cli/verification.py +128 -0
  219. reader_workbench/workbench/commands.py +10 -0
  220. reader_workbench/workbench/config/__init__.py +47 -0
  221. reader_workbench/workbench/config/identity.py +13 -0
  222. reader_workbench/workbench/config/load.py +405 -0
  223. reader_workbench/workbench/config/model.py +274 -0
  224. reader_workbench/workbench/context.py +26 -0
  225. reader_workbench/workbench/decl/__init__.py +31 -0
  226. reader_workbench/workbench/decl/build.py +190 -0
  227. reader_workbench/workbench/decl/model.py +81 -0
  228. reader_workbench/workbench/dop/__init__.py +12 -0
  229. reader_workbench/workbench/dop/builtins.py +261 -0
  230. reader_workbench/workbench/dop/model.py +209 -0
  231. reader_workbench/workbench/engine/__init__.py +42 -0
  232. reader_workbench/workbench/engine/_shared.py +76 -0
  233. reader_workbench/workbench/engine/contracts.py +283 -0
  234. reader_workbench/workbench/engine/execution.py +326 -0
  235. reader_workbench/workbench/engine/file_outputs.py +260 -0
  236. reader_workbench/workbench/engine/inputs.py +161 -0
  237. reader_workbench/workbench/engine/invocations.py +507 -0
  238. reader_workbench/workbench/engine/planning.py +72 -0
  239. reader_workbench/workbench/engine/runtime.py +464 -0
  240. reader_workbench/workbench/engine/setup.py +149 -0
  241. reader_workbench/workbench/engine/validation.py +684 -0
  242. reader_workbench/workbench/experiment/__init__.py +47 -0
  243. reader_workbench/workbench/experiment/model.py +381 -0
  244. reader_workbench/workbench/experiments.py +133 -0
  245. reader_workbench/workbench/graph/__init__.py +47 -0
  246. reader_workbench/workbench/graph/nodes.py +102 -0
  247. reader_workbench/workbench/graph/normalize.py +177 -0
  248. reader_workbench/workbench/graph/refs.py +148 -0
  249. reader_workbench/workbench/input_discovery.py +19 -0
  250. reader_workbench/workbench/inspection/__init__.py +3 -0
  251. reader_workbench/workbench/inspection/catalogs.py +128 -0
  252. reader_workbench/workbench/inspection/common.py +92 -0
  253. reader_workbench/workbench/inspection/dop.py +64 -0
  254. reader_workbench/workbench/inspection/experiments.py +449 -0
  255. reader_workbench/workbench/inspection/inventory.py +68 -0
  256. reader_workbench/workbench/inspection/protocols.py +368 -0
  257. reader_workbench/workbench/inspection/readiness.py +333 -0
  258. reader_workbench/workbench/inspection/reports.py +367 -0
  259. reader_workbench/workbench/inspection/results.py +166 -0
  260. reader_workbench/workbench/inspection/runtime.py +287 -0
  261. reader_workbench/workbench/inspection/semantics.py +192 -0
  262. reader_workbench/workbench/inspection/validation.py +30 -0
  263. reader_workbench/workbench/notebooks/__init__.py +17 -0
  264. reader_workbench/workbench/notebooks/_launch_registry.py +112 -0
  265. reader_workbench/workbench/notebooks/_launch_runtime.py +104 -0
  266. reader_workbench/workbench/notebooks/components/__init__.py +21 -0
  267. reader_workbench/workbench/notebooks/components/deliverables.py +403 -0
  268. reader_workbench/workbench/notebooks/components/overview.py +119 -0
  269. reader_workbench/workbench/notebooks/eda.marimo.py.txt +153 -0
  270. reader_workbench/workbench/notebooks/launch.py +274 -0
  271. reader_workbench/workbench/notebooks/presentation.py +136 -0
  272. reader_workbench/workbench/notebooks/scaffold.py +60 -0
  273. reader_workbench/workbench/ontology.py +78 -0
  274. reader_workbench/workbench/paths.py +44 -0
  275. reader_workbench/workbench/ports/__init__.py +31 -0
  276. reader_workbench/workbench/ports/model.py +168 -0
  277. reader_workbench/workbench/records/__init__.py +44 -0
  278. reader_workbench/workbench/records/epoch.py +329 -0
  279. reader_workbench/workbench/records/evidence.py +247 -0
  280. reader_workbench/workbench/records/identity.py +87 -0
  281. reader_workbench/workbench/records/locking.py +185 -0
  282. reader_workbench/workbench/records/model.py +711 -0
  283. reader_workbench/workbench/records/sources.py +73 -0
  284. reader_workbench/workbench/records/store.py +1022 -0
  285. reader_workbench/workbench/records/verification.py +998 -0
  286. reader_workbench/workbench/registry.py +333 -0
  287. reader_workbench/workbench/spec_overrides.py +215 -0
  288. reader_workbench-1.0.0.dist-info/METADATA +91 -0
  289. reader_workbench-1.0.0.dist-info/RECORD +293 -0
  290. reader_workbench-1.0.0.dist-info/WHEEL +5 -0
  291. reader_workbench-1.0.0.dist-info/entry_points.txt +2 -0
  292. reader_workbench-1.0.0.dist-info/licenses/LICENSE +21 -0
  293. reader_workbench-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,234 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping, Sequence
4
+ from pathlib import Path
5
+ from typing import Literal, Self
6
+
7
+ import pandas as pd
8
+ from pydantic import Field, field_validator, model_validator
9
+
10
+ from reader_workbench.domains.plate_reader.io.synergy_h1 import (
11
+ parse_kinetic_only,
12
+ parse_snapshot_and_timeseries,
13
+ probe_synergy_workbook,
14
+ )
15
+ from reader_workbench.errors import ParseError
16
+ from reader_workbench.plugins.ingest._discovery import discover_auto_input_files
17
+ from reader_workbench.plugins.ingest.discovery_policy import DEFAULT_EXCLUDE, DEFAULT_INCLUDE
18
+ from reader_workbench.workbench.ports import dataframe_output, file_path_input
19
+ from reader_workbench.workbench.registry import Plugin, PluginConfig, PreflightIssue
20
+
21
+
22
+ class SynergyH1UnifiedCfg(PluginConfig):
23
+ # What kind of ingest are we doing?
24
+ mode: Literal["snapshot_only", "kinetic_only", "mixed"] = "mixed"
25
+
26
+ # parsing knobs
27
+ channels: list[str] | None = None
28
+ channel_map: Mapping[str, str] | None = None
29
+ sheet_names: Sequence[str] | None = None
30
+
31
+ # time normalization
32
+ time_round_decimals: int | None = 12
33
+ time_step_h: float | None = None
34
+ time_offset_h: float = Field(
35
+ default=0.0,
36
+ ge=0.0,
37
+ allow_inf_nan=False,
38
+ description="Hours added to parsed measurement times when assay age is known outside the workbook.",
39
+ )
40
+
41
+ # auto-discovery knobs
42
+ auto_roots: list[str] | None = None
43
+ auto_include: list[str] = Field(default_factory=lambda: list(DEFAULT_INCLUDE))
44
+ auto_exclude: list[str] = Field(default_factory=lambda: list(DEFAULT_EXCLUDE))
45
+ auto_pick: Literal["single", "latest"] = "single"
46
+ auto_recursive: bool = False
47
+
48
+ # logging
49
+ print_summary: bool = True
50
+
51
+ @field_validator("time_offset_h", mode="before")
52
+ @classmethod
53
+ def reject_boolean_time_offset(cls, value: object) -> object:
54
+ if isinstance(value, bool):
55
+ raise ValueError("time_offset_h must be a non-negative finite number, not a boolean")
56
+ return value
57
+
58
+ @model_validator(mode="after")
59
+ def require_channel_contract(self) -> Self:
60
+ if self.mode in {"snapshot_only", "mixed"} and not self.channel_map:
61
+ raise ValueError(f"Synergy H1 mode {self.mode!r} requires an explicit channel_map")
62
+ if not self.channels and not self.channel_map:
63
+ raise ValueError("Synergy H1 ingest requires channels or channel_map")
64
+ return self
65
+
66
+
67
+ class SynergyH1(Plugin):
68
+ """Unified Synergy H1 ingest (snapshot-only, kinetic-only, or mixed)."""
69
+
70
+ ConfigModel = SynergyH1UnifiedCfg
71
+
72
+ @classmethod
73
+ def input_ports(cls):
74
+ return {"raw": file_path_input("raw", optional=True)}
75
+
76
+ @classmethod
77
+ def output_ports(cls):
78
+ return {"df": dataframe_output("df", "tidy.v1")}
79
+
80
+ @classmethod
81
+ def _selected_workbooks(
82
+ cls,
83
+ *,
84
+ exp_dir: Path,
85
+ cfg: SynergyH1UnifiedCfg,
86
+ inputs: Mapping[str, object],
87
+ ) -> list[Path]:
88
+ if "raw" in inputs:
89
+ raw_value = inputs["raw"]
90
+ raw_path = raw_value if isinstance(raw_value, Path) else getattr(raw_value, "path", None)
91
+ if raw_path is None:
92
+ raise ParseError("Synergy H1 raw input must select a workbook file")
93
+ path = Path(raw_path)
94
+ return [path if path.is_absolute() else exp_dir / path]
95
+ return discover_auto_input_files(
96
+ exp_dir=exp_dir,
97
+ auto_roots=cfg.auto_roots,
98
+ auto_include=cfg.auto_include,
99
+ auto_exclude=cfg.auto_exclude,
100
+ auto_recursive=cfg.auto_recursive,
101
+ auto_pick=cfg.auto_pick,
102
+ discovery_label="raw .xlsx files",
103
+ singular_label="workbook",
104
+ )
105
+
106
+ @classmethod
107
+ def resolve_missing_file_inputs(cls, *, exp_dir, cfg: SynergyH1UnifiedCfg, inputs):
108
+ if "raw" in inputs:
109
+ return {}
110
+ selected = cls._selected_workbooks(exp_dir=exp_dir, cfg=cfg, inputs=inputs)
111
+ return {"raw": selected[0]}
112
+
113
+ @classmethod
114
+ def preflight_readiness(cls, *, exp_dir, cfg: SynergyH1UnifiedCfg, reads):
115
+ try:
116
+ selected = cls._selected_workbooks(exp_dir=exp_dir, cfg=cfg, inputs=reads)
117
+ except ParseError as err:
118
+ return (PreflightIssue(kind="file", message=str(err)),)
119
+
120
+ issues: list[PreflightIssue] = []
121
+ for path in selected:
122
+ try:
123
+ probe_synergy_workbook(path)
124
+ except Exception as err:
125
+ issues.append(
126
+ PreflightIssue(
127
+ kind="file",
128
+ message=f"Synergy H1 workbook {path} could not be opened: {err}",
129
+ )
130
+ )
131
+ return tuple(issues)
132
+
133
+ def _log_df_summary(self, ctx, df: pd.DataFrame, files_count: int, mode: str):
134
+ try:
135
+ n_rows = len(df)
136
+ n_pos = df["position"].nunique()
137
+ chans = sorted(df["channel"].astype(str).unique().tolist())
138
+ tmin = float(pd.to_numeric(df["time"]).min()) if n_rows else 0.0
139
+ tmax = float(pd.to_numeric(df["time"]).max()) if n_rows else 0.0
140
+ src_counts = df["source"].value_counts().to_dict() if "source" in df.columns else {}
141
+ sheets = int(df["sheet_name"].nunique()) if "sheet_name" in df.columns else 0
142
+ ctx.logger.info(
143
+ "Synergy H1 ingest • files=%d • mode=[bold]%s[/bold] • rows=%d • positions=%d • channels=%d "
144
+ "• time=[%.2f, %.2f] h • sheets=%d • parts=%s",
145
+ files_count,
146
+ mode,
147
+ n_rows,
148
+ n_pos,
149
+ len(chans),
150
+ tmin,
151
+ tmax,
152
+ sheets,
153
+ src_counts or "{}",
154
+ )
155
+ if chans:
156
+ preview = ", ".join(chans[:8]) + (" …" if len(chans) > 8 else "")
157
+ ctx.logger.debug("channels: %s", preview)
158
+
159
+ # Block presence line (unambiguous snapshot/kinetic summary)
160
+ snap_n = int(src_counts.get("snapshot", 0))
161
+ kin_n = int(src_counts.get("kinetic", 0))
162
+ snap_flag = "YES" if snap_n > 0 else "NO"
163
+ kin_flag = "YES" if kin_n > 0 else "NO"
164
+ ctx.logger.debug(
165
+ "parsed blocks • snapshot=%s (%d rows) • kinetic=%s (%d rows)", snap_flag, snap_n, kin_flag, kin_n
166
+ )
167
+
168
+ except Exception:
169
+ # Logging should never break the pipeline
170
+ pass
171
+
172
+ # ---------- run ----------
173
+
174
+ def run(self, ctx, inputs, cfg: SynergyH1UnifiedCfg):
175
+ effective_mode = cfg.mode
176
+
177
+ try:
178
+ raw_path = inputs.get("raw")
179
+ if not isinstance(raw_path, Path):
180
+ raise ParseError("Synergy H1 runtime requires one resolved raw workbook path")
181
+
182
+ if cfg.print_summary:
183
+ ctx.logger.info(
184
+ "[muted]Synergy H1 ingest • workbook selected[/muted] • %s",
185
+ raw_path.name,
186
+ )
187
+ if effective_mode == "kinetic_only":
188
+ out = parse_kinetic_only(
189
+ raw_path,
190
+ channels=cfg.channels,
191
+ channel_map=cfg.channel_map,
192
+ sheet_names=cfg.sheet_names,
193
+ time_round_decimals=cfg.time_round_decimals,
194
+ time_step_h=cfg.time_step_h,
195
+ time_offset_h=cfg.time_offset_h,
196
+ )
197
+ elif effective_mode == "snapshot_only":
198
+ out = parse_snapshot_and_timeseries(
199
+ raw_path,
200
+ channels=cfg.channels,
201
+ channel_map=cfg.channel_map,
202
+ sheet_names=cfg.sheet_names,
203
+ time_round_decimals=cfg.time_round_decimals,
204
+ time_step_h=cfg.time_step_h,
205
+ time_offset_h=cfg.time_offset_h,
206
+ include_snapshot=True,
207
+ include_kinetic=False,
208
+ )
209
+ elif effective_mode == "mixed":
210
+ out = parse_snapshot_and_timeseries(
211
+ raw_path,
212
+ channels=cfg.channels,
213
+ channel_map=cfg.channel_map,
214
+ sheet_names=cfg.sheet_names,
215
+ time_round_decimals=cfg.time_round_decimals,
216
+ time_step_h=cfg.time_step_h,
217
+ time_offset_h=cfg.time_offset_h,
218
+ include_snapshot=True,
219
+ include_kinetic=True,
220
+ )
221
+ else:
222
+ raise ParseError(
223
+ f"Unsupported Synergy H1 mode {effective_mode!r}; expected snapshot_only, kinetic_only, or mixed"
224
+ )
225
+
226
+ if cfg.print_summary:
227
+ self._log_df_summary(ctx, out, files_count=1, mode=effective_mode)
228
+
229
+ except ParseError:
230
+ raise
231
+ except Exception as e:
232
+ raise ParseError(f"Synergy H1 ingest failed: {e}") from e
233
+
234
+ return {"df": out}
@@ -0,0 +1 @@
1
+ """Category-owned manifests for Reader's built-in plugins."""
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from reader_workbench.plugins.export.csv import ExportCsv
4
+ from reader_workbench.plugins.export.xlsx import ExportXlsx
5
+ from reader_workbench.workbench.assets import AssetDescriptor, build_plugin_asset
6
+ from reader_workbench.workbench.ontology import PluginSemantics
7
+
8
+ BUILTIN_PLUGIN_DESCRIPTORS: tuple[AssetDescriptor, ...] = (
9
+ build_plugin_asset(
10
+ plugin_id="export/csv",
11
+ semantics=PluginSemantics(
12
+ domain="generic",
13
+ family="table_export",
14
+ summary="Write dataframe records to CSV files.",
15
+ tags=("csv", "files"),
16
+ ),
17
+ plugin_cls=ExportCsv,
18
+ ),
19
+ build_plugin_asset(
20
+ plugin_id="export/xlsx",
21
+ semantics=PluginSemantics(
22
+ domain="generic",
23
+ family="table_export",
24
+ summary="Write dataframe records to XLSX workbooks.",
25
+ tags=("xlsx", "files"),
26
+ ),
27
+ plugin_cls=ExportXlsx,
28
+ ),
29
+ )
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from reader_workbench.plugins.ingest.flow_cytometer import FlowCytometerIngest
4
+ from reader_workbench.plugins.ingest.synergy_h1 import SynergyH1
5
+ from reader_workbench.workbench.assets import AssetDescriptor, build_plugin_asset
6
+ from reader_workbench.workbench.ontology import PluginSemantics
7
+
8
+ BUILTIN_PLUGIN_DESCRIPTORS: tuple[AssetDescriptor, ...] = (
9
+ build_plugin_asset(
10
+ plugin_id="ingest/flow_cytometer",
11
+ semantics=PluginSemantics(
12
+ domain="cytometry",
13
+ family="fcs_ingest",
14
+ summary="Parse FCS cytometry files into tidy event tables and channel metadata.",
15
+ tags=("fcs", "events", "channels"),
16
+ ),
17
+ plugin_cls=FlowCytometerIngest,
18
+ ),
19
+ build_plugin_asset(
20
+ plugin_id="ingest/synergy_h1",
21
+ semantics=PluginSemantics(
22
+ domain="plate_reader",
23
+ family="workbook_ingest",
24
+ summary="Parse Synergy H1 workbooks into tidy plate-reader traces.",
25
+ tags=("xlsx", "kinetic", "snapshot"),
26
+ ),
27
+ plugin_cls=SynergyH1,
28
+ ),
29
+ )
@@ -0,0 +1,161 @@
1
+ from __future__ import annotations
2
+
3
+ from reader_workbench.plugins.plot.cytometry_diagnostic import CytometryDiagnosticPlot
4
+ from reader_workbench.plugins.plot.distributions import DistributionsPlot
5
+ from reader_workbench.plugins.plot.dual_reporter_triptych import DualReporterTriptychPlot
6
+ from reader_workbench.plugins.plot.four_state_event_window_diagnostic import FourStateEventWindowDiagnosticPlot
7
+ from reader_workbench.plugins.plot.four_state_event_window_summary import FourStateEventWindowSummaryPlot
8
+ from reader_workbench.plugins.plot.four_state_vector_collection import FourStateVectorCollectionHeatmapPlot
9
+ from reader_workbench.plugins.plot.four_state_vector_diagnostic import FourStateVectorDiagnosticPlot
10
+ from reader_workbench.plugins.plot.four_state_vector_heatmap import FourStateVectorHeatmapPlot
11
+ from reader_workbench.plugins.plot.logic_symmetry import LogicSymmetryPlot
12
+ from reader_workbench.plugins.plot.single_reporter_diagnostic import SingleReporterDiagnosticPlot
13
+ from reader_workbench.plugins.plot.snapshot_barplot import SnapshotBarplot
14
+ from reader_workbench.plugins.plot.snapshot_heatmap import SnapshotHeatmapPlot
15
+ from reader_workbench.plugins.plot.time_series import TimeSeriesPlot
16
+ from reader_workbench.plugins.plot.ts_and_snap import TSAndSnapPlot
17
+ from reader_workbench.workbench.assets import AssetDescriptor, build_plugin_asset
18
+ from reader_workbench.workbench.ontology import PluginSemantics
19
+
20
+ BUILTIN_PLUGIN_DESCRIPTORS: tuple[AssetDescriptor, ...] = (
21
+ build_plugin_asset(
22
+ plugin_id="plot/cytometry_diagnostic",
23
+ semantics=PluginSemantics(
24
+ domain="cytometry",
25
+ family="gating_diagnostic",
26
+ summary="Render configured cells, singlets, fluorescence, and retention diagnostics.",
27
+ tags=("gating", "fluorescence", "qc", "records"),
28
+ ),
29
+ plugin_cls=CytometryDiagnosticPlot,
30
+ ),
31
+ build_plugin_asset(
32
+ plugin_id="plot/dual_reporter_triptych",
33
+ semantics=PluginSemantics(
34
+ domain="plate_reader",
35
+ family="composite_plot",
36
+ summary="Render per-design growth, reporter-ratio, and endpoint panels.",
37
+ tags=("dual_reporter", "kinetics", "snapshot"),
38
+ ),
39
+ plugin_cls=DualReporterTriptychPlot,
40
+ ),
41
+ build_plugin_asset(
42
+ plugin_id="plot/single_reporter_diagnostic",
43
+ semantics=PluginSemantics(
44
+ domain="plate_reader",
45
+ family="composite_plot",
46
+ summary="Render single-reporter kinetics, an explicit reduction, and normalizer QC in one row.",
47
+ tags=("single_reporter", "kinetics", "reduction", "qc"),
48
+ ),
49
+ plugin_cls=SingleReporterDiagnosticPlot,
50
+ ),
51
+ build_plugin_asset(
52
+ plugin_id="plot/four_state_event_window_diagnostic",
53
+ semantics=PluginSemantics(
54
+ domain="plate_reader",
55
+ family="event_relative_diagnostic",
56
+ summary="Render trajectories and reduced components for one explicitly identified source design.",
57
+ tags=("event", "window", "diagnostic", "reduction"),
58
+ ),
59
+ plugin_cls=FourStateEventWindowDiagnosticPlot,
60
+ ),
61
+ build_plugin_asset(
62
+ plugin_id="plot/four_state_event_window_summary",
63
+ semantics=PluginSemantics(
64
+ domain="plate_reader",
65
+ family="event_relative_summary",
66
+ summary="Render primary event-relative components across source records.",
67
+ tags=("event", "window", "aggregate", "summary"),
68
+ ),
69
+ plugin_cls=FourStateEventWindowSummaryPlot,
70
+ ),
71
+ build_plugin_asset(
72
+ plugin_id="plot/logic_symmetry",
73
+ semantics=PluginSemantics(
74
+ domain="logic",
75
+ family="geometry_plot",
76
+ summary="Render logic symmetry geometry from a persisted summary record.",
77
+ tags=("logic", "geometry"),
78
+ ),
79
+ plugin_cls=LogicSymmetryPlot,
80
+ ),
81
+ build_plugin_asset(
82
+ plugin_id="plot/four_state_vector_diagnostic",
83
+ semantics=PluginSemantics(
84
+ domain="logic",
85
+ family="four_state_vector_diagnostic",
86
+ summary="Render per-design trajectories beside persisted four-state vector components.",
87
+ tags=("logic", "four_state_vector", "diagnostic", "records"),
88
+ ),
89
+ plugin_cls=FourStateVectorDiagnosticPlot,
90
+ ),
91
+ build_plugin_asset(
92
+ plugin_id="plot/four_state_vector_heatmap",
93
+ semantics=PluginSemantics(
94
+ domain="logic",
95
+ family="four_state_vector_heatmap",
96
+ summary="Render a heatmap over one experiment's four-state vector logic-shape and reference-normalized intensity channels.",
97
+ tags=("logic", "four_state_vector", "vector", "heatmap"),
98
+ ),
99
+ plugin_cls=FourStateVectorHeatmapPlot,
100
+ ),
101
+ build_plugin_asset(
102
+ plugin_id="plot/four_state_vector_collection",
103
+ semantics=PluginSemantics(
104
+ domain="logic",
105
+ family="four_state_vector_heatmap",
106
+ summary="Render a four-state vector heatmap over a provenance-bound record collection.",
107
+ tags=("logic", "four_state_vector", "collection", "heatmap"),
108
+ ),
109
+ plugin_cls=FourStateVectorCollectionHeatmapPlot,
110
+ ),
111
+ build_plugin_asset(
112
+ plugin_id="plot/ts_and_snap",
113
+ semantics=PluginSemantics(
114
+ domain="plate_reader",
115
+ family="composite_plot",
116
+ summary="Render paired time-series and snapshot summary panels.",
117
+ tags=("time_series", "snapshot"),
118
+ ),
119
+ plugin_cls=TSAndSnapPlot,
120
+ ),
121
+ build_plugin_asset(
122
+ plugin_id="plot/distributions",
123
+ semantics=PluginSemantics(
124
+ domain="plate_reader",
125
+ family="distribution",
126
+ summary="Render channel-wise distribution plots from tidy measurements.",
127
+ tags=("density", "qc"),
128
+ ),
129
+ plugin_cls=DistributionsPlot,
130
+ ),
131
+ build_plugin_asset(
132
+ plugin_id="plot/snapshot_barplot",
133
+ semantics=PluginSemantics(
134
+ domain="plate_reader",
135
+ family="snapshot_bar",
136
+ summary="Render grouped snapshot barplots at a selected timepoint.",
137
+ tags=("snapshot", "bars"),
138
+ ),
139
+ plugin_cls=SnapshotBarplot,
140
+ ),
141
+ build_plugin_asset(
142
+ plugin_id="plot/snapshot_heatmap",
143
+ semantics=PluginSemantics(
144
+ domain="plate_reader",
145
+ family="snapshot_heatmap",
146
+ summary="Render heatmaps for snapshot or fold-change plate-reader summaries.",
147
+ tags=("snapshot", "heatmap"),
148
+ ),
149
+ plugin_cls=SnapshotHeatmapPlot,
150
+ ),
151
+ build_plugin_asset(
152
+ plugin_id="plot/time_series",
153
+ semantics=PluginSemantics(
154
+ domain="plate_reader",
155
+ family="time_series",
156
+ summary="Render grouped time-series plots from tidy plate-reader traces.",
157
+ tags=("kinetics", "channels"),
158
+ ),
159
+ plugin_cls=TimeSeriesPlot,
160
+ ),
161
+ )
@@ -0,0 +1,172 @@
1
+ from __future__ import annotations
2
+
3
+ from reader_workbench.plugins.transform.alias import AliasTransform
4
+ from reader_workbench.plugins.transform.assay_labels import AnnotationLabelsTransform
5
+ from reader_workbench.plugins.transform.blank import BlankCorrection
6
+ from reader_workbench.plugins.transform.crosstalk_pairs import CrosstalkPairs
7
+ from reader_workbench.plugins.transform.cytometry_gating import CytometryGatingTransform
8
+ from reader_workbench.plugins.transform.fold_change import FoldChange
9
+ from reader_workbench.plugins.transform.four_state_event_window import FourStateEventWindowTransform
10
+ from reader_workbench.plugins.transform.four_state_vector import FourStateVectorTransform
11
+ from reader_workbench.plugins.transform.four_state_vector_collection import FourStateVectorCollectionTransform
12
+ from reader_workbench.plugins.transform.logic_symmetry import LogicSymmetryTransform
13
+ from reader_workbench.plugins.transform.outlier_filter import OutlierFilter
14
+ from reader_workbench.plugins.transform.overflow import OverflowHandling
15
+ from reader_workbench.plugins.transform.ratio import RatioTransform
16
+ from reader_workbench.plugins.transform.sample_map import SampleMapMerge
17
+ from reader_workbench.plugins.transform.sample_metadata import SampleMetadataMerge
18
+ from reader_workbench.workbench.assets import AssetDescriptor, build_plugin_asset
19
+ from reader_workbench.workbench.ontology import PluginSemantics
20
+
21
+ BUILTIN_PLUGIN_DESCRIPTORS: tuple[AssetDescriptor, ...] = (
22
+ build_plugin_asset(
23
+ plugin_id="transform/alias",
24
+ semantics=PluginSemantics(
25
+ domain="generic",
26
+ family="label_enrichment",
27
+ summary="Add alias columns for configured categorical metadata.",
28
+ tags=("aliases", "annotation"),
29
+ ),
30
+ plugin_cls=AliasTransform,
31
+ ),
32
+ build_plugin_asset(
33
+ plugin_id="transform/assay_labels",
34
+ semantics=PluginSemantics(
35
+ domain="generic",
36
+ family="label_enrichment",
37
+ summary="Materialize configured annotations.labels into dataframe columns.",
38
+ tags=("annotations", "labels", "annotation"),
39
+ ),
40
+ plugin_cls=AnnotationLabelsTransform,
41
+ ),
42
+ build_plugin_asset(
43
+ plugin_id="transform/outlier_filter",
44
+ semantics=PluginSemantics(
45
+ domain="generic",
46
+ family="quality_filter",
47
+ summary="Drop outlier tidy rows using per-channel and per-time z-scores.",
48
+ tags=("qc", "filtering"),
49
+ ),
50
+ plugin_cls=OutlierFilter,
51
+ ),
52
+ build_plugin_asset(
53
+ plugin_id="transform/crosstalk_pairs",
54
+ semantics=PluginSemantics(
55
+ domain="logic",
56
+ family="summary_transform",
57
+ summary="Derive pairwise logic crosstalk rankings from fold-change tables.",
58
+ tags=("logic", "pairs", "summary"),
59
+ ),
60
+ plugin_cls=CrosstalkPairs,
61
+ ),
62
+ build_plugin_asset(
63
+ plugin_id="transform/cytometry_gating",
64
+ semantics=PluginSemantics(
65
+ domain="cytometry",
66
+ family="gating",
67
+ summary="Apply an explicit cytometry gating, threshold, grouping, and QC policy.",
68
+ tags=("gating", "threshold", "qc"),
69
+ ),
70
+ plugin_cls=CytometryGatingTransform,
71
+ ),
72
+ build_plugin_asset(
73
+ plugin_id="transform/logic_symmetry",
74
+ semantics=PluginSemantics(
75
+ domain="logic",
76
+ family="summary_transform",
77
+ summary="Compute logic-symmetry metrics over an explicit four-state mapping.",
78
+ tags=("logic", "geometry", "summary"),
79
+ ),
80
+ plugin_cls=LogicSymmetryTransform,
81
+ ),
82
+ build_plugin_asset(
83
+ plugin_id="transform/four_state_vector",
84
+ semantics=PluginSemantics(
85
+ domain="logic",
86
+ family="summary_transform",
87
+ summary="Compute four-state vector logic summaries from annotated plate-reader traces.",
88
+ tags=("logic", "summary", "four_state_vector"),
89
+ ),
90
+ plugin_cls=FourStateVectorTransform,
91
+ ),
92
+ build_plugin_asset(
93
+ plugin_id="transform/four_state_vector_collection",
94
+ semantics=PluginSemantics(
95
+ domain="logic",
96
+ family="record_collection",
97
+ summary="Collect exact four-state vector record revisions from multiple Reader experiments.",
98
+ tags=("logic", "collection", "provenance"),
99
+ ),
100
+ plugin_cls=FourStateVectorCollectionTransform,
101
+ ),
102
+ build_plugin_asset(
103
+ plugin_id="transform/ratio",
104
+ semantics=PluginSemantics(
105
+ domain="plate_reader",
106
+ family="derived_channel",
107
+ summary="Create derived ratio channels from aligned tidy measurements.",
108
+ tags=("ratios", "derived_signal"),
109
+ ),
110
+ plugin_cls=RatioTransform,
111
+ ),
112
+ build_plugin_asset(
113
+ plugin_id="transform/four_state_event_window",
114
+ semantics=PluginSemantics(
115
+ domain="plate_reader",
116
+ family="event_relative_summary",
117
+ summary="Materialize event-relative summaries from provenance-bound source records.",
118
+ tags=("event", "window", "aggregate", "provenance"),
119
+ ),
120
+ plugin_cls=FourStateEventWindowTransform,
121
+ ),
122
+ build_plugin_asset(
123
+ plugin_id="transform/sample_map",
124
+ semantics=PluginSemantics(
125
+ domain="plate_reader",
126
+ family="metadata_merge",
127
+ summary="Attach well-position sample maps to tidy plate-reader traces.",
128
+ tags=("well_map", "annotation"),
129
+ ),
130
+ plugin_cls=SampleMapMerge,
131
+ ),
132
+ build_plugin_asset(
133
+ plugin_id="transform/sample_metadata",
134
+ semantics=PluginSemantics(
135
+ domain="generic",
136
+ family="metadata_merge",
137
+ summary="Attach sample-keyed metadata tables to tidy measurement rows.",
138
+ tags=("annotation", "table_join"),
139
+ ),
140
+ plugin_cls=SampleMetadataMerge,
141
+ ),
142
+ build_plugin_asset(
143
+ plugin_id="transform/overflow_handling",
144
+ semantics=PluginSemantics(
145
+ domain="plate_reader",
146
+ family="quality_filter",
147
+ summary="Mask, drop, or cap overflowed plate-reader measurements.",
148
+ tags=("overflow", "qc"),
149
+ ),
150
+ plugin_cls=OverflowHandling,
151
+ ),
152
+ build_plugin_asset(
153
+ plugin_id="transform/blank_correction",
154
+ semantics=PluginSemantics(
155
+ domain="plate_reader",
156
+ family="signal_correction",
157
+ summary="Detect and optionally subtract blank control wells.",
158
+ tags=("blanks", "normalization"),
159
+ ),
160
+ plugin_cls=BlankCorrection,
161
+ ),
162
+ build_plugin_asset(
163
+ plugin_id="transform/fold_change",
164
+ semantics=PluginSemantics(
165
+ domain="plate_reader",
166
+ family="summary_transform",
167
+ summary="Summarize nearest-time fold-change tables from tidy signals.",
168
+ tags=("fold_change", "snapshot_summary"),
169
+ ),
170
+ plugin_cls=FoldChange,
171
+ ),
172
+ )
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ from reader_workbench.plugins.validator.to_tidy_plus_map import PromoteToTidyPlusMap
4
+ from reader_workbench.workbench.assets import AssetDescriptor, build_plugin_asset
5
+ from reader_workbench.workbench.ontology import PluginSemantics
6
+
7
+ BUILTIN_PLUGIN_DESCRIPTORS: tuple[AssetDescriptor, ...] = (
8
+ build_plugin_asset(
9
+ plugin_id="validator/to_tidy_plus_map",
10
+ semantics=PluginSemantics(
11
+ domain="plate_reader",
12
+ family="contract_promotion",
13
+ summary="Promote tidy tables to annotated plate-reader contracts when metadata is present.",
14
+ tags=("contract", "annotation"),
15
+ ),
16
+ plugin_cls=PromoteToTidyPlusMap,
17
+ ),
18
+ )
File without changes