7777 "Time" ,
7878]
7979
80+ FIELDS_TO_SKIP = {
81+ "$OP" , # Already captured as analyst
82+ }
83+
84+ # Fields for device control document
85+ DEVICE_CONTROL_FIELDS = {
86+ "CST SETUP STATUS" ,
87+ "CST BEADS LOT ID" ,
88+ "CYTOMETER CONFIG NAME" ,
89+ "CYTOMETER CONFIG CREATE DATE" ,
90+ "CST SETUP DATE" ,
91+ "CST BASELINE DATE" ,
92+ "CST BEADS EXPIRED" ,
93+ }
94+
95+ MEASUREMENT_AGGREGATE_FIELDS = {
96+ "$BTIM" ,
97+ }
98+
8099
81100def _filter_unread_data (unread_data : dict [str , str | None ]) -> dict [str , str | None ]:
82101 """Filter out empty, null, or whitespace-only values from unread data."""
@@ -91,6 +110,26 @@ def _filter_unread_data(unread_data: dict[str, str | None]) -> dict[str, str | N
91110 }
92111
93112
113+ def _separate_custom_fields (
114+ custom_info : dict [str , str ]
115+ ) -> tuple [dict [str , str ], dict [str , str ], dict [str , str ]]:
116+ device_control_info = {}
117+ measurement_aggregate_info = {}
118+ other_info = {}
119+
120+ for key , value in custom_info .items ():
121+ if key in FIELDS_TO_SKIP :
122+ continue
123+ elif key in DEVICE_CONTROL_FIELDS :
124+ device_control_info [key ] = value
125+ elif key in MEASUREMENT_AGGREGATE_FIELDS :
126+ measurement_aggregate_info [key ] = value
127+ else :
128+ other_info [key ] = value
129+
130+ return device_control_info , measurement_aggregate_info , other_info
131+
132+
94133class RegionType (Enum ):
95134 POLYGON = "POLYGON"
96135 RECTANGLE = "RECTANGLE"
@@ -533,15 +572,15 @@ def _create_compensation_matrix_groups(
533572
534573def _process_tube (
535574 tube : StrictXmlElement , specimen_name : str | None , data_processing_time : str | None
536- ) -> list [Measurement ]:
575+ ) -> tuple [ list [Measurement ], dict [ str , str ] ]:
537576 """
538577 Process a tube element to create measurements.
539578
540579 Args:
541580 tube: The tube element to process
542581
543582 Returns:
544- list[Measurement]: List containing a single Measurement
583+ tuple: (measurements, measurement_aggregate_custom_info)
545584 """
546585 tube_name = tube .get_attr ("name" )
547586
@@ -562,6 +601,12 @@ def _process_tube(
562601 if keyword_value :
563602 custom_info [keyword_name ] = keyword_value
564603
604+ (
605+ device_control_info ,
606+ measurement_aggregate_info ,
607+ other_info ,
608+ ) = _separate_custom_fields (custom_info )
609+
565610 # Create populations and data regions
566611 populations = _create_populations (tube )
567612 data_regions = _create_data_regions (tube )
@@ -574,10 +619,11 @@ def _process_tube(
574619 processed_data_identifier = processed_data_id ,
575620 populations = populations ,
576621 data_regions = data_regions ,
577- custom_info = custom_info if custom_info else None ,
622+ device_control_custom_info = device_control_info if device_control_info else None ,
623+ custom_info = other_info if other_info else None ,
578624 )
579625
580- return [measurement ]
626+ return [measurement ], measurement_aggregate_info
581627
582628
583629def create_measurement_groups (root_element : StrictXmlElement ) -> list [MeasurementGroup ]:
@@ -608,7 +654,7 @@ def create_measurement_groups(root_element: StrictXmlElement) -> list[Measuremen
608654 tubes = specimen .findall ("tube" )
609655 for tube in tubes :
610656 experimental_data_identifier = tube .find_or_none ("data_filename" )
611- measurements = _process_tube (
657+ measurements , measurement_aggregate_custom_info = _process_tube (
612658 tube ,
613659 specimen_name ,
614660 export_time .get_text_or_none () if export_time else None ,
@@ -626,6 +672,9 @@ def create_measurement_groups(root_element: StrictXmlElement) -> list[Measuremen
626672 compensation_matrix_groups = compensation_matrix_groups ,
627673 measurements = measurements ,
628674 experiment_identifier = experiment_identifier ,
675+ measurement_aggregate_custom_info = measurement_aggregate_custom_info
676+ if measurement_aggregate_custom_info
677+ else None ,
629678 )
630679
631680 result .append (measurement_group )
0 commit comments