""" Sensospot Data Parser Parsing the numerical output from Sensovations Sensospot image analysis. """ from pathlib import Path from collections import namedtuple import numpy from defusedxml import ElementTree from .utils import apply_map from .columns import ( META_DATA_EXPOSURE_ID, META_DATA_PARAMETERS_TIME, META_DATA_PARAMETERS_CHANNEL, ) ExposureInfo = namedtuple("ExposureInfo", ["channel", "time"]) def _search_measurement_params_file(folder): """searches for a exposure settings file in a folder""" folder_path = Path(folder) params_folder = folder_path / "Parameters" if not params_folder.is_dir(): return None param_files = list(params_folder.glob("**/*.svexp")) if len(param_files) == 1: return param_files[0] else: return None def _parse_measurement_params(params_file): """parses the cannel informations from a settings file""" file_path = Path(params_file) with file_path.open("r") as file_handle: tree = ElementTree.parse(file_handle) result = {} for child in tree.find("Channels"): # child.tag == "ChannelConfig1" exposure = int(child.tag[-1]) channel_description = child.attrib["Description"] # channel_description == "[Cy3|Cy5] Green" channel = channel_description.rsplit(" ", 1)[-1] time = float(child.attrib["ExposureTimeMs"]) result[exposure] = ExposureInfo(channel.lower(), time) return result def get_measurement_params(folder): """returns measurement parameters""" params_file = _search_measurement_params_file(folder) if params_file is not None: return _parse_measurement_params(params_file) return None def _add_measurement_params(data_frame, params): """adds measurement parameters to a data frame""" columns = [META_DATA_PARAMETERS_CHANNEL, META_DATA_PARAMETERS_TIME] map = {k: dict(zip(columns, v)) for k, v in params.items()} return apply_map(data_frame, map, META_DATA_EXPOSURE_ID) def add_optional_measurement_parameters(data_frame, folder): """adds measurement params to the data frame, if they could be parsed""" params = get_measurement_params(folder) if params: available_exposures = set(data_frame[META_DATA_EXPOSURE_ID].unique()) if available_exposures == set(params.keys()): return _add_measurement_params(data_frame, params) else: data_frame[META_DATA_PARAMETERS_CHANNEL] = numpy.nan data_frame[META_DATA_PARAMETERS_TIME] = numpy.nan return data_frame