Skip to content

Commit 5437d3d

Browse files
committed
Added calculated data
1 parent 788f9d5 commit 5437d3d

8 files changed

Lines changed: 1955 additions & 13 deletions

File tree

src/allotropy/allotrope/schema_mappers/adm/pcr/BENCHLING/_2023/_09/dpcr.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class Measurement:
9090
errors: list[Error] | None = None
9191

9292
# custom
93+
sample_custom_info: dict[str, Any] | None = None
9394
custom_info: dict[str, Any] | None = None
9495

9596

@@ -244,6 +245,10 @@ def _get_measurement_document(
244245
]
245246
),
246247
)
248+
249+
measurement_doc.sample_document = add_custom_information_document(
250+
measurement_doc.sample_document, measurement.sample_custom_info
251+
)
247252
return add_custom_information_document(measurement_doc, measurement.custom_info)
248253

249254
def _get_calculated_data_aggregate_document(

src/allotropy/parsers/qiacuity_dpcr/constants.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
from dataclasses import dataclass
2+
3+
4+
@dataclass(frozen=True)
5+
class CalculatedDataConfig:
6+
name: str
7+
key: str
8+
unit: str
9+
feature: str | None = None
10+
11+
12+
CONFIGS: list[CalculatedDataConfig] = [
13+
CalculatedDataConfig(name="CI (95%)", key="CI (95%)", unit="#/μL"),
14+
CalculatedDataConfig(name="SD", key="SD", unit="(unitless)"),
15+
CalculatedDataConfig(name="CV%", key="CV%", unit="%"),
16+
CalculatedDataConfig(
17+
name="Mean Concentration",
18+
key="Mean conc. [copies/μL]",
19+
unit="#/μL",
20+
feature="Mean Concentration",
21+
),
22+
]
23+
24+
CONFIGS_KEYS: set[str] = {config.key for config in CONFIGS}
25+
26+
127
BRAND_NAME = "Qiacuity Digital PCR System"
228
PRODUCT_MANUFACTURER = "Qiagen"
329
SOFTWARE_NAME = "Qiacuity Software Suite"
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Iterable
4+
5+
from allotropy.allotrope.schema_mappers.adm.pcr.BENCHLING._2023._09.dpcr import (
6+
CalculatedDataItem,
7+
DataSource,
8+
)
9+
from allotropy.parsers.qiacuity_dpcr.constants import CONFIGS
10+
from allotropy.parsers.utils.pandas import SeriesData
11+
from allotropy.parsers.utils.uuids import random_uuid_str
12+
13+
14+
def _iter_row_calculated_items(row: SeriesData) -> Iterable[CalculatedDataItem]:
15+
measurement_identifier = row.get(str, "_measurement_identifier")
16+
if not measurement_identifier:
17+
return []
18+
19+
items: list[CalculatedDataItem] = []
20+
21+
for conf in CONFIGS:
22+
value = row.get(float, conf.key)
23+
if value is None:
24+
continue
25+
items.append(
26+
CalculatedDataItem(
27+
identifier=random_uuid_str(),
28+
name=conf.name,
29+
value=float(value),
30+
unit=conf.unit,
31+
data_sources=[
32+
DataSource(
33+
identifier=measurement_identifier,
34+
feature=conf.feature or conf.name,
35+
)
36+
],
37+
)
38+
)
39+
40+
return items
41+
42+
43+
def create_calculated_data(rows: list[SeriesData]) -> list[CalculatedDataItem]:
44+
calculated: list[CalculatedDataItem] = []
45+
for row in rows:
46+
calculated.extend(list(_iter_row_calculated_items(row)))
47+
return calculated

src/allotropy/parsers/qiacuity_dpcr/qiacuity_dpcr_parser.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
MeasurementGroup,
88
)
99
from allotropy.named_file_contents import NamedFileContents
10+
from allotropy.parsers.qiacuity_dpcr.qiacuity_dpcr_calculated_data import (
11+
create_calculated_data as create_qiacuity_calculated_data,
12+
)
1013
from allotropy.parsers.qiacuity_dpcr.qiacuity_dpcr_reader import QiacuitydPCRReader
1114
from allotropy.parsers.qiacuity_dpcr.qiacuity_dpcr_structure import (
1215
create_measurements,
1316
create_metadata,
1417
)
1518
from allotropy.parsers.release_state import ReleaseState
16-
from allotropy.parsers.utils.pandas import map_rows
19+
from allotropy.parsers.utils.pandas import SeriesData
20+
from allotropy.parsers.utils.uuids import random_uuid_str
1721
from allotropy.parsers.vendor_parser import VendorParser
1822

