Skip to content

Commit 3a12c2f

Browse files
move params to constructor
1 parent 39ddfb7 commit 3a12c2f

2 files changed

Lines changed: 59 additions & 79 deletions

File tree

src/allotropy/parsers/luminex_xponent/luminex_xponent_structure.py

Lines changed: 38 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -68,65 +68,45 @@ class Header:
6868

6969
@classmethod
7070
def create(
71-
cls, header_data: pd.DataFrame, minimum_assay_bead_count_setting: float | None
71+
cls,
72+
header_data: pd.DataFrame,
73+
header_row: SeriesData,
74+
minimum_assay_bead_count_setting: float | None,
7275
) -> Header:
73-
info_row = SeriesData(header_data.iloc[0])
74-
raw_datetime = info_row[str, "BatchStartTime"]
75-
sample_volume = info_row.get(str, ["SampleVolume", "MaxSampleUptakeVolume"])
76-
77-
# Read all the keys first
78-
software_version = info_row.get(str, "Build")
79-
equipment_serial_number = info_row.get(str, "SN")
80-
analytical_method_identifier = info_row.get(str, "ProtocolName")
81-
method_version = info_row.get(str, "ProtocolVersion")
82-
experimental_data_identifier = info_row.get(str, "Batch")
83-
detector_gain_setting = info_row.get(
84-
str, ["ProtocolReporterGain", "ProtocolOperatingMode"]
85-
)
86-
data_system_instance_identifier = info_row[str, "ComputerName"]
87-
analyst = info_row.get(str, "Operator")
88-
89-
# Mark keys that are accessed directly in helper methods
90-
if "Program" in header_data:
91-
info_row.mark_read("Program")
92-
if "ProtocolPlate" in header_data:
93-
info_row.mark_read("ProtocolPlate")
94-
95-
custom_info = {
96-
"Country Code": info_row.get(str, "Country Code"),
97-
"ProtocolDevelopingCompany": info_row.get(str, "ProtocolDevelopingCompany"),
98-
"Version": info_row.get(str, "Version"),
99-
# These properties will be used by the measurement groups down below.
100-
"BatchStopTime": info_row.get(str, "BatchStopTime"),
101-
"ProtocolDescription": info_row.get(str, "ProtocolDescription"),
102-
}
76+
sample_volume = header_row.get(str, ["SampleVolume", "MaxSampleUptakeVolume"])
10377

104-
info_row.get_unread(
105-
skip={
106-
"Date",
107-
},
108-
)
78+
header_row.mark_read({"Program", "ProtocolPlate", "Date"})
10979

110-
# Build parameters dictionary
11180
return Header(
11281
model_number=cls._get_model_number(header_data),
113-
software_version=software_version,
114-
equipment_serial_number=equipment_serial_number,
115-
analytical_method_identifier=analytical_method_identifier,
116-
method_version=method_version,
117-
experimental_data_identifier=experimental_data_identifier,
82+
software_version=header_row.get(str, "Build"),
83+
equipment_serial_number=header_row.get(str, "SN"),
84+
analytical_method_identifier=header_row.get(str, "ProtocolName"),
85+
method_version=header_row.get(str, "ProtocolVersion"),
86+
experimental_data_identifier=header_row.get(str, "Batch"),
11887
sample_volume_setting=(
11988
try_float(sample_volume.split()[0], "sample volume setting")
12089
if sample_volume
12190
else None
12291
),
12392
plate_well_count=cls._get_plate_well_count(header_data),
124-
measurement_time=raw_datetime,
125-
detector_gain_setting=detector_gain_setting,
126-
data_system_instance_identifier=data_system_instance_identifier,
93+
measurement_time=header_row[str, "BatchStartTime"],
94+
detector_gain_setting=header_row.get(
95+
str, ["ProtocolReporterGain", "ProtocolOperatingMode"]
96+
),
97+
data_system_instance_identifier=header_row[str, "ComputerName"],
12798
minimum_assay_bead_count_setting=minimum_assay_bead_count_setting,
128-
analyst=analyst,
129-
custom_info=custom_info,
99+
analyst=header_row.get(str, "Operator"),
100+
custom_info={
101+
"Country Code": header_row.get(str, "Country Code"),
102+
"ProtocolDevelopingCompany": header_row.get(
103+
str, "ProtocolDevelopingCompany"
104+
),
105+
"Version": header_row.get(str, "Version"),
106+
# Used for the measurement groups
107+
"BatchStopTime": header_row.get(str, "BatchStopTime"),
108+
"ProtocolDescription": header_row.get(str, "ProtocolDescription"),
109+
},
130110
)
131111

