Skip to content

Commit fe27364

Browse files
authored
feat: NovaBio Flex2 - use SeriesData.get_unread (#1070)
<img width="773" height="320" alt="image" src="https://github.com/user-attachments/assets/1e21fb64-776d-4ca5-9af6-99e0fbe5e390" />
1 parent f89010f commit fe27364

6 files changed

Lines changed: 467 additions & 65 deletions

File tree

src/allotropy/allotrope/schema_mappers/adm/solution_analyzer/benchling/_2024/_09/solution_analyzer.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ class Measurement:
125125
# Errors
126126
errors: list[Error] | None = None
127127

128+
# Custom information
129+
custom_info: dict[str, Any] | None = None
130+
device_control_custom_info: dict[str, Any] | None = None
131+
128132

129133
@dataclass(frozen=True)
130134
class MeasurementGroup:
@@ -302,7 +306,7 @@ def _get_measurement_document_item(
302306
metadata.sample_volume_setting,
303307
),
304308
),
305-
None,
309+
measurement.device_control_custom_info,
306310
),
307311
]
308312
),
@@ -355,7 +359,7 @@ def _get_sample_document(self, measurement: Measurement) -> SampleDocument:
355359
batch_identifier=measurement.batch_identifier,
356360
description=measurement.description,
357361
),
358-
None,
362+
measurement.custom_info,
359363
)
360364

361365
def _create_analyte_document(self, analyte: Analyte) -> AnalyteDocument:

src/allotropy/parsers/novabio_flex2/novabio_flex2_structure.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from allotropy.parsers.utils.pandas import SeriesData
3131
from allotropy.parsers.utils.uuids import random_uuid_str
3232
from allotropy.parsers.utils.values import try_float_or_none
33+
from allotropy.parsers.utils.warnings_tools import suppress_unused_keys_warning
3334

3435

3536
@dataclass(frozen=True)
@@ -78,13 +79,26 @@ class Sample:
7879
viable_cell_count: float | None = None
7980
cell_type_processing_method: str | None = None
8081
cell_density_dilution_factor: float | None = None
82+
custom_info: dict[str, Any] | None = None
83+
device_control_custom_info: dict[str, Any] | None = None
8184

8285
@classmethod
8386
def create(cls, units: SeriesData, data: SeriesData) -> Sample:
8487
cell_density_dilution = data.get(str, "Cell Density Dilution", "")
8588
if cell_density_dilution:
8689
cell_density_dilution = cell_density_dilution.split(":")[0]
8790

91+
unused_keys = {
92+
"Sample Time",
93+
"PCO2 @ Temp",
94+
"PO2 @ Temp",
95+
"pH @ Temp",
96+
"Operator",
97+
"Osm",
98+
}
99+
data.mark_read(unused_keys)
100+
units.mark_read(unused_keys)
101+
88102
return Sample(
89103
identifier=data[str, "Sample ID"],
90104
sample_type=data[str, "Sample Type"],
@@ -122,6 +136,17 @@ def create(cls, units: SeriesData, data: SeriesData) -> Sample:
122136
if cell_density_dilution
123137
else None,
124138
cell_density_dilution_factor=try_float_or_none(str(cell_density_dilution)),
139+
device_control_custom_info=data.get_custom_keys(
140+
{
141+
"Sparging O2%",
142+
"pH / Gas Flow Time",
143+
"Vessel Pressure (psi)",
144+
"Chemistry Flow Time",
145+
"Valid Images",
146+
"Cell Density Flow",
147+
}
148+
),
149+
custom_info={**data.get_unread(), **units.get_unread()},
125150
)
126151

127152

@@ -165,6 +190,7 @@ def parse_units(units: SeriesData) -> SeriesData:
165190
return SeriesData(pd.Series(data))
166191