1923

@@ -25,14 +29,25 @@ class QiacuitydPCRParser(VendorParser[Data, Model]):
2529

2630
def create_data(self, named_file_contents: NamedFileContents) -> Data:
2731
reader = QiacuitydPCRReader(named_file_contents)
32+
# Assign stable measurement identifiers per row for data source linkage
33+
reader.well_data["_measurement_identifier"] = [
34+
random_uuid_str() for _ in range(len(reader.well_data))
35+
]
36+
# Build SeriesData list once and reuse
37+
series_rows: list[SeriesData] = [
38+
SeriesData(row) for _, row in reader.well_data.iterrows()
39+
]
40+
calculated_data = create_qiacuity_calculated_data(series_rows)
41+
measurement_groups = [
42+
MeasurementGroup(
43+
measurements=[create_measurements(row) for row in series_rows],
44+
# TODO: Hardcoded plate well count to 0 since it's a required field
45+
# ASM will be modified to optional in future version
46+
plate_well_count=0,
47+
)
48+
]
2849
return Data(
2950
create_metadata(named_file_contents.original_file_path),
30-
measurement_groups=[
31-
MeasurementGroup(
32-
measurements=map_rows(reader.well_data, create_measurements),
33-
# TODO: Hardcoded plate well count to 0 since it's a required field
34-
# ASM will be modified to optional in future version
35-
plate_well_count=0,
36-
)
37-
],
51+
measurement_groups=measurement_groups,
52+
calculated_data=calculated_data,
3853
)

src/allotropy/parsers/qiacuity_dpcr/qiacuity_dpcr_structure.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,15 @@ def create_measurements(data: SeriesData) -> Measurement:
2929
"sample type", sample_role_type, SAMPLE_ROLE_TYPE_MAPPING
3030
)
3131

32+
identifier = data.get(str, "_measurement_identifier") or random_uuid_str()
33+
34+
sample_custom_info = data.get_custom_keys({"IC", "Control type"})
35+
for key in sample_custom_info:
36+
if sample_custom_info[key] in ("", "-", "-", "--"):
37+
sample_custom_info[key] = None
38+
3239
return Measurement(
33-
identifier=random_uuid_str(),
40+
identifier=identifier,
3441
measurement_time=DEFAULT_EPOCH_TIMESTAMP,
3542
sample_identifier=data[str, "Sample/NTC/Control"],
3643
sample_role_type=sample_role_type,
@@ -42,6 +49,8 @@ def create_measurements(data: SeriesData) -> Measurement:
4249
positive_partition_count=data[int, "Partitions (positive)"],
4350
negative_partition_count=data.get(int, "Partitions (negative)"),
4451
fluorescence_intensity_threshold_setting=data.get(float, "Threshold"),
52+
sample_custom_info=sample_custom_info,
53+
custom_info=data.get_unread(),
4554
)
4655

4756

