4141 Data ,
4242 SystemInformation ,
4343)
44+ from allotropy .parsers .utils .pandas import map_rows , SeriesData
4445from allotropy .parsers .utils .uuids import random_uuid_str
4546from allotropy .parsers .utils .values import quantity_or_none , try_float_or_none
4647from 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+
162204def _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