1- from typing import Any , Dict , List
1+ from typing import Any , Callable , Dict , List , Optional
22
33import pandas as pd
44from nisystemlink .clients .testmonitor .models import Result , Step , StepProjection
@@ -39,11 +39,19 @@ def convert_results_to_dataframe(
3939 return normalized_dataframe
4040
4141
42- def convert_steps_to_dataframe (steps : List [Step ]) -> pd .DataFrame :
42+ def convert_steps_to_dataframe (
43+ steps : List [Step ],
44+ is_valid_measurement : Optional [Callable [[Dict [str , Any ]], bool ]] = None ,
45+ ) -> pd .DataFrame :
4346 """Converts a list of steps into a normalized dataframe.
4447
4548 Args:
4649 steps: A list of steps.
50+ is_valid_measurement: Optional function to check if a measurement is valid. The method takes
51+ a dictionary as input and returns a boolean value. If the function is not provided, the
52+ default behavior is to keep only those measurements that have both 'name' and 'measurement' fields.
53+ If none of the measurement data have the desired fields, the data.parameters will not
54+ appear in the dataframe.
4755
4856 Returns:
4957 DataFrame:
@@ -57,7 +65,7 @@ def convert_steps_to_dataframe(steps: List[Step]) -> pd.DataFrame:
5765 all other step fields are duplicated.
5866 """
5967 DATA_PARAMETERS = "data.parameters"
60- step_dicts = __convert_steps_to_dict (steps )
68+ step_dicts = __convert_steps_to_dict (steps , is_valid_measurement )
6169 steps_dataframe = pd .json_normalize (step_dicts , sep = "." )
6270 steps_dataframe = __explode_and_normalize (
6371 steps_dataframe , DATA_PARAMETERS , f"{ DATA_PARAMETERS } ."
@@ -156,7 +164,10 @@ def __is_property_header(header: str) -> bool:
156164 return header .startswith (DataFrameHeaders .PROPERTY_COLUMN_HEADER_PREFIX )
157165
158166
159- def __convert_steps_to_dict (steps : List [Step ]) -> List [Dict [str , Any ]]:
167+ def __convert_steps_to_dict (
168+ steps : List [Step ],
169+ is_valid_measurement : Optional [Callable [[Dict [str , Any ]], bool ]] = None ,
170+ ) -> List [Dict [str , Any ]]:
160171 """Converts a list of steps to dictionaries, excluding None values.
161172
162173 Args:
@@ -168,12 +179,44 @@ def __convert_steps_to_dict(steps: List[Step]) -> List[Dict[str, Any]]:
168179 steps_dict = []
169180 for step in steps :
170181 single_step_dict = step .dict (exclude_none = True )
182+
183+ if step .data is not None and step .data .parameters is not None :
184+ single_step_dict ["data" ]["parameters" ] = __get_valid_data_parameters (
185+ single_step_dict , is_valid_measurement
186+ )
187+
171188 __normalize_inputs_outputs (single_step_dict , step )
172189 __normalize_step_status (single_step_dict )
173190 steps_dict .append (single_step_dict )
174191 return steps_dict
175192
176193
194+ def __get_valid_data_parameters (
195+ step_dict : Dict [str , Any ],
196+ is_valid_measurement : Optional [Callable [[Dict [str , Any ]], bool ]] = None ,
197+ ) -> List [Dict [str , Any ]]:
198+ """Gets valid measurement data parameters from the step dictionary.
199+
200+ Args:
201+ step_dict: A dictionary with step information.
202+ is_valid_measurement: Optional callback function to check if a measurement is valid. The method takes
203+ a dictionary as input and returns a boolean value. If the function is not provided, the
204+ default behavior is to keep only those measurements that have both 'name' and 'measurement' fields.
205+
206+ Returns:
207+ List[Dict[str, Any]]: A list of dictionaries containing valid measurement data.
208+ """
209+ allowed_measurement_keys = ["name" , "units" ]
210+ valid_measurement_parameters = []
211+ for parameter in step_dict ["data" ]["parameters" ]:
212+ if (is_valid_measurement and is_valid_measurement (parameter )) or all (
213+ key in parameter for key in allowed_measurement_keys
214+ ):
215+ valid_measurement_parameters .append (parameter )
216+
217+ return valid_measurement_parameters
218+
219+
177220def __normalize_inputs_outputs (
178221 step_dict : Dict [str , Any ],
179222 step : Step ,
0 commit comments