Skip to content

Commit 77f55e4

Browse files
authored
feat: Roche Cedex BioHT - Support for roche v5 files and new fields in output (#1047)
This is part of: - [Roche Cedex BioHT - Add custom information field Detection Kit and Detection Kit Range for Roche detection kits of analytes ](https://jira.benchling.team/browse/BNCH-129548) - [Roche Cedex BioHT - Add support of v5.0 file](https://jira.benchling.team/browse/BNCH-129104) It adds new fields, constants and modifies the parser to allow V5 files and detecting if the file passed is not of one of the versions allowed
1 parent 6250eb3 commit 77f55e4

18 files changed

Lines changed: 7278 additions & 41 deletions

src/allotropy/allotrope/schema_mappers/adm/cell_counting/benchling/_2023/_11/cell_counting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class Measurement:
8686

8787
errors: list[Error] | None = None
8888

89-
# customer information document fields
89+
# Custom information document fields
9090
debris_index: float | None = None
9191
cell_aggregation_percentage: JsonFloat | None = None
9292

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class Analyte:
5757
value: float
5858
unit: str
5959

60+
# Custom information document fields
61+
custom_info: dict[str, Any] | None = None
62+
6063
def __lt__(self, other: Any) -> bool:
6164
if not isinstance(other, Analyte):
6265
return False
@@ -327,27 +330,27 @@ def _get_sample_document(self, measurement: Measurement) -> SampleDocument:
327330

328331
def _create_analyte_document(self, analyte: Analyte) -> AnalyteDocument:
329332
if analyte.unit == "g/L":
330-
return AnalyteDocument(
333+
output = AnalyteDocument(
331334
analyte_name=analyte.name,
332335
mass_concentration=TQuantityValueGramPerLiter(value=analyte.value),
333336
)
334337
elif analyte.unit == "mL/L":
335-
return AnalyteDocument(
338+
output = AnalyteDocument(
336339
analyte_name=analyte.name,
337340
volume_concentration=TQuantityValueMilliliterPerLiter(
338341
value=analyte.value
339342
),
340343
)
341344
elif analyte.unit == "mmol/L":
342-
return AnalyteDocument(
345+
output = AnalyteDocument(
343346
analyte_name=analyte.name,
344347
molar_concentration=TQuantityValueMillimolePerLiter(
345348
value=analyte.value
346349
),
347350
)
348351
elif analyte.unit == "U/L":
349352
if analyte.name == "ldh":
350-
return AnalyteDocument(
353+
output = AnalyteDocument(
351354
analyte_name=analyte.name,
352355
molar_concentration=TQuantityValueMillimolePerLiter(
353356
value=analyte.value * 0.0167
@@ -358,9 +361,13 @@ def _create_analyte_document(self, analyte: Analyte) -> AnalyteDocument:
358361
else:
359362
msg = f"Invalid unit for {analyte.name}: {analyte.unit}"
360363
raise AllotropeConversionError(msg)
364+
else:
365+
msg = f"Invalid unit for analyte: {analyte.unit}, possible values are: g/L, mL/L, mmol/L"
366+
raise AllotropeConversionError(msg)
367+
368+
output = add_custom_information_document(output, analyte.custom_info or {})
361369

362-
msg = f"Invalid unit for analyte: {analyte.unit}, possible values are: g/L, mL/L, mmol/L"
363-
raise AllotropeConversionError(msg)
370+
return output
364371

365372
def _create_processed_data_document(
366373
self, measurement: Measurement

src/allotropy/parsers/roche_cedex_bioht/constants.py

Lines changed: 173 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"analyst",
4242
]
4343

44-
DATA_HEADER = [
44+
DATA_HEADER_V6 = [
4545
"row type",
4646
"measurement time",
4747
"sample identifier",
@@ -57,6 +57,21 @@
5757
"col13",
5858
]
5959

60+
DATA_HEADER_V5 = [
61+
"row type",
62+
"measurement time",
63+
"analyte name",
64+
"col4",
65+
"col5",
66+
"sample identifier",
67+
"sample role type",
68+
"concentration unit",
69+
"flag",
70+
"concentration value",
71+
"col10",
72+
"col11",
73+
]
74+
6075

6176
ANALYTES_LOOKUP = {
6277
"AC2B": "acetate",
@@ -140,6 +155,163 @@
140155
"OSM2B": "osmolality",
141156
}
142157

158+
DETECTION_KIT_LOOKUP = {
159+
"AC2B": "Standard",
160+
"AC2D": "High with Pre - Dilution",
161+
"AQB": "Standard",
162+
"AQD": "High with Pre - Dilution",
163+
"AQQB": "Standard",
164+
"NH3LB": "Low",
165+
"NH3B": "Standard",
166+
"NH3D": "High with Pre - Dilution",
167+
"ARAB": "Standard",
168+
"ARAD": "High with Pre - Dilution",
169+
"ASNLB": "Low",
170+
"ASNHB": "Standard",
171+
"ASNHD": "High with Pre - Dilution",
172+
"ASPB": "Standard",
173+
"ASPD": "High with Pre - Dilution",
174+
"CA2B": "Standard",
175+
"CA2D": "High with Pre - Dilution",
176+
"CHO2B": "Standard",
177+
"CHO2D": "High with Pre - Dilution",
178+
"ETOHB": "Standard",
179+
"ETOHD": "High with Pre - Dilution",
180+
"FORB": "Standard",
181+
"FORD": "High with Pre - Dilution",
182+
"GAL2B": "Standard",
183+
"GAL2D": "High with Pre - Dilution",
184+
"GLC2L": "Low",
185+
"GLC2B": "Standard",
186+
"GLC2D": "High with Pre - Dilution",
187+
"GLC3D": "High with Pre - Dilution",
188+
"GLC3B": "Standard",
189+
"GLC3L": "Low",
190+
"GLU2B": "Standard",
191+
"GLU2D": "High with Pre - Dilution",
192+
"GLN2B": "Standard",
193+
"GLN2D": "High with Pre - Dilution",
194+
"GLYB": "Low",
195+
"GLYD": "Standard",
196+
"GLYE": "High with Pre - Dilution",
197+
"FABLA": "Low",
198+
"FABLB": "Standard",
199+
"FABHB": "High",
200+
"FABHD": "High with Pre - Dilution",
201+
"IGGLB": "Low",
202+
"IGGHB": "Standard",
203+
"IGGHD": "High with Pre - Dilution",
204+
"MIGLB": "Low",
205+
"MIGHB": "Standard",
206+
"MIGHD": "High with Pre - Dilution",
207+
"FE2B": "Standard",
208+
"FE2D": "High with Pre - Dilution",
209+
"LAC2B": "Standard",
210+
"LAC2D": "High with Pre - Dilution",
211+
"LDH2B": "Standard",
212+
"LDH2D": "High with Pre - Dilution",
213+
"MG2L": "Low",
214+
"MG2B": "Standard",
215+
"MG2D": "High with Pre - Dilution",
216+
"NO3L": "Low",
217+
"NO3B": "Standard",
218+
"NO3D": "High with Pre - Dilution",
219+
"PHO2L": "Low",
220+
"PHO2B": "Standard",
221+
"PHO2D": "High with Pre - Dilution",
222+
"KB": "Standard",
223+
"KD": "High with Pre - Dilution",
224+
"PYRB": "Standard",
225+
"PYRD": "High with Pre - Dilution",
226+
"NAB": "Standard",
227+
"NAD": "High with Pre - Dilution",
228+
"SUCB": "Standard",
229+
"SUCD": "High with Pre - Dilution",
230+
"SUGLB": "Standard",
231+
"SUGLD": "High with Pre - Dilution",
232+
"TP2B": "Standard",
233+
"TP2D": "High with Pre - Dilution",
234+
"TP2LB": "Low",
235+
}
236+
237+
DETECTION_KIT_RANGE_LOOKUP = {
238+
"AC2B": "0.25 - 38.9 mmol/L",
239+
"AC2D": "14 - 389 mmol/L",
240+
"AQB": "0.1 - 10 mmol/L",
241+
"AQD": "1 - 100 mmol/L",
242+
"NH3LB": "0.0278 - 1.389 mmol/L",
243+
"NH3B": " 0.278 - 13.89 mmol/L",
244+
"NH3D": "5.56 - 277.8 mmol/L",
245+
"ARAB": "0.055 - 27.75 mmol/L",
246+
"ARAD": "0.55 - 277.5 mmol/L",
247+
"ASNLB": "0.1 - 1 mmol/L",
248+
"ASNHB": "0.5 - 9 mmol/L",
249+
"ASNHD": "2.5 - 45 mmol/L",
250+
"ASPB": "0.1 - 9 mmol/L",
251+
"ASPD": "1 - 45 mmol/L",
252+
"CA2B": "0.2 - 5 mmol/L",
253+
"CA2D": "2 - 50 mmol/L",
254+
"CHO2B": "0.025 - 2.07 mmol/L",
255+
"CHO2D": "0.25 - 20.7 mmol/L",
256+
"ETOHB": "11 - 220 mmol/L",
257+
"ETOHD": "220 - 4400 mmol/L",
258+
"FORB": "0.2 - 20 mmol/L",
259+
"FORD": "2 - 200 mmol/L",
260+
"GAL2B": "0.055 - 27.75 mmol/L",
261+
"GAL2D": "0.55 - 277.5 mmol/L",
262+
"GLC2L": "0.011 - 4.163 mmol/L",
263+
"GLC2B": "0.111 - 41.63 mmol/L",
264+
"GLC2D": "1.11 - 416.3 mmol/L",
265+
"GLC3D": "1.11 - 416.3 mmol/L",
266+
"GLC3B": "0.111 - 41.63 mmol/L",
267+
"GLC3L": "0.011 - 4.163 mmol/L",
268+
"GLU2B": "0.099 - 10.2 mmol/L",
269+
"GLU2D": "0.99 - 102 mmol/L",
270+
"GLN2B": "0.1 - 10.26 mmol/L",
271+
"GLN2D": "0.5 - 51.3 mmol/L",
272+
"GLYB": "0.1 - 10 mmol/L",
273+
"GLYD": "1 - 100 mmol/L",
274+
"GLYE": "10 - 1000 mmol/L ",
275+
"FABLA": "0.13 - 0.35 g/L",
276+
"FABLB": "0.01 - 0.4 g/L",
277+
"FABHB": "0.3 - 8 g/L",
278+
"FABHD": "6 - 40 g/L",
279+
"IGGLB": "0.000067 - 0.000533 mmol/L",
280+
"IGGHB": "0.000533 - 0.01067 mmol/L",
281+
"IGGHD": "0.002665 - 0.05335 mmol/L ",
282+
"MIGLB": "0.01 - 0.2 g/L",
283+
"MIGHB": "0.2 - 1.5 g/L",
284+
"MIGHD": "1 - 7.5 g/L",
285+
"FE2B": "0.0009 - 0.179 mmol/L",
286+
"FE2D": "0.009 - 1.79 mmol/L ",
287+
"LAC2B": "0.0444 - 15.55 mmol/L",
288+
"LAC2D": "0.444 - 155.5 mmol/L ",
289+
"LDH2B": "20 - 1000 U/L",
290+
"LDH2D": "200 - 10000 U/L",
291+
"MG2L": "0.01 - 0.25 mmol/L",
292+
"MG2B": "0.1 - 2.5 mmol/L",
293+
"MG2D": "2.3 - 25 mmol/L",
294+
"NO3L": "0.2 - 4 mmol/L",
295+
"NO3B": "1 - 25 mmol/L",
296+
"NO3D": "15 - 250 mmol/L",
297+
"PHO2L": "0.01 - 0.837 mmol/L",
298+
"PHO2B": "0.1 - 8.37 mmol/L",
299+
"PHO2D": "1.1 - 92.07 mmol/L",
300+
"KB": "2 - 28 mmol/L",
301+
"KD": "24 - 280 mmol/L",
302+
"PYRB": "0.2 - 8 mmol/L",
303+
"PYRD": "2 - 80 mmol/L",
304+
"NAB": "50 - 275 mmol/L",
305+
"NAD": "250 - 1375 mmol/L",
306+
"SUCB": "0.292 - 40.9 mmol/L",
307+
"SUCD": "2.92 - 409 mmol/L",
308+
"SUGLB": "0.292 - 40.9 mmol/L",
309+
"SUGLD": "2.92 - 409 mmol/L",
310+
"TP2B": "4 - 120 g/L",
311+
"TP2D": "40 - 1200 g/L",
312+
"TP2LB": "0.3 - 6.0 g/L",
313+
}
314+
143315
SAMPLE_ROLE_TYPES = {"SAM": "Sample"}
144316

145317
SOLUTION_ANALYZER = "solution-analyzer"

src/allotropy/parsers/roche_cedex_bioht/roche_cedex_bioht_reader.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77

88
from allotropy.parsers.roche_cedex_bioht.constants import (
99
ANALYTES_LOOKUP,
10-
DATA_HEADER,
10+
DATA_HEADER_V5,
11+
DATA_HEADER_V6,
12+
DETECTION_KIT_LOOKUP,
13+
DETECTION_KIT_RANGE_LOOKUP,
1114
INFO_HEADER,
1215
SAMPLE_ROLE_TYPES,
1316
)
@@ -41,20 +44,43 @@ def read_title_data(self, contents: IOType) -> SeriesData:
4144
)
4245

4346
def read_samples_data(self, contents: IOType) -> pd.DataFrame:
47+
software_version = self.title_data[str, "software version"]
48+
if software_version.startswith("5"):
49+
data_header = DATA_HEADER_V5
50+
elif software_version.startswith("6"):
51+
data_header = DATA_HEADER_V6
52+
else:
53+
msg = f"Unsupported software version: {software_version}"
54+
raise ValueError(msg)
55+
4456
contents.seek(0)
4557
sample_rows = read_csv(
4658
contents,
4759
delimiter="\t",
48-
usecols=DATA_HEADER,
49-
names=DATA_HEADER,
60+
usecols=data_header,
61+
names=data_header,
5062
skiprows=[0],
5163
)
5264

65+
sample_rows = sample_rows.drop_duplicates()
66+
67+
sample_rows["analyte code"] = sample_rows["analyte name"]
68+
sample_rows["detection kit"] = sample_rows["analyte name"]
69+
sample_rows["detection kit range"] = sample_rows["analyte name"]
70+
5371
sample_rows = sample_rows.replace(
54-
{"analyte name": ANALYTES_LOOKUP, "sample role type": SAMPLE_ROLE_TYPES}
72+
{
73+
"analyte name": ANALYTES_LOOKUP,
74+
"sample role type": SAMPLE_ROLE_TYPES,
75+
"detection kit": DETECTION_KIT_LOOKUP,
76+
"detection kit range": DETECTION_KIT_RANGE_LOOKUP,
77+
}
5578
)
5679

57-
sample_rows["batch identifier"] = sample_rows["batch identifier"].fillna("")
80+
if "batch identifier" in sample_rows.columns:
81+
sample_rows["batch identifier"] = sample_rows["batch identifier"].fillna("")
82+
else:
83+
sample_rows["batch identifier"] = [""] * len(sample_rows)
5884

5985
# concentration values under the test threshold will have a preceding < character
6086
# this will turn those values into NaN, which is the expected output

0 commit comments

Comments
 (0)