Skip to content

Commit 2f75e0a

Browse files
fix minor comments, add get_unread call for rpoint data
1 parent ecc5354 commit 2f75e0a

1 file changed

Lines changed: 83 additions & 67 deletions

File tree

src/allotropy/parsers/cytiva_biacore_t200_evaluation/cytiva_biacore_t200_evaluation_data_creator.py

Lines changed: 83 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
Data,
4242
SystemInformation,
4343
)
44+
from allotropy.parsers.utils.pandas import map_rows, SeriesData
4445
from allotropy.parsers.utils.uuids import random_uuid_str
4546
from allotropy.parsers.utils.values import quantity_or_none, try_float_or_none
4647
from allotropy.types import DictType
@@ -159,6 +160,47 @@ def _extract_chi2_value(kinetic_result: Any) -> float | None:
159160
)
160161

161162

163+
def _create_report_point(
164+
series_data: SeriesData,
165+
flow_cell_id: str,
166+
cycle_number: int,
167+
display_flow_cell_id: str | None = None,
168+
) -> ReportPoint | None:
169+
"""Create a single ReportPoint object from SeriesData."""
170+
try:
171+
time_setting = series_data.get(float, ["column1", "Time"], default=0.0)
172+
relative_resonance = series_data.get(
173+
float, ["column3", "Relative"], default=0.0
174+
)
175+
identifier_role = series_data.get(str, ["column4", "Role"], default="baseline")
176+
absolute_resonance = series_data.get(
177+
float, ["column5", "Absolute"], default=0.0
178+
)
179+
180+
unread_data = series_data.get_unread()
181+
182+
fc_id_for_display = display_flow_cell_id or flow_cell_id
183+
report_point_id = f"CYTIVA_BIACORE_T200_EVALUATION_RP_C{cycle_number}_FC{fc_id_for_display}_{random_uuid_str()}"
184+
185+
custom_info: dict[str, dict[str, object]] = {
186+
"window": {"value": 5.0, "unit": "s"}
187+
}
188+
for key, value in unread_data.items():
189+
custom_info[key] = {"value": value}
190+
191+
return ReportPoint(
192+
identifier=report_point_id,
193+
identifier_role=identifier_role,
194+
absolute_resonance=absolute_resonance,
195+
time_setting=time_setting,
196+
relative_resonance=relative_resonance,
197+
custom_info=custom_info,
198+
)
199+
except Exception:
200+
series_data.get_unread()
201+
return None
202+
203+
162204
def _create_report_points_from_cycle_data(
163205
rp_df: pd.DataFrame | None,
164206
flow_cell_id: str,
@@ -169,8 +211,6 @@ def _create_report_points_from_cycle_data(
169211
if rp_df is None or rp_df.empty:
170212
return None
171213

172-
report_points: list[ReportPoint] = []
173-
174214
filtered_df = rp_df
175215
if "Flow Cell Number" in rp_df.columns or "flow_cell" in rp_df.columns:
176216
# Try to filter by flow cell
@@ -185,45 +225,21 @@ def _create_report_points_from_cycle_data(
185225
# If filtering fails, use all data (fallback)
186226
filtered_df = rp_df
187227

188-
# Map column names based on specification:
189-
# column1 = Time setting, column3 = Relative resonance, column4 = Identifier role, column5 = Absolute resonance
190-
for _idx, row in filtered_df.iterrows():
191-
try:
192-
# Extract values from the expected columns
193-
time_setting = try_float_or_none(
194-
str(row.get("column1") or row.get("Time") or 0.0)
195-
)
196-
relative_resonance = try_float_or_none(
197-
str(row.get("column3") or row.get("Relative") or 0.0)
198-
)
199-
identifier_role = str(row.get("column4") or row.get("Role") or "baseline")
200-
absolute_resonance = try_float_or_none(
201-
str(row.get("column5") or row.get("Absolute") or 0.0)
202-
)
203-
204-
# Use display flow cell ID for identifiers, fallback to base flow cell ID
205-
fc_id_for_display = display_flow_cell_id or flow_cell_id
206-
# Generate a unique identifier for this report point (include cycle number)
207-
report_point_id = f"CYTIVA_BIACORE_T200_EVALUATION_RP_C{cycle_number}_FC{fc_id_for_display}_{random_uuid_str()}"
208-
209-
report_points.append(
210-
ReportPoint(
211-
identifier=report_point_id,
212-
identifier_role=identifier_role,
213-
absolute_resonance=absolute_resonance or 0.0,
214-
time_setting=time_setting or 0.0,
215-
relative_resonance=relative_resonance,
216-
custom_info={"window": {"value": 5.0, "unit": "s"}},
217-
)
218-
)
219-
except Exception: # noqa: S112
220-
# Skip malformed rows - acceptable for data parsing
221-
continue
228+
report_points = [
229+
rp
230+
for rp in map_rows(
231+
filtered_df,
232+
lambda series_data: _create_report_point(
233+
series_data, flow_cell_id, cycle_number, display_flow_cell_id
234+
),
235+
)
236+
if rp is not None
237+
]
222238

223239
return report_points if report_points else None
224240

225241

226-
def _create_measurements_for_cycle(_: Data, cycle: CycleData) -> list[Measurement]:
242+
def _create_measurements_for_cycle(data: Data, cycle: CycleData) -> list[Measurement]:
227243
sensorgram_df = cycle.sensorgram_data
228244
cycle_num = cycle.cycle_number
229245

@@ -259,52 +275,52 @@ def _normalize_flow_cell_id(value: Any) -> str:
259275

260276
device_control_custom_info: DictType = {
261277
"buffer volume": quantity_or_none(
262-
TQuantityValueMilliliter, _.run_metadata.buffer_volume
278+
TQuantityValueMilliliter, data.run_metadata.buffer_volume
263279
),
264280
"detection": (
265-
_.run_metadata.detection_config.config.get("Detection")
266-
if _.run_metadata.detection_config
281+
data.run_metadata.detection_config.config.get("Detection")
282+
if data.run_metadata.detection_config
267283
else None
268284
),
269285
"detectiondual": (
270-
_.run_metadata.detection_config.config.get("DetectionDual")
271-
if _.run_metadata.detection_config
286+
data.run_metadata.detection_config.config.get("DetectionDual")
287+
if data.run_metadata.detection_config
272288
else None
273289
),
274290
"detectionmulti": (
275-
_.run_metadata.detection_config.config.get("DetectionMulti")
276-
if _.run_metadata.detection_config
291+
data.run_metadata.detection_config.config.get("DetectionMulti")
292+
if data.run_metadata.detection_config
277293
else None
278294
),
279295
"flowcellsingle": (
280-
_.run_metadata.detection_config.config.get("FlowCellSingle")
281-
if _.run_metadata.detection_config
296+
data.run_metadata.detection_config.config.get("FlowCellSingle")
297+
if data.run_metadata.detection_config
282298
else None
283299
),
284300
"flowcelldual": (
285-
_.run_metadata.detection_config.config.get("FlowCellDual")
286-
if _.run_metadata.detection_config
301+
data.run_metadata.detection_config.config.get("FlowCellDual")
302+
if data.run_metadata.detection_config
287303
else None
288304
),
289305
"flowcellmulti": (
290-
_.run_metadata.detection_config.config.get("FlowCellMulti")
291-
if _.run_metadata.detection_config
306+
data.run_metadata.detection_config.config.get("FlowCellMulti")
307+
if data.run_metadata.detection_config
292308
else None
293309
),
294310
"maximum operating temperature": quantity_or_none(
295-
TQuantityValueDegreeCelsius, _.run_metadata.rack_temperature_max
311+
TQuantityValueDegreeCelsius, data.run_metadata.rack_temperature_max
296312
),
297313
"minimum operating temperature": quantity_or_none(
298-
TQuantityValueDegreeCelsius, _.run_metadata.rack_temperature_min
314+
TQuantityValueDegreeCelsius, data.run_metadata.rack_temperature_min
299315
),
300316
"analysis temperature": quantity_or_none(
301-
TQuantityValueDegreeCelsius, _.run_metadata.analysis_temperature
317+
TQuantityValueDegreeCelsius, data.run_metadata.analysis_temperature
302318
),
303-
"prime": str(bool(_.run_metadata.prime)).lower()
304-
if _.run_metadata.prime is not None
319+
"prime": str(bool(data.run_metadata.prime)).lower()
320+
if data.run_metadata.prime is not None
305321
else None,
306-
"normalize": str(bool(_.run_metadata.normalize)).lower()
307-
if _.run_metadata.normalize is not None
322+
"normalize": str(bool(data.run_metadata.normalize)).lower()
323+
if data.run_metadata.normalize is not None
308324
else None,
309325
}
310326
# Add experimental data identifier per measurement via chip immobilization mapping
@@ -313,7 +329,7 @@ def _normalize_flow_cell_id(value: Any) -> str:
313329
except Exception:
314330
fc_index = None
315331
if fc_index is not None:
316-
for imm in _.chip_data.immobilizations:
332+
for imm in data.chip_data.immobilizations:
317333
if imm.flow_cell_index == fc_index and imm.ligand:
318334
device_control_custom_info = {
319335
**device_control_custom_info,
@@ -331,27 +347,27 @@ def _normalize_flow_cell_id(value: Any) -> str:
331347
# Extract kinetic analysis data for this specific flow cell
332348
# Match EvaluationItem identifier to flow cell identifier
333349
combined_kinetic_data = None
334-
if _.kinetic_analysis and _.kinetic_analysis.results_by_identifier:
350+
if data.kinetic_analysis and data.kinetic_analysis.results_by_identifier:
335351
# Try to find the specific EvaluationItem for this flow cell
336352
# Flow cell IDs are typically "1", "2", "3", "4"
337353
# EvaluationItem IDs are typically "EvaluationItem1", "EvaluationItem2", etc.
338354
matching_eval_item = None
339355

340356
# First, try direct mapping: flow cell "1" -> "EvaluationItem1"
341357
eval_item_key = f"EvaluationItem{fc_id}"
342-
if eval_item_key in _.kinetic_analysis.results_by_identifier:
358+
if eval_item_key in data.kinetic_analysis.results_by_identifier:
343359
matching_eval_item = eval_item_key
344360
else:
345361
# If direct mapping fails, look for any EvaluationItem that might correspond to this flow cell
346362
# This could be enhanced with more sophisticated matching logic if needed
347-
for eval_key in _.kinetic_analysis.results_by_identifier.keys():
363+
for eval_key in data.kinetic_analysis.results_by_identifier.keys():
348364
if fc_id in eval_key or eval_key.endswith(fc_id):
349365
matching_eval_item = eval_key
350366
break
351367

352368
# Use only the matching EvaluationItem data for this flow cell
353369
if matching_eval_item:
354-
result = _.kinetic_analysis.results_by_identifier[matching_eval_item]
370+
result = data.kinetic_analysis.results_by_identifier[matching_eval_item]
355371
combined_kinetic_data = result
356372

357373
kinetic_data = combined_kinetic_data
@@ -365,19 +381,19 @@ def _normalize_flow_cell_id(value: Any) -> str:
365381
DeviceControlDocument(
366382
device_type=constants.DEVICE_TYPE,
367383
flow_cell_identifier=display_fc_id,
368-
flow_rate=try_float_or_none(_.run_metadata.baseline_flow),
384+
flow_rate=try_float_or_none(data.run_metadata.baseline_flow),
369385
detection_type=constants.SURFACE_PLASMON_RESONANCE,
370386
device_control_custom_info=device_control_custom_info,
371387
)
372388
],
373389
well_plate_identifier=(
374-
((_.application_template_details or {}).get("racks", {}) or {}).get(
375-
"_Rack1"
376-
)
390+
(
391+
(data.application_template_details or {}).get("racks", {}) or {}
392+
).get("_Rack1")
377393
),
378394
sample_custom_info={
379395
"rack2": (
380-
(_.application_template_details or {}).get("racks", {}) or {}
396+
(data.application_template_details or {}).get("racks", {}) or {}
381397
).get("_Rack2")
382398
},
383399
sensorgram_data_cube=_get_sensorgram_datacube(

0 commit comments

Comments
 (0)