tests/parsers/qiacuity_dpcr/testdata/qiacuity_dpcr_example01.json

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
}
6060
}
6161
]
62+
},
63+
"custom information document": {
64+
"Reaction Mix": "5p"
6265
}
6366
},
6467
{
@@ -106,6 +109,9 @@
106109
}
107110
}
108111
]
112+
},
113+
"custom information document": {
114+
"Reaction Mix": "5p"
109115
}
110116
},
111117
{
@@ -153,6 +159,9 @@
153159
}
154160
}
155161
]
162+
},
163+
"custom information document": {
164+
"Reaction Mix": "5p"
156165
}
157166
},
158167
{
@@ -200,6 +209,9 @@
200209
}
201210
}
202211
]
212+
},
213+
"custom information document": {
214+
"Reaction Mix": "5p"
203215
}
204216
},
205217
{
@@ -247,6 +259,9 @@
247259
}
248260
}
249261
]
262+
},
263+
"custom information document": {
264+
"Reaction Mix": "5p"
250265
}
251266
},
252267
{
@@ -294,6 +309,9 @@
294309
}
295310
}
296311
]
312+
},
313+
"custom information document": {
314+
"Reaction Mix": "5p"
297315
}
298316
}
299317
]
@@ -305,7 +323,107 @@
305323
"UNC path": "tests/parsers/qiacuity_dpcr/testdata/qiacuity_dpcr_example01.csv",
306324
"software name": "Qiacuity Software Suite",
307325
"ASM converter name": "allotropy_qiacuity_dpcr",
308-
"ASM converter version": "0.1.92"
326+
"ASM converter version": "0.1.107"
327+
},
328+
"calculated data aggregate document": {
329+
"calculated data document": [
330+
{
331+
"calculated data identifier": "QIACUITY_DPCR_TEST_ID_6",
332+
"data source aggregate document": {
333+
"data source document": [
334+
{
335+
"data source identifier": "QIACUITY_DPCR_TEST_ID_0",
336+
"data source feature": "CI (95%)"
337+
}
338+
]
339+
},
340+
"calculated data name": "CI (95%)",
341+
"calculated datum": {
342+
"value": 4.6,
343+
"unit": "#/μL"
344+
}
345+
},
346+
{
347+
"calculated data identifier": "QIACUITY_DPCR_TEST_ID_7",
348+
"data source aggregate document": {
349+
"data source document": [
350+
{
351+
"data source identifier": "QIACUITY_DPCR_TEST_ID_1",
352+
"data source feature": "CI (95%)"
353+
}
354+
]
355+
},
356+
"calculated data name": "CI (95%)",
357+
"calculated datum": {
358+
"value": 15.2,
359+
"unit": "#/μL"
360+
}
361+
},
362+
{
363+
"calculated data identifier": "QIACUITY_DPCR_TEST_ID_8",
364+
"data source aggregate document": {
365+
"data source document": [
366+
{
367+
"data source identifier": "QIACUITY_DPCR_TEST_ID_2",
368+
"data source feature": "CI (95%)"
369+
}
370+
]
371+
},
372+
"calculated data name": "CI (95%)",
373+
"calculated datum": {
374+
"value": 10.3,
375+
"unit": "#/μL"
376+
}
377+
},
378+
{
379+
"calculated data identifier": "QIACUITY_DPCR_TEST_ID_9",
380+
"data source aggregate document": {
381+
"data source document": [
382+
{
383+
"data source identifier": "QIACUITY_DPCR_TEST_ID_3",
384+
"data source feature": "CI (95%)"
385+
}
386+
]
387+
},
388+
"calculated data name": "CI (95%)",
389+
"calculated datum": {
390+
"value": 4.2,
391+
"unit": "#/μL"
392+
}
393+
},
394+
{
395+
"calculated data identifier": "QIACUITY_DPCR_TEST_ID_10",
396+
"data source aggregate document": {
397+
"data source document": [
398+
{
399+
"data source identifier": "QIACUITY_DPCR_TEST_ID_4",
400+
"data source feature": "CI (95%)"
401+
}
402+
]
403+
},
404+
"calculated data name": "CI (95%)",
405+
"calculated datum": {
406+
"value": 3.3,
407+
"unit": "#/μL"
408+
}
409+
},
410+
{
411+
"calculated data identifier": "QIACUITY_DPCR_TEST_ID_11",
412+
"data source aggregate document": {
413+
"data source document": [
414+
{
415+
"data source identifier": "QIACUITY_DPCR_TEST_ID_5",
416+
"data source feature": "CI (95%)"
417+
}
418+
]
419+
},
420+
"calculated data name": "CI (95%)",
421+
"calculated datum": {
422+
"value": 6.6,
423+
"unit": "#/μL"
424+
}
425+
}
426+
]
309427
}
310428
}
311429
}

0 commit comments

Comments
 (0)