132112
@classmethod
@@ -227,13 +207,12 @@ def create(
227207
results_data: dict[str, pd.DataFrame],
228208
count_data: SeriesData,
229209
bead_ids_data: SeriesData,
230-
header_data: pd.DataFrame,
210+
header_row: SeriesData,
231211
) -> Measurement:
232212
location = str(count_data.series.name)
233213
dilution_factor_data = results_data["Dilution Factor"]
234214
errors_data = results_data.get("Warnings/Errors")
235215
measurement_identifier = random_uuid_str()
236-
header_row = SeriesData(header_data.iloc[0])
237216

238217
if location not in dilution_factor_data.index:
239218
msg = f"Could not find 'Dilution Factor' data for: '{location}'."
@@ -334,10 +313,6 @@ def get_statistic_dimensions(analyte: str) -> list[StatisticDimension]:
334313
)
335314
)
336315

337-
# Read the remaining keys from count_data
338-
sample_identifier = count_data[str, "Sample"]
339-
assay_bead_count = count_data[float, "Total Events"]
340-
341316
device_control_custom_info = {
342317
"ProtocolHeater": header_row.get(str, "ProtocolHeater"),
343318
"DDGate": header_row.get(str, "DDGate"),
@@ -352,26 +327,18 @@ def get_statistic_dimensions(analyte: str) -> list[StatisticDimension]:
352327
"BeadType": header_row.get(str, "BeadType"),
353328
}
354329

355-
# Get unread keys after all keys have been read
356-
count_data.get_unread()
357-
header_row.get_unread(
358-
skip={
359-
"Date",
360-
},
361-
)
362-
363330
return Measurement(
364331
identifier=measurement_identifier,
365-
sample_identifier=sample_identifier,
332+
sample_identifier=count_data[str, "Sample"],
366333
location_identifier=location_id,
367334
dilution_factor_setting=dilution_factor_setting,
368-
assay_bead_count=assay_bead_count,
335+
assay_bead_count=count_data[float, "Total Events"],
369336
analytes=analytes,
370337
errors=errors,
371338
calculated_data=calculated_data,
372339
device_control_custom_info=device_control_custom_info,
373340
sample_custom_info=sample_custom_info,
374-
measurement_custom_info=count_data.get_unread(),
341+
measurement_custom_info=count_data.get_unread() | header_row.get_unread(),
375342
)
376343

377344
@classmethod
@@ -406,7 +373,7 @@ class MeasurementList:
406373

407374
@classmethod
408375
def create(
409-
cls, results_data: dict[str, pd.DataFrame], header_data: pd.DataFrame
376+
cls, results_data: dict[str, pd.DataFrame], header_row: SeriesData
410377
) -> MeasurementList:
411378
if missing_sections := [
412379
section for section in REQUIRED_SECTIONS if section not in results_data
@@ -433,12 +400,9 @@ def create_measurement(count_data: SeriesData) -> Measurement:
433400
results_data=results_data,
434401
count_data=count_data,
435402
bead_ids_data=bead_ids_data,
436-
header_data=header_data,
403+
header_row=header_row,
437404
)
438405

439-
# Capture unread bead IDs data to prevent warnings
440-
bead_ids_data.get_unread()
441-
442406
return MeasurementList(map_rows(results_data["Count"], create_measurement))
443407

