From 554acff84485ac207b0306075c8d52259b6da965 Mon Sep 17 00:00:00 2001 From: Tim Ruhland Date: Fri, 19 Jul 2024 14:15:30 +0200 Subject: [PATCH 1/3] new metadata extraction feature --- .../dcc_gp_temperature_typical_v12_QoX.xml | 693 ++++++++++++++++++ dcc/dcc.py | 94 ++- doc/pydcc.md | 18 +- examples/read_QoX_metadata.py | 117 +++ tests/unit_test.py | 85 ++- 5 files changed, 980 insertions(+), 27 deletions(-) create mode 100644 data/dcc/dcc_gp_temperature_typical_v12_QoX.xml create mode 100644 examples/read_QoX_metadata.py diff --git a/data/dcc/dcc_gp_temperature_typical_v12_QoX.xml b/data/dcc/dcc_gp_temperature_typical_v12_QoX.xml new file mode 100644 index 0000000..030a04d --- /dev/null +++ b/data/dcc/dcc_gp_temperature_typical_v12_QoX.xml @@ -0,0 +1,693 @@ + + + + + + + + Notepad++ (32-bit) + + v 8.2 + + + + + + Quality of X - Wörterbuch + Quality of X - Wording + + QoX + https://da-wo-es-liegt.de + + + + DE + de + en + de + GP_DCC_temperature_typical_1.2 + + + calibrationLaboratory + string-calibrationLaboratory-coreData + + Auftrags Nr. + Order no. + + + + 1957-08-13 + 1957-08-13 + laboratory + + + + + Temperatur-Fühler + Temperature sensor + + + + String + + + String + + + manufacturer + string-manufacturer-item + + Serien Nr. + Serial no. + + + + customer + string-customer-item + + Messmittel Nr. + Measurement equipment no. + + + + calibrationLaboratory + string-calibrationLaboratory-item + + Equipment Nr. + Equipment no. + + + + + + + + + Kalibrierfirma GmbH + + info@kalibrierfirma.xx + +49 123 4567-89 + +49 123 4567-90 + + Musterstadt + DE + 00900 + Musterstraße + 1 + + www.kalibrierfirma.xx + + + + + + + + + Michaela Musterfrau + + + true + + + + + Michael Mustermann + + + + + + + Kunde GmbH + + info@kunde.xx + + Musterstadt + DE + 00900 + + Kunden Nr. 1024418 + Customer ID no. 1024418 + + + + + + ISO/IEC 17025:2018-03 + 7.8.4.3 + + Die Ergebnisse gelten zum Zeitpunkt der Kalibrierung. Es obliegt dem Antragsteller, zu gegebener Zeit eine Rekalibrierung zu veranlassen. + The results refer only to the object calibrated in this DCC. The measurement results are valid at the time of calibration. The applicant is responsible for arranging a recalibration in due time. + + + + + Angabe des Temperaturbereichs, in dem kalibriert wurde: + Specification of the temperature range in which calibration was performed: + + + + + Unteres Limit + Lower limit + + + 306 + \kelvin + + + + + Oberes Limit + Upper limit + + + 593 + \kelvin + + + + + + + Die Konformitätsaussage erfolgt anhand der Vorgaben des Kunden. Sie sind im DCC mit aufgeführt. + The conformity statement is made on the basis of the customer's specifications. They are listed in the DCC. + + + + Kunde GmbH + + info@kunde.xx + + Musterstadt + 00900 + DE + + + pass + + + + Datum, wann nach der Festlegung durch den Kunden spätestens der Kalibriergegenstand rekalibriert werden soll: + Date when the calibration item is to be recalibrated at the latest according to the customer's specification: + + 1959-10-22 + + + Kunde GmbH + + info@kunde.xx + + Musterstadt + 00900 + DE + + + + + + Link-zu-QoD beschreibung + + Hier könnte stehen wie QoD ausgewerte werden kann. + Here you can read how QoD can be evaluated. + + + + + + + + Messergebnisse + Measurement results + + + + + Erweiterte Messunsicherheit + Expanded uncertainty + + + Angegeben ist die erweiterte Messunsicherheit, die sich aus der Standardmessunsicherheit durch Multiplikation mit dem Erweiterungsfaktor k=2 ergibt. Sie wurde gemäß dem „Guide to the Expression of Uncertainty in Measurement (GUM)“ ermittelt. Der Wert der Messgröße liegt dann im Regelfall mit einer Wahrscheinlichkeit von annähernd 95 % im zugeordneten Überdeckungsintervall. + The expanded uncertainty was calculated from the contributions of uncertainty originating from the standards used, from the weighings and the air buoyancy corrections. The reported uncertainty does not include an estimate of long-term variations. + + GUM + + + + Kalibrierung von Temperaturmessfühlern + Calibration of temperature sensors + + DKD-R 5-1:2018 + + + + + + Pt 100 Widerstandsthermometer + Pt 100 thermometer + + + + manufacturer + string-manufacturer-measuringEquipment-1 + + + + + + + + Eintauchtiefe im Wasserbad + Immersion depth in water bath + + + + + Eintauchtiefe + Immersion depth + + + 0.1 + \metre + + + + + + + Umgebungsbedingung Temperatur + Ambient condition temperature + + + Diese Werte wurden nicht gemessen, sondern wurden anhand der typischen Wetterbedingungen zu einer Jahreszeit angegeben. [^1] + These values were not measured, but were given based on typical weather conditions at a time of year. [^1] + + + + + Temperatur min + temperature min + + + 293 + \kelvin + + + + + Temperatur max + temperature max + + + 299 + \kelvin + + + + + + + Umgebungsbedingung relative Luftfeuchte + Ambient condition relative humidity + + + Diese Werte wurden nicht gemessen, sondern wurden anhand der typischen Wetterbedingungen zu einer Jahreszeit angegeben. [^1] + These values were not measured, but were given based on typical weather conditions at a time of year. [^1] + + + + + Feuchte min + humidity min + + + 0.20 + \one + + + + + Feuchte max + humidity max + + + 0.70 + \one + + + + + + + + + Messergebnisse + Measuring results + + + + + + Bezugswert + Reference value + + + + 306.248 373.121 448.253 523.319 593.154 + \kelvin + + + 33.098 99.971 175.103 250.169 320.004 + \degreecelsius + + + + + + Kalibrierpunkt + Calibration value + + + + + + 306 373 448 523 593 + \kelvin + + + 32.85 99.85 174.85 249.85 319.85 + \degreecelsius + + + + + + + + + + Angezeigter Messwert Kalibriergegenstand + Indicated measured value probe + + + + 306.32 373.21 448.36 523.31 593.07 + \kelvin + + + 33.17 100.06 175.21 250.16 319.92 + \degreecelsius + + + + + + Messabweichung + Measurement error + + + 0.072 0.089 0.107 -0.009 -0.084 + \kelvin + + 0.061 + 2 + 0.95 + normal + + + + + + + Konformität + Conformity + + + pass + + + + Unteres Akzeptanzlimit + Lower acceptance limit + + + -0.23 -0.23 -0.23 -0.30 -0.30 + \kelvin + + + + + Oberes Akzeptanzlimit + Upper acceptance limit + + + 0.23 0.23 0.23 0.30 0.30 + \kelvin + + + + + + + pass + + + + Qualitätskennzahl aus dem GEMIMEG-II Projekt + + + 0.8 0.8 0.8 0.7 0.5 + \one + + + + + + + + + + + Parameter für QoS/QoD + Parameter for QoS/QoD + + + + + + accuracy + + + + evaluation_size + + + 10.0 + \one + + + + + fuzzy_membership_accurate + + + 0.15 + \milli\kelvin\second\tothe{-1} + + + + + fuzzy_membership_inaccurate + + + 0.35 + \milli\kelvin\second\tothe{-1} + + + + + + + timeliness + + + + threshold + + + 1000.0 + \milli\second + + + + + window_size + + + 10 + \one + + + + + fusion_weight_threshold + + + 100 + \percent + + + + + fusion_weight_window + + + 100 + \percent + + + + + fusion_weight_average + + + 100 + \percent + + + + + fuzzy_membership_timely + + + 1.0 + \one + + + + + fuzzy_membership_delayed + + + 1.25 + \one + + + + + fuzzy_membership_Late + + + 1.50 + \one + + + + + + + completeness + + + + packet_rate + + + 1.0 + \second\tothe{-1} + + + + + evaluation_size + + + 10 + \one + + + + + + + consistency + + + + deviation_threshold + + + 0.0 + \percent + + + + + value_range_min + + + -40.0 + \kelvin + + + + + value_range_max + + + 125.0 + \kelvin + + + + + + + + + + + + + + diff --git a/dcc/dcc.py b/dcc/dcc.py index 3506f53..339da71 100644 --- a/dcc/dcc.py +++ b/dcc/dcc.py @@ -3,10 +3,11 @@ # Python module for processing of digital calibration certificates (DCC) # according to https://www.ptb.de/dcc/ # -# Copyright (c) Siemens AG, 2021 +# Copyright (c) Siemens AG, 2024 # # Authors: # Andreas Tobola, Siemens AG +# Tim Ruhland, Siemens AG # # This work is licensed under the terms of the MIT License. # See the LICENSE file in the top-level directory. @@ -436,7 +437,7 @@ def __read_name(self, node, name, lang): name = name + ' ' + local_name.text return name - def __read_path_realted_info(self, node, attr): + def __read_path_related_info(self, node, attr): attr = attr + "dcc:" + str(str(node.tag.rpartition('}')[2])) if "refType" in node.attrib.keys(): attr = attr + " [ @ refType =" + "\'" + str(node.attrib['refType']) + "\'" + "]" @@ -449,7 +450,7 @@ def __read_path_realted_info(self, node, attr): def __find_quantities_in_lists(self, node, quant, name, lang, xpath): name = self.__read_name(node, name, lang) - xpath = self.__read_path_realted_info(node, xpath) + xpath = self.__read_path_related_info(node, xpath) if node.tag == '{https://ptb.de/dcc}quantity': quant.append([node, name, xpath]) @@ -461,19 +462,27 @@ def __find_quantities_in_lists(self, node, quant, name, lang, xpath): def get_calibration_results(self, type, lang=''): quantities = [] res = [] - result_nodes = self.root.findall('dcc:measurementResults/dcc:measurementResult/dcc:results/dcc:result', - self.name_space) - for result in result_nodes: + meas_result_nodes = self.root.findall('dcc:measurementResults/dcc:measurementResult', self.name_space) + + for meas_result in meas_result_nodes: xpath = ".//" - xpath = self.__read_path_realted_info(result, xpath) + xpath = self.__read_path_related_info(meas_result, xpath) xpath = xpath + " //" - - data_node = result.find('dcc:data', self.name_space) name = '' - name = self.__read_name(result, name, lang) + name = self.__read_name(meas_result, name, lang) + result_nodes = meas_result.findall('./dcc:results/dcc:result', self.name_space) + + for result in result_nodes: + xpath_res = xpath + xpath_res = self.__read_path_related_info(result, xpath_res) + xpath_res = xpath_res + " //" - for nodes in data_node: - self.__find_quantities_in_lists(nodes, quantities, name, lang, xpath) + data_node = result.find('dcc:data', self.name_space) + name_res = name + name_res = self.__read_name(result, name_res, lang) + + for nodes in data_node: + self.__find_quantities_in_lists(nodes, quantities, name_res, lang, xpath_res) for quant in quantities: si_node = quant[0].find('{https://ptb.de/si}*', self.name_space) @@ -482,8 +491,64 @@ def get_calibration_results(self, type, lang=''): local_res = [quant[2], self.__etree_to_dict(si_node)] else: local_res = [quant[1], self.__etree_to_dict(si_node)] - res.append(local_res) + res.append(local_res) + return res + + def get_calibration_metadata(self, refType, type_format= 'xpath', lang=''): + """ + Retrieves calibration metadata based on a specific reference type. + This method locates metadata within an XML structure based on the refType and extracts it into a dictionary. + + Args: + refType (str): The specific reference type of the metadata to filter by. + type_format (str, optional): The format type for the metadata retrieval, defaulting to 'xpath'. + lang (str, optional): The language of the metadata. + + Returns: + dict: A dictionary representing the retrieved metadata elements. + """ + meas_result_elementTree = self.root.find('dcc:measurementResults/dcc:measurementResult', self.name_space) + entire_xml_dict = self.__etree_to_dict(meas_result_elementTree) + + def find_key_and_get_parents(data, target_key, target_value): + def traverse_dict(d, target_key, target_value, path): + if isinstance(d, dict): + for key, value in d.items(): + new_path = path + [key] if key != target_key else path + if key == target_key and value == target_value: + return new_path + if isinstance(value, dict): + result = traverse_dict(value, target_key, target_value, new_path) + if result: + return result + elif isinstance(value, list): + for index, item in enumerate(value): + result = traverse_dict(item, target_key, target_value, new_path + [f'index{index}']) + if result: + return result + return None + + path = traverse_dict(data, target_key, target_value, []) + if path is None: + return None + + parent_keys = [key for key in path if not key.startswith('index')] + return parent_keys + + def format_items_as_path(items): + return '/'.join(f'dcc:{item}' for item in items) + + + target_key = '@refType' + target_value = refType + result_path = find_key_and_get_parents(entire_xml_dict, target_key, target_value) + xml_path = format_items_as_path(result_path) + + metadata_elementTree = self.root.find('dcc:measurementResults/' + xml_path, self.name_space) + metadata_dict = self.__etree_to_dict(metadata_elementTree) + + return metadata_dict def __etree_to_dict(self, t): """ @@ -492,7 +557,7 @@ def __etree_to_dict(self, t): This method is licensed under a Creative Commons Attribution ShareAlike 3.0 (CC-BY-SA 3.0) License URL: http://creativecommons.org/licenses/by/3.0/deed.en_US """ - # method to recursively traverse the xml tree from a specified point and to return the elemnts in dictionary form + # method to recursively traverse the xml tree from a specified point and to return the elements in dictionary form tkey = t.tag.rpartition('}')[2] tree_dict = {tkey: {} if t.attrib else None} children = list(t) @@ -551,4 +616,3 @@ def get_item_id_by_name(self, searched_name, searched_language = None, searched_ class DCCSignatureError(Exception): """ this exception is raised if any problem with the validation of the DCC signature occurs""" - diff --git a/doc/pydcc.md b/doc/pydcc.md index a8373f1..3b3a4a0 100644 --- a/doc/pydcc.md +++ b/doc/pydcc.md @@ -75,12 +75,12 @@ if not dcco.status_report.is_loaded: The schema verification must be executed after loading the DCC. -Verify DCC file according to the official XML shema [2] when internet connection is available. +Verify DCC file according to the official XML schema [2] when internet connection is available. ```python dcco.verify_dcc_xml(online=True) ``` -VVerify the DCC file according to the official XML schema [2] when the internet connection is unavailable. +Verify the DCC file according to the official XML schema [2] when the internet connection is unavailable. In this case, please make sure to download all required schema files to the local repository using the schema downloader class. ```python dcco.verify_dcc_xml(online=False) @@ -123,7 +123,7 @@ dcco.get_signing_time() ## Calibration Date -Returns calibration date as datetime object. Note that the DCC defines the start date (beginPerformanceDate) and the end date (endPerformanceDate) of calibration. The date retured by this API reffers to the end of calibration (endPerformanceDate). +Returns calibration date as datetime object. Note that the DCC defines the start date (beginPerformanceDate) and the end date (endPerformanceDate) of calibration. The date returned by this API refers to the end of calibration (endPerformanceDate). ```python dcco.calibration_date() ``` @@ -143,6 +143,7 @@ dcco.calibration_laboratory_name() ``` + ## Links to other documents Return true if a link to a previous DCC exists. @@ -195,10 +196,19 @@ serial_number = dcco.get_item_id_by_name('Serial no.') Try the example code in ../examples/read_identifications.py +## Get metadata from DCC + +Metadata in a DCC includes information about the calibration, such as the basic calibration value, the basic conformity, or the data quality (QoX) parameters. This information can be retrieved using the `get_calibration_metadata` method. +```python +metadata = dcco.get_calibration_metadata(refType='QoX_parameter') +for item in metadata: + print(item) +``` +Try the example code in ../examples/read_QoX_metadata.py -## Get the mendatory language +## Get the mandatory language ```python dcco.mandatory_language() diff --git a/examples/read_QoX_metadata.py b/examples/read_QoX_metadata.py new file mode 100644 index 0000000..f2333df --- /dev/null +++ b/examples/read_QoX_metadata.py @@ -0,0 +1,117 @@ +# PyDCC +# +# Automated uncertainty verification example +# +# Copyright (c) Siemens AG, 2024 +# +# Authors: +# Tim Ruhland, Siemens AG +# +# This work is licensed under the terms of the MIT License. +# See the LICENSE file in the top-level directory. +# +# SPDX-License-Identifier: MIT +# +# This example demonstrates the automated extraction of metadata from a Digital Calibration Certificate (DCC). +# It focuses on retrieving calibration QoX parameters and verifying their compliance with required standards. +# This process ensures the applicability of automated parameter extraction from the DCC . + + +import sys +sys.path.append("../dcc/") +# from dcc import DCC +import numpy as np + +# TODO: REMOVE +sys.path.append(r"D:\git_projects\pydcc") +from dcc.dcc import DCC + + +def search_metadata_results(data, reftype): + def find_in_dict(d): + if isinstance(d, dict): + if '@refType' in d and d['@refType'] == reftype: + return d + for key, value in d.items(): + result = find_in_dict(value) + if result: + return result + elif isinstance(d, list): + for item in d: + result = find_in_dict(item) + if result: + return result + return None + + if 'metaData' in data: + return find_in_dict(data['metaData']) + return None + +def get_all_quantities_from_list(list_refType, data): + def find_list(d): + if isinstance(d, dict) and d.get('@refType') == list_refType: + return d + elif isinstance(d, dict): + for value in d.values(): + result = find_list(value) + if result: + return result + elif isinstance(d, list): + for item in d: + result = find_list(item) + if result: + return result + return None + + def extract_quantities(d): + quantities = {} + if isinstance(d, list): + for item in d: + if isinstance(item, dict) and '@refType' in item: + if 'realListXMLList' in item: + value = item['realListXMLList']['valueXMLList'] + unit = item['realListXMLList']['unitXMLList'] + try: + value = float(value) + except ValueError: + pass + quantities[item['@refType']] = (value, unit) + elif isinstance(d, dict): + for key, value in d.items(): + if key == 'quantity' and isinstance(value, list): + for quantity in value: + if '@refType' in quantity and 'realListXMLList' in quantity: + val = quantity['realListXMLList']['valueXMLList'] + uni = quantity['realListXMLList']['unitXMLList'] + try: + val = float(val) + except ValueError: + pass + quantities[quantity['@refType']] = (val, uni) + return quantities + + specific_list = find_list(data) + if specific_list: + return extract_quantities(specific_list.get('quantity', [])) + return {} + + +if __name__ == '__main__': + + # Load DCC and create the DCC object (dcco) + dcco = DCC('D:/git_projects/pydcc/data/dcc/dcc_gp_temperature_typical_v12_QoX.xml') + + if not dcco.status_report.is_loaded: + print("Error: DCC was not loaded successfully!") + + # Get the metadata dictionary for the searched "dcc:metaData" reference type + metadata_dict = dcco.get_calibration_metadata(refType='QoX_parameter') + metadata_dict_2 = dcco.get_calibration_metadata(refType='basic_conformity') + + # Search for metadata results with a specific quantity reference type + metadata_quantity_dict = search_metadata_results(metadata_dict, 'QoX_packetRate') + print("Metadata QoX_packetRate: %s" % metadata_quantity_dict) + + # Get all quantities within a specific list + all_quantities = get_all_quantities_from_list('QoX_completeness', metadata_dict) + print("All quantities: %s" % all_quantities) diff --git a/tests/unit_test.py b/tests/unit_test.py index 00fafd9..9163ff5 100644 --- a/tests/unit_test.py +++ b/tests/unit_test.py @@ -3,10 +3,11 @@ # Python module for processing of digital calibration certificates (DCC) # according to https://www.ptb.de/dcc/ # -# Copyright (c) Siemens AG, 2021 +# Copyright (c) Siemens AG, 2024 # # Authors: # Andreas Tobola, Siemens AG +# Tim Ruhland, Siemens AG # # This work is licensed under the terms of the MIT License. # See the LICENSE file in the top-level directory. @@ -88,15 +89,83 @@ def test_mandatoryLang(self): def test_get_calibration_results(self): res = dcco_gp.get_calibration_results('name') - self.assertEqual(res[0], [' Messergebnisse Bezugswert', {'hybrid': {'realListXMLList': [{'valueXMLList': '306.248 373.121 448.253 523.319 593.154', 'unitXMLList': '\\kelvin'}, {'valueXMLList': '33.098 99.971 175.103 250.169 320.004', 'unitXMLList': '\\degreecelsius'}]}}]) - self.assertEqual(res[1], [' Messergebnisse Angezeigter Messwert Kalibriergegenstand', {'hybrid': {'realListXMLList': [{'valueXMLList': '306.32 373.21 448.36 523.31 593.07', 'unitXMLList': '\\kelvin'}, {'valueXMLList': '33.17 100.06 175.21 250.16 319.92', 'unitXMLList': '\\degreecelsius'}]}}]) - self.assertEqual(res[2], [' Messergebnisse Messabweichung', {'realListXMLList': {'valueXMLList': '0.072 0.089 0.107 -0.009 -0.084', 'unitXMLList': '\\kelvin', 'expandedUncXMLList': {'uncertaintyXMLList': '0.061', 'coverageFactorXMLList': '2', 'coverageProbabilityXMLList': '0.95', 'distributionXMLList': 'normal'}}}]) + + # Test if dcc_list is a list + self.assertIsInstance(res, list) + + # Test with a different language + self.assertEqual(len(res), 3) + + for result in res: + self.assertIsInstance(result, list) + self.assertEqual(len(result), 2) + self.assertIsInstance(result[0], str) + self.assertIsInstance(result[1], dict) + + self.assertEqual(res[0], [' Messergebnisse Messergebnisse Bezugswert', {'hybrid': {'realListXMLList': [{'valueXMLList': '306.248 373.121 448.253 523.319 593.154', 'unitXMLList': '\\kelvin'}, {'valueXMLList': '33.098 99.971 175.103 250.169 320.004', 'unitXMLList': '\\degreecelsius'}]}}]) + self.assertEqual(res[1], [' Messergebnisse Messergebnisse Angezeigter Messwert Kalibriergegenstand', {'hybrid': {'realListXMLList': [{'valueXMLList': '306.32 373.21 448.36 523.31 593.07', 'unitXMLList': '\\kelvin'}, {'valueXMLList': '33.17 100.06 175.21 250.16 319.92', 'unitXMLList': '\\degreecelsius'}]}}]) + self.assertEqual(res[2], [' Messergebnisse Messergebnisse Messabweichung', {'realListXMLList': {'valueXMLList': '0.072 0.089 0.107 -0.009 -0.084', 'unitXMLList': '\\kelvin', 'expandedUncXMLList': {'uncertaintyXMLList': '0.061', 'coverageFactorXMLList': '2', 'coverageProbabilityXMLList': '0.95', 'distributionXMLList': 'normal'}}}]) res = dcco_gp.get_calibration_results('xpath') - self.assertEqual(res[0], [".//dcc:result [ @ refType ='gp_measuringResult1'] //dcc:list [ @ refType ='gp_table1'] //dcc:quantity [ @ refType ='basic_referenceValue']", {'hybrid': {'realListXMLList': [{'valueXMLList': '306.248 373.121 448.253 523.319 593.154', 'unitXMLList': '\\kelvin'}, {'valueXMLList': '33.098 99.971 175.103 250.169 320.004','unitXMLList': '\\degreecelsius'}]}}]) - self.assertEqual(res[1], [".//dcc:result [ @ refType ='gp_measuringResult1'] //dcc:list [ @ refType ='gp_table1'] //dcc:quantity [ @ refType ='basic_measuredValue']", {'hybrid': {'realListXMLList': [{'valueXMLList': '306.32 373.21 448.36 523.31 593.07', 'unitXMLList': '\\kelvin'}, {'valueXMLList': '33.17 100.06 175.21 250.16 319.92', 'unitXMLList': '\\degreecelsius'}]}}]) - self.assertEqual(res[2], [".//dcc:result [ @ refType ='gp_measuringResult1'] //dcc:list [ @ refType ='gp_table1'] //dcc:quantity [ @ refType ='basic_measurementError']", {'realListXMLList': {'valueXMLList': '0.072 0.089 0.107 -0.009 -0.084', 'unitXMLList': '\\kelvin', 'expandedUncXMLList': {'uncertaintyXMLList': '0.061', 'coverageFactorXMLList': '2', 'coverageProbabilityXMLList': '0.95', 'distributionXMLList': 'normal'}}}]) - + self.assertEqual(res[0], [".//dcc:measurementResult //dcc:result [ @ refType ='gp_measuringResult1'] //dcc:list [ @ refType ='gp_table1'] //dcc:quantity [ @ refType ='basic_referenceValue']", {'hybrid': {'realListXMLList': [{'valueXMLList': '306.248 373.121 448.253 523.319 593.154', 'unitXMLList': '\\kelvin'}, {'valueXMLList': '33.098 99.971 175.103 250.169 320.004','unitXMLList': '\\degreecelsius'}]}}]) + self.assertEqual(res[1], [".//dcc:measurementResult //dcc:result [ @ refType ='gp_measuringResult1'] //dcc:list [ @ refType ='gp_table1'] //dcc:quantity [ @ refType ='basic_measuredValue']", {'hybrid': {'realListXMLList': [{'valueXMLList': '306.32 373.21 448.36 523.31 593.07', 'unitXMLList': '\\kelvin'}, {'valueXMLList': '33.17 100.06 175.21 250.16 319.92', 'unitXMLList': '\\degreecelsius'}]}}]) + self.assertEqual(res[2], [".//dcc:measurementResult //dcc:result [ @ refType ='gp_measuringResult1'] //dcc:list [ @ refType ='gp_table1'] //dcc:quantity [ @ refType ='basic_measurementError']", {'realListXMLList': {'valueXMLList': '0.072 0.089 0.107 -0.009 -0.084', 'unitXMLList': '\\kelvin', 'expandedUncXMLList': {'uncertaintyXMLList': '0.061', 'coverageFactorXMLList': '2', 'coverageProbabilityXMLList': '0.95', 'distributionXMLList': 'normal'}}}]) + + def test_get_calibration_metadata(self): + xml_file_name_QoX = 'dcc_gp_temperature_typical_v12_QoX.xml' + xml_file_path_QoX = '../data/dcc/' + xml_file_name_QoX + dcco_QoX = DCC(xml_file_path_QoX) + + # Check if the DCC file is loaded correctly + self.assertTrue(dcco_QoX.is_loaded()) + + # Retrieve metadata for a specific reference type + metadata_dict = dcco_QoX.get_calibration_metadata(refType='QoX_parameter') + + # Test if metadata_dict is a dictionary + self.assertTrue(isinstance(metadata_dict, dict)) + + # Verify that the top-level keys are correctly set + self.assertIn('metaData', metadata_dict) + self.assertIn('data', metadata_dict['metaData']) + + # Verify the content under 'declaration' + self.assertIn('declaration', metadata_dict['metaData']) + declaration_content = metadata_dict['metaData']['declaration']['content'] + self.assertEqual(len(declaration_content), 2) # Expecting two language entries + self.assertEqual(declaration_content[0]['@lang'], 'de') + self.assertEqual(declaration_content[1]['@lang'], 'en') + self.assertEqual(declaration_content[0]['#text'], 'Parameter für QoS/QoD') + self.assertEqual(declaration_content[1]['#text'], 'Parameter for QoS/QoD') + + # Verify details within the 'data' -> 'list' + data_list = metadata_dict['metaData']['data']['list'] + self.assertIsInstance(data_list, list) + self.assertGreater(len(data_list), 0) # Ensure there is at least one item + + # Check details of the first item in the list for specific quantities + first_item = data_list[0] + self.assertIn('name', first_item) + self.assertIn('content', first_item['name']) + self.assertEqual(first_item['name']['content'], 'accuracy') + self.assertIn('quantity', first_item) + self.assertIsInstance(first_item['quantity'], list) + + # Detailed check on each quantity item + for quantity in first_item['quantity']: + self.assertIn('@refType', quantity) + self.assertIn('realListXMLList', quantity) + self.assertIn('valueXMLList', quantity['realListXMLList']) + self.assertIn('unitXMLList', quantity['realListXMLList']) + + # Check specific types of quantities + if quantity['@refType'] == 'QoX_evaluationSize': + self.assertEqual(quantity['realListXMLList']['valueXMLList'], '10.0') + self.assertEqual(quantity['realListXMLList']['unitXMLList'], '\\one') + elif quantity['@refType'] == 'QoX_fuzzyMembershipAccurate': + self.assertEqual(quantity['realListXMLList']['valueXMLList'], '0.15') + self.assertEqual(quantity['realListXMLList']['unitXMLList'], '\\milli\\kelvin\\second\\tothe{-1}') + def test_calibration_date(self): calib_date = dcco_gp.calibration_date() ref_date = datetime.datetime(1957, 8, 13, 0, 0) From a5923f67cf4abcfd3ccf9a65e33c5ee106594dc1 Mon Sep 17 00:00:00 2001 From: Tim Ruhland Date: Tue, 23 Jul 2024 09:23:38 +0200 Subject: [PATCH 2/3] Update copyright year and delete path --- dcc/dcc.py | 2 +- examples/read_QoX_metadata.py | 8 ++------ tests/unit_test.py | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/dcc/dcc.py b/dcc/dcc.py index 339da71..30dfaee 100644 --- a/dcc/dcc.py +++ b/dcc/dcc.py @@ -3,7 +3,7 @@ # Python module for processing of digital calibration certificates (DCC) # according to https://www.ptb.de/dcc/ # -# Copyright (c) Siemens AG, 2024 +# Copyright (c) Siemens AG, 2021 # # Authors: # Andreas Tobola, Siemens AG diff --git a/examples/read_QoX_metadata.py b/examples/read_QoX_metadata.py index f2333df..1067bbd 100644 --- a/examples/read_QoX_metadata.py +++ b/examples/read_QoX_metadata.py @@ -18,12 +18,8 @@ import sys -sys.path.append("../dcc/") -# from dcc import DCC import numpy as np - -# TODO: REMOVE -sys.path.append(r"D:\git_projects\pydcc") +sys.path.append("../dcc/") from dcc.dcc import DCC @@ -99,7 +95,7 @@ def extract_quantities(d): if __name__ == '__main__': # Load DCC and create the DCC object (dcco) - dcco = DCC('D:/git_projects/pydcc/data/dcc/dcc_gp_temperature_typical_v12_QoX.xml') + dcco = DCC('../data/dcc/dcc_gp_temperature_typical_v12_QoX.xml') if not dcco.status_report.is_loaded: print("Error: DCC was not loaded successfully!") diff --git a/tests/unit_test.py b/tests/unit_test.py index 9163ff5..e024703 100644 --- a/tests/unit_test.py +++ b/tests/unit_test.py @@ -3,7 +3,7 @@ # Python module for processing of digital calibration certificates (DCC) # according to https://www.ptb.de/dcc/ # -# Copyright (c) Siemens AG, 2024 +# Copyright (c) Siemens AG, 2021 # # Authors: # Andreas Tobola, Siemens AG From 45a8cacfc4fcd62622d36ee0b35ecee2ff803d1e Mon Sep 17 00:00:00 2001 From: Andreas Tobola Date: Tue, 23 Jul 2024 09:40:28 +0200 Subject: [PATCH 3/3] Version increment --- next_version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/next_version.txt b/next_version.txt index db0c2a6..0a60c4f 100644 --- a/next_version.txt +++ b/next_version.txt @@ -1 +1 @@ -0.2.31 \ No newline at end of file +0.2.32 \ No newline at end of file