167192
@staticmethod
193+
@suppress_unused_keys_warning
168194
def parse_data(
169195
raw_data: pd.DataFrame,
170196
) -> tuple[SeriesData, list[SeriesData]]:
@@ -198,6 +224,8 @@ def _create_measurement(sample: Sample, **kwargs: Any) -> Measurement:
198224
sample_identifier=sample.identifier,
199225
description=sample.sample_type,
200226
batch_identifier=sample.batch_identifier,
227+
custom_info=sample.custom_info,
228+
device_control_custom_info=sample.device_control_custom_info,
201229
**kwargs,
202230
)
203231

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Callable
4+
import functools
5+
import gc
6+
import os
7+
from typing import Any
8+
9+
10+
def suppress_unused_keys_warning(func: Callable[..., Any]) -> Callable[..., Any]:
11+
"""
12+
Use when a class that warns for unread keys is created for a special one-off use that does not intend to read all keys, e.g.
13+
to read a single value and return.
14+
Not appropriate to use in the main use of the class where unread keys should be copied into custom data fields.
15+
"""
16+
17+
@functools.wraps(func)
18+
def _wrapper(*args: Any, **kwargs: Any) -> Any:
19+
previous_warn_unused_keys = os.environ.pop("WARN_UNUSED_KEYS", None)
20+
try:
21+
return func(*args, **kwargs)
22+
finally:
23+
gc.collect()
24+
if previous_warn_unused_keys is not None:
25+
os.environ["WARN_UNUSED_KEYS"] = previous_warn_unused_keys
26+
27+
return _wrapper

tests/parsers/novabio_flex2/testdata/SampleResults2022-06-28_142558.json

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@
1010
"device control document": [
1111
{
1212
"device type": "solution-analyzer",
13-
"detection type": "metabolite-detection"
13+
"detection type": "metabolite-detection",
14+
"custom information document": {
15+
"Vessel Pressure (psi)": 0.0,
16+
"Sparging O2%": 20.9,
17+
"Chemistry Flow Time": 6.67,
18+
"pH / Gas Flow Time": 3.65
19+
}
1420
}
1521
]
1622
},
@@ -19,7 +25,12 @@
1925
"sample document": {
2026
"sample identifier": "SMPL_1234",
2127
"batch identifier": "BATCH_123",
22-
"description": "Spent Media"
28+
"description": "Spent Media",
29+
"custom information document": {
30+
"Vessel ID": "3L Bioreactor",
31+
"Chemistry Dilution Ratio": "1:1",
32+
"Cell Type": "Workhog"
33+
}
2334
},
2435
"analyte aggregate document": {
2536
"analyte document": [
@@ -94,7 +105,13 @@
94105
"device control document": [
95106
{
96107
"device type": "solution-analyzer",
97-
"detection type": "blood-gas-detection"
108+
"detection type": "blood-gas-detection",
109+
"custom information document": {
110+
"Vessel Pressure (psi)": 0.0,
111+
"Sparging O2%": 20.9,
112+
"Chemistry Flow Time": 6.67,
113+
"pH / Gas Flow Time": 3.65
114+
}
98115
}
99116
]
100117
},
@@ -103,7 +120,12 @@
103120
"sample document": {
104121
"sample identifier": "SMPL_1234",
105122
"batch identifier": "BATCH_123",
106-
"description": "Spent Media"
123+
"description": "Spent Media",
124+
"custom information document": {
125+
"Vessel ID": "3L Bioreactor",
126+
"Chemistry Dilution Ratio": "1:1",
127+
"Cell Type": "Workhog"
128+
}
107129
},
108130
"pO2": {
109131
"value": 191.6,
@@ -127,7 +149,13 @@
127149
"device control document": [
128150
{
129151
"device type": "solution-analyzer",
130-
"detection type": "ph-detection"
152+
"detection type": "ph-detection",
153+
"custom information document": {
154+
"Vessel Pressure (psi)": 0.0,
155+
"Sparging O2%": 20.9,
156+
"Chemistry Flow Time": 6.67,
157+
"pH / Gas Flow Time": 3.65
158+
}
131159
}
132160
]
133161
},
@@ -136,7 +164,12 @@
136164
"sample document": {
137165
"sample identifier": "SMPL_1234",
138166
"batch identifier": "BATCH_123",
139-
"description": "Spent Media"
167+
"description": "Spent Media",
168+
"custom information document": {
169+
"Vessel ID": "3L Bioreactor",
170+
"Chemistry Dilution Ratio": "1:1",
171+
"Cell Type": "Workhog"
172+
}
140173
},
141174
"pH": {
142175
"value": 7.4,
@@ -159,7 +192,7 @@
159192
"file name": "SampleResults2022-06-28_142558.csv",
160193
"UNC path": "tests/parsers/novabio_flex2/testdata/SampleResults2022-06-28_142558.csv",
161194
"ASM converter name": "allotropy_novabio_flex2",
162-
"ASM converter version": "0.1.79",
195+
"ASM converter version": "0.1.105",
163196
"software name": "NovaBio Flex"
164197
},
165198
"device system document": {

0 commit comments

Comments
 (0)