444408

@@ -450,14 +414,14 @@ class Data:
450414

451415
@classmethod
452416
def create(cls, reader: LuminexXponentReader) -> Data:
417+
header_row = SeriesData(reader.header_data.iloc[0])
418+
453419
return Data(
454420
header=Header.create(
455-
reader.header_data, reader.minimum_assay_bead_count_setting
421+
reader.header_data, header_row, reader.minimum_assay_bead_count_setting
456422
),
457423
calibrations=map_rows(reader.calibration_data, create_calibration),
458-
measurement_list=MeasurementList.create(
459-
reader.results_data, reader.header_data
460-
),
424+
measurement_list=MeasurementList.create(reader.results_data, header_row),
461425
)
462426

463427

@@ -518,7 +482,7 @@ def create_measurement_groups(
518482
)
519483
for measurement in measurements
520484
]
521-
# Remove the custom info that was used to create the measurement groups.
485+
# Remove the header custom info that was used to create the measurement groups.
522486
header.custom_info.pop("BatchStopTime", None)
523487
header.custom_info.pop("ProtocolDescription", None)
524488

tests/parsers/luminex_xponent/luminex_xponent_structure_test.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ def test_create_header() -> None:
7575
},
7676
orient="index",
7777
).T
78-
header = Header.create(data, minimum_assay_bead_count_setting=10)
78+
header_row = SeriesData(data.iloc[0])
79+
header = Header.create(data, header_row, minimum_assay_bead_count_setting=10)
7980

8081
assert header == Header(
8182
model_number="Model", # Program, col 4
@@ -133,8 +134,10 @@ def test_create_heder_without_required_col(required_col: str) -> None:
133134
)
134135

135136
with pytest.raises(AllotropeConversionError, match=error_msg):
137+
data_without_col = data.drop(columns=[required_col])
138+
header_row = SeriesData(data_without_col.iloc[0])
136139
Header.create(
137-
data.drop(columns=[required_col]), minimum_assay_bead_count_setting=100
140+
data_without_col, header_row, minimum_assay_bead_count_setting=100
138141
)
139142

140143

@@ -191,7 +194,8 @@ def test_create_measurement_list() -> None:
191194
"allotropy.parsers.luminex_xponent.luminex_xponent_structure.random_uuid_str",
192195
return_value="dummy_id",
193196
):
194-
measurement_list = MeasurementList.create(results_data, mock_header_data)
197+
header_row = SeriesData(mock_header_data.iloc[0])
198+
measurement_list = MeasurementList.create(results_data, header_row)
195199

196200
assert measurement_list == MeasurementList(
197201
measurements=[
@@ -244,7 +248,18 @@ def test_create_measurement_list() -> None:
244248
Error(error="Another Warning."),
245249
],
246250
calculated_data=[],
247-
measurement_custom_info={},
251+
measurement_custom_info={
252+
"ProtocolName": "Order66",
253+
"ProtocolVersion": 5.0,
254+
"ProtocolReporterGain": "Pro MAP",
255+
"SampleVolume": "1 uL",
256+
"Build": "1.1.0",
257+
"Program": "xPonent",
258+
"SN": "SN1234",
259+
"ComputerName": "AAA000",
260+
"Batch": "ABC_0000",
261+
"BatchStartTime": "1/17/2024 7:41:29 AM",
262+
},
248263
sample_custom_info={
249264
"BatchDescription": "Test Batch Description",
250265
"PanelName": None,
@@ -301,4 +316,5 @@ def test_create_measurement_list_without_required_table_then_raise(
301316
f"Unable to parse input file, missing expected sections: ['{table_name}']."
302317
),
303318
):
304-
MeasurementList.create(results_data, mock_header_data)
319+
header_row = SeriesData(mock_header_data.iloc[0])
320+
MeasurementList.create(results_data, header_row)

0 commit comments

Comments
 (0)