From e9896ea2744954fd758c4e8690341d6e183e6b87 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 15:30:37 +0200 Subject: [PATCH 01/17] Fix formatting with black formatter Signed-off-by: Steffen Vogel --- cimpy/cimexamples.py | 20 +- cimpy/cimexport.py | 379 +++++++++++------- cimpy/cimimport.py | 244 ++++++----- cimpy/examples/addExternalNetworkInjection.py | 20 +- cimpy/examples/convertToBusBranch.py | 22 +- cimpy/examples/exportCIGREMV.py | 19 +- cimpy/examples/importCIGREMV.py | 14 +- cimpy/utils.py | 110 +++-- documentation/conf.py | 34 +- documentation/set_inheritance_diagram.py | 23 +- setup.py | 24 +- tests/create_pickle_dump.py | 18 +- tests/test_export.py | 163 ++++---- tests/test_import.py | 29 +- 14 files changed, 668 insertions(+), 451 deletions(-) diff --git a/cimpy/cimexamples.py b/cimpy/cimexamples.py index 6bcac494..b8fe1980 100644 --- a/cimpy/cimexamples.py +++ b/cimpy/cimexamples.py @@ -2,32 +2,28 @@ def import_example(): - """TODO: Add documentation - """ + """TODO: Add documentation""" base = Path(__file__).resolve().parent - example = base / 'examples' / 'importCIGREMV.py' + example = base / "examples" / "importCIGREMV.py" exec(open(example).read()) def export_example(): - """TODO: Add documentation - """ + """TODO: Add documentation""" base = Path(__file__).resolve().parent - example = base / 'examples' / 'exportCIGREMV.py' + example = base / "examples" / "exportCIGREMV.py" exec(open(example).read()) def convertToBusBranch_example(): - """TODO: Add documentation - """ + """TODO: Add documentation""" base = Path(__file__).resolve().parent - example = base / 'examples' / 'convertToBusBranch.py' + example = base / "examples" / "convertToBusBranch.py" exec(open(example).read()) def addExternalNetworkInjection_example(): - """TODO: Add documentation - """ + """TODO: Add documentation""" base = Path(__file__).resolve().parent - example = base / 'examples' / 'addExternalNetworkInjection.py' + example = base / "examples" / "addExternalNetworkInjection.py" exec(open(example).read()) diff --git a/cimpy/cimexport.py b/cimpy/cimexport.py index ce0802b7..35463b78 100644 --- a/cimpy/cimexport.py +++ b/cimpy/cimexport.py @@ -7,6 +7,7 @@ import logging import sys from cimpy.cgmes_v2_4_15.Base import Base + cgmesProfile = Base.cgmesProfile from pathlib import Path import copy @@ -18,16 +19,18 @@ def _get_class_attributes_with_references(import_result, version): class_attributes_list = [] # extract topology and urls - topology = import_result['topology'] - urls = import_result['meta_info']['urls'] + topology = import_result["topology"] + urls = import_result["meta_info"]["urls"] for key in topology.keys(): class_dict = dict(name=topology[key].__class__.__name__) - class_dict['mRID'] = key + class_dict["mRID"] = key # array containing all attributes, attribute references to objects attributes_dict = _get_attributes(topology[key]) # change attribute references to mRID of the object, res needed because classes like SvPowerFlow does not have # mRID as an attribute. Therefore the corresponding class has to be searched in the res dictionary - class_dict['attributes'] = _get_reference_uuid(attributes_dict, version, topology, key, urls) + class_dict["attributes"] = _get_reference_uuid( + attributes_dict, version, topology, key, urls + ) class_attributes_list.append(class_dict) del class_dict @@ -37,11 +40,11 @@ def _get_class_attributes_with_references(import_result, version): # This function resolves references to objects def _get_reference_uuid(attr_dict, version, topology, mRID, urls): reference_list = [] - base_class_name = 'cimpy.' + version + '.Base' + base_class_name = "cimpy." + version + ".Base" base_module = importlib.import_module(base_class_name) - base_class = getattr(base_module, 'Base') + base_class = getattr(base_module, "Base") for key in attr_dict: - if key in ['serializationProfile', 'possibleProfileList']: + if key in ["serializationProfile", "possibleProfileList"]: reference_list.append({key: attr_dict[key]}) continue @@ -53,57 +56,74 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): # classes like SvVoltage does not have an attribute called mRID, the mRID is only stored as a key # for this object in the res dictionary # The % added before the mRID is used in the lambda _set_attribute_or_reference - if not hasattr(elem, 'mRID'): + if not hasattr(elem, "mRID"): # search for the object in the res dictionary and return the mRID - UUID = '%' + _search_mRID(elem, topology) - if UUID == '%': - logger.warning('Object of type {} not found as reference for object with UUID {}.'.format( - elem.__class__.__name__, mRID)) + UUID = "%" + _search_mRID(elem, topology) + if UUID == "%": + logger.warning( + "Object of type {} not found as reference for object with UUID {}.".format( + elem.__class__.__name__, mRID + ) + ) else: - UUID = '%' + elem.mRID + UUID = "%" + elem.mRID array.append(UUID) else: - logger.warning('Reference object not subclass of Base class for object with UUID {}.'.format(mRID)) + logger.warning( + "Reference object not subclass of Base class for object with UUID {}.".format( + mRID + ) + ) if len(array) == 1: - attributes['value'] = array[0] + attributes["value"] = array[0] else: - attributes['value'] = array + attributes["value"] = array elif issubclass(type(attr_dict[key]), base_class): # 0..1, 1..1 # resource = key + ' rdf:resource=' - if not hasattr(attr_dict[key], 'mRID'): + if not hasattr(attr_dict[key], "mRID"): # search for object in res dict and return mRID # The % added before the mRID is used in the lambda _set_attribute_or_reference - UUID = '%' + _search_mRID(attr_dict[key], topology) - if UUID == '%': - logger.warning('Object of type {} not found as reference for object with UUID {}.'.format( - attr_dict[key].__class__.__name__, mRID)) + UUID = "%" + _search_mRID(attr_dict[key], topology) + if UUID == "%": + logger.warning( + "Object of type {} not found as reference for object with UUID {}.".format( + attr_dict[key].__class__.__name__, mRID + ) + ) else: - UUID = '%' + attr_dict[key].mRID - attributes['value'] = UUID + UUID = "%" + attr_dict[key].mRID + attributes["value"] = UUID elif attr_dict[key] == "" or attr_dict[key] is None: pass else: # attribute in urls dict? - if key.split('.')[1] in urls.keys(): + if key.split(".")[1] in urls.keys(): # value in urls dict? should always be true - if attr_dict[key] in urls[key.split('.')[1]].keys(): - attributes['value'] = '%URL%' + urls[key.split('.')[1]][attr_dict[key]] + if attr_dict[key] in urls[key.split(".")[1]].keys(): + attributes["value"] = ( + "%URL%" + urls[key.split(".")[1]][attr_dict[key]] + ) else: - logger.warning('URL reference for attribute {} and value {} not found!'.format( - key.split('.')[1], attr_dict[key])) + logger.warning( + "URL reference for attribute {} and value {} not found!".format( + key.split(".")[1], attr_dict[key] + ) + ) else: - attributes['value'] = attr_dict[key] + attributes["value"] = attr_dict[key] - attributes['attr_name'] = key - if 'value' in attributes.keys(): - if isinstance(attributes['value'], list): - for reference_item in attributes['value']: + attributes["attr_name"] = key + if "value" in attributes.keys(): + if isinstance(attributes["value"], list): + for reference_item in attributes["value"]: # ignore default values - if reference_item not in ['', None, 0.0, 0]: - reference_list.append({'value': reference_item, 'attr_name': key}) + if reference_item not in ["", None, 0.0, 0]: + reference_list.append( + {"value": reference_item, "attr_name": key} + ) # ignore default values - elif attributes['value'] not in ['', None, 0.0, 0, 'list']: + elif attributes["value"] not in ["", None, 0.0, 0, "list"]: reference_list.append(attributes) return reference_list @@ -121,30 +141,30 @@ def _search_mRID(class_object, topology): # Lambda function for chevron renderer to decide whether the current element is a reference or an attribute def _set_attribute_or_reference(text, render): result = render(text) - result = result.split('@') + result = result.split("@") value = result[0] attr_name = result[1] - if '%URL%' in value: - reference = value.split('%')[2] + if "%URL%" in value: + reference = value.split("%")[2] return ' rdf:resource="' + reference + '"/>' - elif '%' in value: - reference = value.split('%')[1] + elif "%" in value: + reference = value.split("%")[1] return ' rdf:resource="#' + reference + '"/>' else: - return '>' + value + '' + return ">" + value + "" # Lambda function for chevron renderer to set an attribute or a reference in the model description. def _set_attribute_or_reference_model(text, render): result = render(text) - result = result.split('@') + result = result.split("@") value = result[0] attr_name = result[1] - if '%' in value: - reference = value.split('%')[1] + if "%" in value: + reference = value.split("%")[1] return ' rdf:resource="' + reference + '"/>' else: - return '>' + value + '' + return ">" + value + "" # Restructures the namespaces dict into a list. The template engine writes each entry in the RDF header @@ -175,88 +195,133 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): # store serializationProfile and possibleProfileList # serializationProfile class attribute, same for multiple instances of same class, only last origin of variable stored - serializationProfile = copy.deepcopy(klass['attributes'][0]['serializationProfile']) - possibleProfileList = copy.deepcopy(klass['attributes'][1]['possibleProfileList']) + serializationProfile = copy.deepcopy( + klass["attributes"][0]["serializationProfile"] + ) + possibleProfileList = copy.deepcopy( + klass["attributes"][1]["possibleProfileList"] + ) - class_serializationProfile = '' + class_serializationProfile = "" - if 'class' in serializationProfile.keys(): + if "class" in serializationProfile.keys(): # class was imported - if Profile[serializationProfile['class']] in activeProfileList: + if Profile[serializationProfile["class"]] in activeProfileList: # else: class origin profile not active for export, get active profile from possibleProfileList - if Profile[serializationProfile['class']].value in possibleProfileList[klass['name']]['class']: + if ( + Profile[serializationProfile["class"]].value + in possibleProfileList[klass["name"]]["class"] + ): # profile active and in possibleProfileList # else: class should not have been imported from this profile, get allowed profile # from possibleProfileList - class_serializationProfile = serializationProfile['class'] + class_serializationProfile = serializationProfile["class"] else: - logger.warning('Class {} was read from profile {} but this profile is not possible for this class' - .format(klass['name'], serializationProfile['class'])) + logger.warning( + "Class {} was read from profile {} but this profile is not possible for this class".format( + klass["name"], serializationProfile["class"] + ) + ) else: - logger.info('Class {} was read from profile {} but this profile is not active for the export. Use' - 'default profile from possibleProfileList.'.format(klass['name'], serializationProfile['class'])) - - if class_serializationProfile == '': + logger.info( + "Class {} was read from profile {} but this profile is not active for the export. Use" + "default profile from possibleProfileList.".format( + klass["name"], serializationProfile["class"] + ) + ) + + if class_serializationProfile == "": # class was created - if klass['name'] in possibleProfileList.keys(): - if 'class' in possibleProfileList[klass['name']].keys(): - possibleProfileList[klass['name']]['class'].sort() - for klass_profile in possibleProfileList[klass['name']]['class']: + if klass["name"] in possibleProfileList.keys(): + if "class" in possibleProfileList[klass["name"]].keys(): + possibleProfileList[klass["name"]]["class"].sort() + for klass_profile in possibleProfileList[klass["name"]]["class"]: if Profile(klass_profile).name in activeProfileList: # active profile for class export found class_serializationProfile = Profile(klass_profile).name break - if class_serializationProfile == '': + if class_serializationProfile == "": # no profile in possibleProfileList active - logger.warning('All possible export profiles for class {} not active. Skip class for export.' - .format(klass['name'])) + logger.warning( + "All possible export profiles for class {} not active. Skip class for export.".format( + klass["name"] + ) + ) continue else: - logger.warning('Class {} has no profile to export to.'.format(klass['name'])) + logger.warning( + "Class {} has no profile to export to.".format(klass["name"]) + ) else: - logger.warning('Class {} has no profile to export to.'.format(klass['name'])) + logger.warning( + "Class {} has no profile to export to.".format(klass["name"]) + ) # iterate over attributes - for attribute in klass['attributes']: - if 'attr_name' in attribute.keys(): - attribute_class = attribute['attr_name'].split('.')[0] - attribute_name = attribute['attr_name'].split('.')[1] + for attribute in klass["attributes"]: + if "attr_name" in attribute.keys(): + attribute_class = attribute["attr_name"].split(".")[0] + attribute_name = attribute["attr_name"].split(".")[1] # IdentifiedObject.mRID is not exported as an attribute - if attribute_name == 'mRID': + if attribute_name == "mRID": continue - attribute_serializationProfile = '' + attribute_serializationProfile = "" if attribute_name in serializationProfile.keys(): # attribute was imported - if Profile[serializationProfile[attribute_name]] in activeProfileList: + if ( + Profile[serializationProfile[attribute_name]] + in activeProfileList + ): attr_value = Profile[serializationProfile[attribute_name]].value - if attr_value in possibleProfileList[attribute_class][attribute_name]: - attribute_serializationProfile = serializationProfile[attribute_name] - - if attribute_serializationProfile == '': + if ( + attr_value + in possibleProfileList[attribute_class][attribute_name] + ): + attribute_serializationProfile = serializationProfile[ + attribute_name + ] + + if attribute_serializationProfile == "": # attribute was added if attribute_class in possibleProfileList.keys(): - if attribute_name in possibleProfileList[attribute_class].keys(): + if ( + attribute_name + in possibleProfileList[attribute_class].keys() + ): possibleProfileList[attribute_class][attribute_name].sort() - for attr_profile in possibleProfileList[attribute_class][attribute_name]: + for attr_profile in possibleProfileList[attribute_class][ + attribute_name + ]: if Profile(attr_profile) in activeProfileList: # active profile for class export found - attribute_serializationProfile = Profile(attr_profile).name + attribute_serializationProfile = Profile( + attr_profile + ).name break - if attribute_serializationProfile == '': + if attribute_serializationProfile == "": # no profile in possibleProfileList active, skip attribute - logger.warning('All possible export profiles for attribute {}.{} of class {} ' - 'not active. Skip attribute for export.' - .format(attribute_class, attribute_name, klass['name'])) + logger.warning( + "All possible export profiles for attribute {}.{} of class {} " + "not active. Skip attribute for export.".format( + attribute_class, attribute_name, klass["name"] + ) + ) continue else: - logger.warning('Attribute {}.{} of class {} has no profile to export to.'. - format(attribute_class, attribute_name, klass['name'])) + logger.warning( + "Attribute {}.{} of class {} has no profile to export to.".format( + attribute_class, attribute_name, klass["name"] + ) + ) else: - logger.warning('The class {} for attribute {} is not in the possibleProfileList'.format( - attribute_class, attribute_name)) + logger.warning( + "The class {} for attribute {} is not in the possibleProfileList".format( + attribute_class, attribute_name + ) + ) if attribute_serializationProfile == class_serializationProfile: # class and current attribute belong to same profile @@ -271,21 +336,33 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): # add class with all attributes in the same profile to the export dict sorted by the profile if class_serializationProfile in export_dict.keys(): - export_class = dict(name=klass['name'], mRID=klass['mRID'], attributes=same_package_list) - export_dict[class_serializationProfile]['classes'].append(export_class) + export_class = dict( + name=klass["name"], mRID=klass["mRID"], attributes=same_package_list + ) + export_dict[class_serializationProfile]["classes"].append(export_class) del export_class else: - export_class = dict(name=klass['name'], mRID=klass['mRID'], attributes=same_package_list) - export_dict[class_serializationProfile] = {'classes': [export_class]} + export_class = dict( + name=klass["name"], mRID=klass["mRID"], attributes=same_package_list + ) + export_dict[class_serializationProfile] = {"classes": [export_class]} # add class with all attributes defined in another profile to the about_key sorted by the profile for about_key in about_dict.keys(): if about_key in export_about_dict.keys(): - export_about_class = dict(name=klass['name'], mRID=klass['mRID'], attributes=about_dict[about_key]) - export_about_dict[about_key]['classes'].append(export_about_class) + export_about_class = dict( + name=klass["name"], + mRID=klass["mRID"], + attributes=about_dict[about_key], + ) + export_about_dict[about_key]["classes"].append(export_about_class) else: - export_about_class = dict(name=klass['name'], mRID=klass['mRID'], attributes=about_dict[about_key]) - export_about_dict[about_key] = {'classes': [export_about_class]} + export_about_class = dict( + name=klass["name"], + mRID=klass["mRID"], + attributes=about_dict[about_key], + ) + export_about_dict[about_key] = {"classes": [export_about_class]} return export_dict, export_about_dict @@ -312,7 +389,7 @@ def cim_export(import_result, file_name, version, activeProfileList): """ t0 = time() - logger.info('Start export procedure.') + logger.info("Start export procedure.") profile_list = list(map(lambda a: Profile[a], activeProfileList)) @@ -320,23 +397,30 @@ def cim_export(import_result, file_name, version, activeProfileList): for profile in profile_list: # File name - full_file_name = file_name + '_' + profile.long_name() + '.xml' + full_file_name = file_name + "_" + profile.long_name() + ".xml" if not os.path.exists(full_file_name): - output = generate_xml(import_result, version, file_name, profile, profile_list) + output = generate_xml( + import_result, version, file_name, profile, profile_list + ) - with open(full_file_name, 'w') as file: - logger.info('Write file \"%s\"', full_file_name) + with open(full_file_name, "w") as file: + logger.info('Write file "%s"', full_file_name) file.write(output) else: - logger.error('File {} already exists. Delete file or change file name to serialize CGMES ' - 'classes.'.format(full_file_name)) - print('[ERROR:] File {} already exists. Delete file or change file name to serialize CGMES ' - 'classes.'.format(full_file_name), file=sys.stderr) + logger.error( + "File {} already exists. Delete file or change file name to serialize CGMES " + "classes.".format(full_file_name) + ) + print( + "[ERROR:] File {} already exists. Delete file or change file name to serialize CGMES " + "classes.".format(full_file_name), + file=sys.stderr, + ) exit(-1) - logger.info('End export procedure. Elapsed time: {}'.format(time() - t0)) + logger.info("End export procedure. Elapsed time: {}".format(time() - t0)) def generate_xml(cim_data, version, model_name, profile, available_profiles): @@ -352,55 +436,70 @@ def generate_xml(cim_data, version, model_name, profile, available_profiles): """ # returns all classes with their attributes and resolved references - class_attributes_list = _get_class_attributes_with_references( - cim_data, version) + class_attributes_list = _get_class_attributes_with_references(cim_data, version) # determine class and attribute export profiles. The export dict contains all classes and their attributes where # the class definition and the attribute definitions are in the same profile. Every entry in about_dict generates # a rdf:about in another profile export_dict, about_dict = _sort_classes_to_profile( - class_attributes_list, available_profiles) + class_attributes_list, available_profiles + ) - namespaces_list = _create_namespaces_list( - cim_data['meta_info']['namespaces']) + namespaces_list = _create_namespaces_list(cim_data["meta_info"]["namespaces"]) if profile.name not in export_dict.keys() and profile.name not in about_dict.keys(): - raise RuntimeError("Profile " + profile.name + " not available for export, export_dict=" + str(export_dict.keys()) + ' and about_dict='+ str(about_dict.keys()) + '.') + raise RuntimeError( + "Profile " + + profile.name + + " not available for export, export_dict=" + + str(export_dict.keys()) + + " and about_dict=" + + str(about_dict.keys()) + + "." + ) # extract class lists from export_dict and about_dict if profile.name in export_dict.keys(): - classes = export_dict[profile.name]['classes'] + classes = export_dict[profile.name]["classes"] else: classes = False if profile.name in about_dict.keys(): - about = about_dict[profile.name]['classes'] + about = about_dict[profile.name]["classes"] else: about = False - #Model header + # Model header model_description = { - 'mRID': model_name, - 'description': [ - {'attr_name': 'created', 'value': datetime.now().strftime( - "%d/%m/%Y %H:%M:%S")}, - {'attr_name': 'modelingAuthoritySet', - 'value': 'www.sogno.energy'}, - {'attr_name': 'profile', - 'value': profile.long_name()} - ] + "mRID": model_name, + "description": [ + { + "attr_name": "created", + "value": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + }, + {"attr_name": "modelingAuthoritySet", "value": "www.sogno.energy"}, + {"attr_name": "profile", "value": profile.long_name()}, + ], } - template_path = Path(os.path.join(os.path.dirname(__file__), 'export_template.mustache')).resolve() + template_path = Path( + os.path.join(os.path.dirname(__file__), "export_template.mustache") + ).resolve() with open(template_path) as f: - output = chevron.render(f, {"classes": classes, - "about": about, - "set_attributes_or_reference": _set_attribute_or_reference, - "set_attributes_or_reference_model": _set_attribute_or_reference_model, - "namespaces": namespaces_list, - "model": [model_description]}) + output = chevron.render( + f, + { + "classes": classes, + "about": about, + "set_attributes_or_reference": _set_attribute_or_reference, + "set_attributes_or_reference_model": _set_attribute_or_reference_model, + "namespaces": namespaces_list, + "model": [model_description], + }, + ) del model_description return output + # This function extracts all attributes from class_object in the form of Class_Name.Attribute_Name def _get_attributes(class_object): inheritance_list = [class_object] @@ -408,14 +507,16 @@ def _get_attributes(class_object): parent = class_object # get parent classes - while 'Base.Base' not in str(class_type): + while "Base.Base" not in str(class_type): parent = parent.__class__.__bases__[0]() # insert parent class at beginning of list, classes inherit from top to bottom inheritance_list.insert(0, parent) class_type = type(parent) # dictionary containing all attributes with key: 'Class_Name.Attribute_Name' - attributes_dict = dict(serializationProfile=class_object.serializationProfile, possibleProfileList={}) + attributes_dict = dict( + serializationProfile=class_object.serializationProfile, possibleProfileList={} + ) # __dict__ of a subclass returns also the attributes of the parent classes # to avoid multiple attributes create list with all attributes already processed @@ -431,7 +532,7 @@ def _get_attributes(class_object): for key in parent_attributes_dict.keys(): if key not in attributes_list: attributes_list.append(key) - attributes_name = class_name + '.' + key + attributes_name = class_name + "." + key attributes_dict[attributes_name] = getattr(class_object, key) else: continue @@ -439,7 +540,9 @@ def _get_attributes(class_object): # get all possibleProfileLists from all parent classes except the Base class (no attributes) # the serializationProfile from parent classes is not needed because entries in the serializationProfile # are only generated for the inherited class - if class_name != 'Base': - attributes_dict['possibleProfileList'][class_name] = parent_class.possibleProfileList + if class_name != "Base": + attributes_dict["possibleProfileList"][ + class_name + ] = parent_class.possibleProfileList return attributes_dict diff --git a/cimpy/cimimport.py b/cimpy/cimimport.py index 68efc8a3..67b24b2a 100644 --- a/cimpy/cimimport.py +++ b/cimpy/cimimport.py @@ -41,37 +41,55 @@ def cim_import(xml_files, cgmes_version, start_dict=None): logger_grouped = dict(errors={}, info={}) # create a dict which will contain meta information and the topology - import_result = start_dict if start_dict is not None else dict(meta_info={}, topology={}) + import_result = ( + start_dict if start_dict is not None else dict(meta_info={}, topology={}) + ) # create sub-dictionaries - import_result['meta_info'] = dict(namespaces=_get_namespaces(xml_files[0]), urls={}) - namespace_rdf = _get_rdf_namespace(import_result['meta_info']['namespaces']) + import_result["meta_info"] = dict(namespaces=_get_namespaces(xml_files[0]), urls={}) + namespace_rdf = _get_rdf_namespace(import_result["meta_info"]["namespaces"]) # CIM element tag base (e.g. {http://iec.ch/TC57/2012/CIM-schema-cim16#} ) - base = "{" + import_result['meta_info']['namespaces']["cim"] + "}" - - import_result, logger_grouped, = _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace_rdf, - base, logger_grouped) - - import_result, logger_grouped = _set_attributes(import_result, xml_files, namespace_rdf, base, logger_grouped) - - if logger_grouped['errors']: - for error, count in logger_grouped['errors'].items(): - logging_message = '{} : {} times'.format(error, count) + base = "{" + import_result["meta_info"]["namespaces"]["cim"] + "}" + + (import_result, logger_grouped,) = _instantiate_classes( + import_result, + xml_files, + cgmes_version_path, + namespace_rdf, + base, + logger_grouped, + ) + + import_result, logger_grouped = _set_attributes( + import_result, xml_files, namespace_rdf, base, logger_grouped + ) + + if logger_grouped["errors"]: + for error, count in logger_grouped["errors"].items(): + logging_message = "{} : {} times".format(error, count) logger.warning(logging_message) - if logger_grouped['info']: - for info, count in logger_grouped['info'].items(): - logging_message = '{} : {} times'.format(info, count) + if logger_grouped["info"]: + for info, count in logger_grouped["info"].items(): + logging_message = "{} : {} times".format(info, count) logger.info(logging_message) # print info which classes and how many were instantiated print(logging_message) elapsed_time = time() - t0 - logger.info('Created totally {} CIM objects in {}s\n\n'.format(len(import_result['topology']), elapsed_time)) + logger.info( + "Created totally {} CIM objects in {}s\n\n".format( + len(import_result["topology"]), elapsed_time + ) + ) # print info of how many classes in total were instantiated to terminal - print('Created totally {} CIM objects in {}s'.format(len(import_result['topology']), elapsed_time)) + print( + "Created totally {} CIM objects in {}s".format( + len(import_result["topology"]), elapsed_time + ) + ) return import_result @@ -81,18 +99,19 @@ def cim_import(xml_files, cgmes_version, start_dict=None): # are set in the _set_attributes function because some attributes might be stored in one package and the class in # another. Since after this function all classes are instantiated, there should be no problem in setting the attributes. # Also the information from which package file a class was read is stored in the serializationProfile dictionary. -def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace_rdf, base, - logger_grouped): +def _instantiate_classes( + import_result, xml_files, cgmes_version_path, namespace_rdf, base, logger_grouped +): # extract topology from import_result - topology = import_result['topology'] + topology = import_result["topology"] # length of element tag base m = len(base) # first step: create the dict res{uuid}=instance_of_the_cim_class for xml_file in xml_files: - logger.info('START of parsing file \"%s\"', xml_file) + logger.info('START of parsing file "%s"', xml_file) # Reset stream if hasattr(xml_file, "seek"): @@ -107,7 +126,7 @@ def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace # Get the root element ({http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF). _, root = next(context) - package = '' + package = "" for event, elem in context: @@ -122,14 +141,14 @@ def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace try: # module_name = package_map[package][tag] # Import the module for the CGMES object. - module_name = cgmes_version_path + '.' + tag + module_name = cgmes_version_path + "." + tag module = importlib.import_module(module_name) except ModuleNotFoundError: - error_msg = 'Module {} not implemented'.format(tag) + error_msg = "Module {} not implemented".format(tag) try: - logger_grouped['errors'][error_msg] += 1 + logger_grouped["errors"][error_msg] += 1 except KeyError: - logger_grouped['errors'][error_msg] = 1 + logger_grouped["errors"][error_msg] = 1 root.clear() continue @@ -139,43 +158,47 @@ def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace # Instantiate the class and map it to the uuid. # res[uuid] = klass(UUID=uuid) topology[uuid] = klass() - info_msg = 'CIM object {} created'.format(module_name.split('.')[-1]) + info_msg = "CIM object {} created".format( + module_name.split(".")[-1] + ) try: - logger_grouped['info'][info_msg] += 1 + logger_grouped["info"][info_msg] += 1 except KeyError: - logger_grouped['info'][info_msg] = 1 + logger_grouped["info"][info_msg] = 1 # check if the class has the attribute mRID and set the mRID to the read in UUID. If the class # does not has this attribute, the UUID is only stored in the res dictionary. - if hasattr(topology[uuid], 'mRID'): + if hasattr(topology[uuid], "mRID"): topology[uuid].mRID = uuid - if package != '': - topology[uuid].serializationProfile['class'] = short_package_name[package] + if package != "": + topology[uuid].serializationProfile[ + "class" + ] = short_package_name[package] else: - error_msg = 'Package information not found for class {}'.format( + error_msg = "Package information not found for class {}".format( klass.__class__.__name__ ) try: - logger_grouped['errors'][error_msg] += 1 + logger_grouped["errors"][error_msg] += 1 except KeyError: - logger_grouped['errors'][error_msg] = 1 + logger_grouped["errors"][error_msg] = 1 # Check which package is read elif event == "end": - if 'Model.profile' in elem.tag: + if "Model.profile" in elem.tag: for package_key in short_package_name.keys(): if package_key in elem.text: package = package_key break # the author of all imported files should be the same, avoid multiple entries - elif 'author' in import_result['meta_info'].keys(): + elif "author" in import_result["meta_info"].keys(): pass # extract author - elif 'Model.createdBy' in elem.tag: - import_result['meta_info']['author'] = elem.text - elif 'Model.modelingAuthoritySet' in elem.tag: - import_result['meta_info']['author'] = elem.text + elif "Model.createdBy" in elem.tag: + import_result["meta_info"]["author"] = elem.text + elif "Model.modelingAuthoritySet" in elem.tag: + import_result["meta_info"]["author"] = elem.text # Clear children of the root element to minimise memory usage. root.clear() @@ -188,8 +211,8 @@ def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace # the attributes are read in the serializationProfile dictionary. def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_grouped): - topology = import_result['topology'] - urls = import_result['meta_info']['urls'] + topology = import_result["topology"] + urls = import_result["meta_info"]["urls"] m = len(base) @@ -206,7 +229,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Get the root element ({http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF). _, root = next(context) - package = '' + package = "" for event, elem in context: @@ -222,11 +245,13 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe try: obj = topology[uuid] except KeyError: - error_msg = 'Missing {} object with uuid: {}'.format(elem.tag[m:], uuid) + error_msg = "Missing {} object with uuid: {}".format( + elem.tag[m:], uuid + ) try: - logger_grouped['errors'][error_msg] += 1 + logger_grouped["errors"][error_msg] += 1 except KeyError: - logger_grouped['errors'][error_msg] = 1 + logger_grouped["errors"][error_msg] = 1 root.clear() continue @@ -235,17 +260,22 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Process end events with elements in the CIM namespace. if event == "end" and elem.tag[:m] == base: # Break if class closing element (e.g. ). - if elem.get("{%s}ID" % namespace_rdf) is None \ - and elem.get("{%s}about" % namespace_rdf) is None: + if ( + elem.get("{%s}ID" % namespace_rdf) is None + and elem.get("{%s}about" % namespace_rdf) is None + ): # Get the attribute/reference name. attr = elem.tag[m:].rsplit(".")[-1] if not hasattr(obj, attr): - error_msg = "'%s' has not attribute '%s'" % (obj.__class__.__name__, attr) + error_msg = "'%s' has not attribute '%s'" % ( + obj.__class__.__name__, + attr, + ) try: - logger_grouped['errors'][error_msg] += 1 + logger_grouped["errors"][error_msg] += 1 except KeyError: - logger_grouped['errors'][error_msg] = 1 + logger_grouped["errors"][error_msg] = 1 continue # Use the rdf:resource attribute to distinguish between attributes and references/enums. @@ -255,12 +285,14 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Convert value type using the default value. try: typ = type(getattr(obj, attr)) - if isinstance(getattr(obj, attr), bool): # if typ== + if isinstance( + getattr(obj, attr), bool + ): # if typ== # The function bool("false") returns True, # because it is called upon non-empty string! # This means that it wrongly reads "false" value as boolean True. # This is why this special case testing is necessary. - if str.title(elem.text) == 'True': + if str.title(elem.text) == "True": setattr(obj, attr, True) else: setattr(obj, attr, False) @@ -276,14 +308,17 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Use the '#' prefix to distinguish between references and enumerations. if uuid2[0] == "#": # reference try: - val = topology[uuid2[1:]] # remove '#' prefix + val = topology[ + uuid2[1:] + ] # remove '#' prefix except KeyError: - error_msg = 'Referenced {} [{}] object missing.'.format( - obj.__class__.__name__, uuid2[1:]) + error_msg = "Referenced {} [{}] object missing.".format( + obj.__class__.__name__, uuid2[1:] + ) try: - logger_grouped['errors'][error_msg] += 1 + logger_grouped["errors"][error_msg] += 1 except KeyError: - logger_grouped['errors'][error_msg] = 1 + logger_grouped["errors"][error_msg] = 1 continue @@ -291,7 +326,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe if default is None: # 1..1 or 0..1 # Rely on properties to set any bi-directional references. setattr(obj, attr, val) - elif default == 'list': # many + elif default == "list": # many setattr(obj, attr, [val]) elif isinstance(default, list): # many attribute = getattr(obj, attr) @@ -303,63 +338,92 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe pass else: # note here - error_msg = 'Multiplicity Error for class {} [{}], attribute {}. Multiplicity should be 1..1 or 0..1'.format( - obj.__class__.__name__, uuid, attr) + error_msg = "Multiplicity Error for class {} [{}], attribute {}. Multiplicity should be 1..1 or 0..1".format( + obj.__class__.__name__, uuid, attr + ) try: - logger_grouped['errors'][error_msg] += 1 + logger_grouped["errors"][error_msg] += 1 except KeyError: - logger_grouped['errors'][error_msg] = 1 + logger_grouped["errors"][error_msg] = 1 if hasattr(val, obj.__class__.__name__): - default1 = getattr(val, obj.__class__.__name__) + default1 = getattr( + val, obj.__class__.__name__ + ) if default1 is None: - setattr(val, obj.__class__.__name__, obj) - elif default1 == 'list': # many - setattr(val, obj.__class__.__name__, [obj]) + setattr( + val, obj.__class__.__name__, obj + ) + elif default1 == "list": # many + setattr( + val, obj.__class__.__name__, [obj] + ) elif isinstance(default1, list): # many - attribute2 = getattr(val, obj.__class__.__name__) + attribute2 = getattr( + val, obj.__class__.__name__ + ) if obj not in attribute2: attribute2.append(obj) - setattr(val, obj.__class__.__name__, attribute2) + setattr( + val, + obj.__class__.__name__, + attribute2, + ) elif default1 == obj: pass else: - error_msg = 'Multiplicity Error for class {} [{}], attribute {}. Multiplicity should be 1..1 or 0..1'.format( - val.__class__.__name__, uuid2[1:], obj.__class__.__name__) + error_msg = "Multiplicity Error for class {} [{}], attribute {}. Multiplicity should be 1..1 or 0..1".format( + val.__class__.__name__, + uuid2[1:], + obj.__class__.__name__, + ) try: - logger_grouped['errors'][error_msg] += 1 + logger_grouped["errors"][ + error_msg + ] += 1 except KeyError: - logger_grouped['errors'][error_msg] = 1 + logger_grouped["errors"][ + error_msg + ] = 1 else: # enum # if http in uuid2 reference to URL, create mapping - if 'http' in uuid2: + if "http" in uuid2: if attr in urls.keys(): - if uuid2.rsplit(".", 1)[1] not in urls[attr].keys(): - urls[attr][uuid2.rsplit(".", 1)[1]] = uuid2 + if ( + uuid2.rsplit(".", 1)[1] + not in urls[attr].keys() + ): + urls[attr][ + uuid2.rsplit(".", 1)[1] + ] = uuid2 else: - urls[attr] = {uuid2.rsplit(".", 1)[1]: uuid2} + urls[attr] = { + uuid2.rsplit(".", 1)[1]: uuid2 + } # url_reference_dict[uuid2.rsplit(".", 1)[1]] = uuid2 val = uuid2.rsplit(".", 1)[1] setattr(obj, attr, val) - if package != '': - obj.serializationProfile[attr] = short_package_name[package] + if package != "": + obj.serializationProfile[attr] = short_package_name[ + package + ] else: - error_msg = 'Package information not found for class {}, attribute {}'.format( + error_msg = "Package information not found for class {}, attribute {}".format( obj.__class__.__name__, attr ) try: - logger_grouped['errors'][error_msg] += 1 + logger_grouped["errors"][error_msg] += 1 except KeyError: - logger_grouped['errors'][error_msg] = 1 + logger_grouped["errors"][error_msg] = 1 else: # if elem.get("{%s}ID" % nd_rdf is not None: # Finished setting object attributes. break # Check which package is read - elif event == "end" and 'Model.profile' in elem.tag: + elif event == "end" and "Model.profile" in elem.tag: for package_key in short_package_name.keys(): if package_key in elem.text: package = package_key @@ -376,7 +440,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe def _get_namespaces(source): namespaces = {} events = ("end", "start-ns", "end-ns") - for (event, elem) in etree.iterparse(source, events): + for event, elem in etree.iterparse(source, events): if event == "start-ns": prefix, ns = elem namespaces[prefix] = ns @@ -393,10 +457,10 @@ def _get_namespaces(source): # Returns the RDF Namespace from the namespaces dictionary def _get_rdf_namespace(namespaces): try: - namespace = namespaces['rdf'] + namespace = namespaces["rdf"] except KeyError: ns = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" - logger.warning('No rdf namespace found. Using %s' % ns) + logger.warning("No rdf namespace found. Using %s" % ns) return namespace @@ -404,11 +468,11 @@ def _get_rdf_namespace(namespaces): # TODO: use cimpy.cgmes.Profile instead # used to map the profile name to their abbreviations according to the CGMES standard short_package_name = { - "DiagramLayout": 'DL', + "DiagramLayout": "DL", "Dynamics": "DY", "Equipment": "EQ", "GeographicalLocation": "GL", "StateVariables": "SV", "SteadyStateHypothesis": "SSH", - "Topology": "TP" + "Topology": "TP", } diff --git a/cimpy/examples/addExternalNetworkInjection.py b/cimpy/examples/addExternalNetworkInjection.py index d5e12938..45fe8bf7 100644 --- a/cimpy/examples/addExternalNetworkInjection.py +++ b/cimpy/examples/addExternalNetworkInjection.py @@ -2,26 +2,28 @@ import cimpy from pathlib import Path -logging.basicConfig(filename='importCIGREMV.log', level=logging.INFO, filemode='w') +logging.basicConfig(filename="importCIGREMV.log", level=logging.INFO, filemode="w") example = Path(__file__).resolve().parent # called as cimpy.examples.addExternalNetworkInjection() or file run from quickstart directory? -if 'cimexamples.py' in str(__file__): - sample_folder = example / 'examples' / 'sampledata' / 'CIGRE_MV' +if "cimexamples.py" in str(__file__): + sample_folder = example / "examples" / "sampledata" / "CIGRE_MV" else: - sample_folder = example / 'sampledata' / 'CIGRE_MV' + sample_folder = example / "sampledata" / "CIGRE_MV" -sample_files = sample_folder.glob('*.xml') +sample_files = sample_folder.glob("*.xml") xml_files = [] -for file in sample_folder.glob('*.xml'): +for file in sample_folder.glob("*.xml"): xml_files.append(str(file.absolute())) import_result = cimpy.cim_import(xml_files, "cgmes_v2_4_15") -import_result = cimpy.utils.add_external_network_injection(import_result, "cgmes_v2_4_15", "N1", 20.0) +import_result = cimpy.utils.add_external_network_injection( + import_result, "cgmes_v2_4_15", "N1", 20.0 +) -activeProfileList = ['DL', 'EQ', 'SV', 'TP'] +activeProfileList = ["DL", "EQ", "SV", "TP"] -cimpy.cim_export(import_result, 'ExternalInjection', 'cgmes_v2_4_15', activeProfileList) +cimpy.cim_export(import_result, "ExternalInjection", "cgmes_v2_4_15", activeProfileList) diff --git a/cimpy/examples/convertToBusBranch.py b/cimpy/examples/convertToBusBranch.py index 46129fe1..e4eecf31 100644 --- a/cimpy/examples/convertToBusBranch.py +++ b/cimpy/examples/convertToBusBranch.py @@ -2,20 +2,24 @@ import cimpy from pathlib import Path -logging.basicConfig(filename='Convert_to_Bus_Branch.log', level=logging.INFO, filemode='w') +logging.basicConfig( + filename="Convert_to_Bus_Branch.log", level=logging.INFO, filemode="w" +) example = Path(__file__).resolve().parent # called as cimpy.examples.convertBusBranch() or file run from quickstart directory? -if 'cimexamples.py' in str(__file__): - sample_folder = example / 'examples' / 'sampledata' / 'Sample_Grid_Switches' / 'Node-Breaker' +if "cimexamples.py" in str(__file__): + sample_folder = ( + example / "examples" / "sampledata" / "Sample_Grid_Switches" / "Node-Breaker" + ) else: - sample_folder = example / 'sampledata' / 'Sample_Grid_Switches' / 'Node-Breaker' + sample_folder = example / "sampledata" / "Sample_Grid_Switches" / "Node-Breaker" -sample_files = sample_folder.glob('*.xml') +sample_files = sample_folder.glob("*.xml") xml_files = [] -for file in sample_folder.glob('*.xml'): +for file in sample_folder.glob("*.xml"): xml_files.append(str(file.absolute())) @@ -23,6 +27,8 @@ import_result = cimpy.utils.node_breaker_to_bus_branch(import_result) -activeProfileList = ['DL', 'EQ', 'TP'] +activeProfileList = ["DL", "EQ", "TP"] -cimpy.cim_export(import_result, 'Bus_Branch_Converted', 'cgmes_v2_4_15', activeProfileList) +cimpy.cim_export( + import_result, "Bus_Branch_Converted", "cgmes_v2_4_15", activeProfileList +) diff --git a/cimpy/examples/exportCIGREMV.py b/cimpy/examples/exportCIGREMV.py index f3fbb0ca..777e4e1f 100644 --- a/cimpy/examples/exportCIGREMV.py +++ b/cimpy/examples/exportCIGREMV.py @@ -1,24 +1,27 @@ import logging from pathlib import Path import cimpy -logging.basicConfig(filename='exportCIGREMV.log', level=logging.INFO, filemode='w') + +logging.basicConfig(filename="exportCIGREMV.log", level=logging.INFO, filemode="w") example = Path(__file__).resolve().parent # called as cimpy.examples.import_example() or file run from quickstart directory? -if 'cimexamples.py' in str(__file__): - sample_folder = example / 'examples' / 'sampledata' / 'CIGRE_MV' +if "cimexamples.py" in str(__file__): + sample_folder = example / "examples" / "sampledata" / "CIGRE_MV" else: - sample_folder = example / 'sampledata' / 'CIGRE_MV' + sample_folder = example / "sampledata" / "CIGRE_MV" -sample_files = sample_folder.glob('*.xml') +sample_files = sample_folder.glob("*.xml") xml_files = [] -for file in sample_folder.glob('*.xml'): +for file in sample_folder.glob("*.xml"): xml_files.append(str(file.absolute())) import_result = cimpy.cim_import(xml_files, "cgmes_v2_4_15") -activeProfileList = ['DL', 'EQ', 'SV', 'TP'] +activeProfileList = ["DL", "EQ", "SV", "TP"] -cimpy.cim_export(import_result, 'CIGREMV_reference_cgmes_v2_4_15', 'cgmes_v2_4_15', activeProfileList) +cimpy.cim_export( + import_result, "CIGREMV_reference_cgmes_v2_4_15", "cgmes_v2_4_15", activeProfileList +) diff --git a/cimpy/examples/importCIGREMV.py b/cimpy/examples/importCIGREMV.py index 03f2e6a9..2898dfaa 100644 --- a/cimpy/examples/importCIGREMV.py +++ b/cimpy/examples/importCIGREMV.py @@ -2,25 +2,25 @@ import cimpy from pathlib import Path -logging.basicConfig(filename='importCIGREMV.log', level=logging.INFO, filemode='w') +logging.basicConfig(filename="importCIGREMV.log", level=logging.INFO, filemode="w") example = Path(__file__).resolve().parent # called as cimpy.examples.import_example() or file run from quickstart directory? -if 'cimexamples.py' in str(__file__): - sample_folder = example / 'examples' / 'sampledata' / 'CIGRE_MV' +if "cimexamples.py" in str(__file__): + sample_folder = example / "examples" / "sampledata" / "CIGRE_MV" else: - sample_folder = example / 'sampledata' / 'CIGRE_MV' + sample_folder = example / "sampledata" / "CIGRE_MV" print(sample_folder) -sample_files = sample_folder.glob('*.xml') +sample_files = sample_folder.glob("*.xml") print(sample_files) xml_files = [] -for file in sample_folder.glob('*.xml'): +for file in sample_folder.glob("*.xml"): xml_files.append(str(file.absolute())) import_result = cimpy.cim_import(xml_files, "cgmes_v2_4_15") print("\n\n") results = ["ACLineSegment", "PowerTransformer", "EnergyConsumer"] -for key, value in import_result['topology'].items(): +for key, value in import_result["topology"].items(): if value.__class__.__name__ in results: print(value.__str__()) diff --git a/cimpy/utils.py b/cimpy/utils.py index e9cfea3b..38cfb82e 100644 --- a/cimpy/utils.py +++ b/cimpy/utils.py @@ -1,9 +1,9 @@ import importlib + def node_breaker_to_bus_branch(import_result): - """TODO: Add documentation - """ - res = import_result['topology'] + """TODO: Add documentation""" + res = import_result["topology"] breaker_list = [] terminals_list = [] operational_limit_set_list = [] @@ -25,7 +25,7 @@ def node_breaker_to_bus_branch(import_result): diagram_objects_list.append(mRID) elif class_name == "DiagramObjectPoint": diagram_object_points_list.append(mRID) - elif class_name == 'ConnectivityNode': + elif class_name == "ConnectivityNode": connect_nodes.append(mRID) # search for open breakers @@ -61,10 +61,14 @@ def node_breaker_to_bus_branch(import_result): keep_diagram = [] if res[diagram_object].IdentifiedObject is None: continue - if 'ConnectivityNode' in str(type(res[diagram_object].IdentifiedObject)): + if "ConnectivityNode" in str(type(res[diagram_object].IdentifiedObject)): if res[diagram_object].IdentifiedObject.TopologicalNode is not None: - keep_diagram.append(res[diagram_object].IdentifiedObject.TopologicalNode) - elif res[diagram_object].IdentifiedObject.mRID in (open_breakers + del_terminals_list): + keep_diagram.append( + res[diagram_object].IdentifiedObject.TopologicalNode + ) + elif res[diagram_object].IdentifiedObject.mRID in ( + open_breakers + del_terminals_list + ): del_diagram_object.append(diagram_object) del_diagram_object_points = [] @@ -74,65 +78,83 @@ def node_breaker_to_bus_branch(import_result): if res[diagram_point].DiagramObject.mRID in del_diagram_object: del_diagram_object_points.append(diagram_point) - del_list = open_breakers + del_diagram_object_points + del_diagram_object + del_voltage_limit + \ - del_operationallimitset + del_terminals_list + connect_nodes + del_list = ( + open_breakers + + del_diagram_object_points + + del_diagram_object + + del_voltage_limit + + del_operationallimitset + + del_terminals_list + + connect_nodes + ) for key in del_list: del res[key] - import_result['topology'] = res + import_result["topology"] = res return import_result def add_external_network_injection(import_result, version, mRID, voltage_set_point): - """TODO: Add documentation - """ - res = import_result['topology'] - TopologicalNode = '' + """TODO: Add documentation""" + res = import_result["topology"] + TopologicalNode = "" if mRID in res: - if 'TopologicalNode' in str(type(res[mRID])): + if "TopologicalNode" in str(type(res[mRID])): TopologicalNode = res[mRID] - elif 'ConnectivityNode' in str(type(res[mRID])): + elif "ConnectivityNode" in str(type(res[mRID])): TopologicalNode = res[mRID].TopologicalNode.mRID - if TopologicalNode != '': + if TopologicalNode != "": i = 1 - while 'Injection ' + str(i) in res.keys(): + while "Injection " + str(i) in res.keys(): i += 1 - inj_name = 'Injection ' + str(i) - reg_name = 'Regulating Control ' + str(i) - terminal_name = 'Terminal Injection ' + str(i) + inj_name = "Injection " + str(i) + reg_name = "Regulating Control " + str(i) + terminal_name = "Terminal Injection " + str(i) - #module_name = "cimpy." + version + ".Equipment." + # module_name = "cimpy." + version + ".Equipment." module_name = "cimpy." + version + "." - - terminal_module = importlib.import_module((module_name + 'Terminal')) - terminal_class = getattr(terminal_module, 'Terminal') - res[terminal_name] = terminal_class(mRID=terminal_name, - name=terminal_name, - TopologicalNode=TopologicalNode) - - regulating_control_module = importlib.import_module(module_name + 'RegulatingControl') - regulating_control_class = getattr(regulating_control_module, 'RegulatingControl') - res[reg_name] = regulating_control_class(mRID=reg_name, - name=reg_name, - targetValue=voltage_set_point, - Terminal=res[terminal_name]) - - network_injection_module = importlib.import_module(module_name + 'ExternalNetworkInjection') - network_injection_class = getattr(network_injection_module, 'ExternalNetworkInjection') - res[inj_name] = network_injection_class(mRID=inj_name, - name=inj_name, - RegulatingControl=res[reg_name], - Terminals=[res[terminal_name]]) + + terminal_module = importlib.import_module((module_name + "Terminal")) + terminal_class = getattr(terminal_module, "Terminal") + res[terminal_name] = terminal_class( + mRID=terminal_name, name=terminal_name, TopologicalNode=TopologicalNode + ) + + regulating_control_module = importlib.import_module( + module_name + "RegulatingControl" + ) + regulating_control_class = getattr( + regulating_control_module, "RegulatingControl" + ) + res[reg_name] = regulating_control_class( + mRID=reg_name, + name=reg_name, + targetValue=voltage_set_point, + Terminal=res[terminal_name], + ) + + network_injection_module = importlib.import_module( + module_name + "ExternalNetworkInjection" + ) + network_injection_class = getattr( + network_injection_module, "ExternalNetworkInjection" + ) + res[inj_name] = network_injection_class( + mRID=inj_name, + name=inj_name, + RegulatingControl=res[reg_name], + Terminals=[res[terminal_name]], + ) res[reg_name].RegulatingCondEq = res[inj_name] res[terminal_name].ConductingEquipment = res[inj_name] res[terminal_name].RegulatingControl = res[reg_name] else: - print('No Terminal with mRID ', mRID, ' found in object list!') + print("No Terminal with mRID ", mRID, " found in object list!") - import_result['topology'] = res + import_result["topology"] = res return import_result diff --git a/documentation/conf.py b/documentation/conf.py index 6fa2a30b..6b4c9671 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -12,14 +12,15 @@ # import os import sys -sys.path.insert(0, os.path.abspath('../')) + +sys.path.insert(0, os.path.abspath("../")) # -- Project information ----------------------------------------------------- -project = 'cimpy' -copyright = '2019, ACS RWTH Aachen University' -author = 'Author' +project = "cimpy" +copyright = "2019, ACS RWTH Aachen University" +author = "Author" # -- General configuration --------------------------------------------------- @@ -28,33 +29,32 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.viewcode', - 'sphinx.ext.todo', - 'sphinx_rtd_theme', - 'sphinx.ext.inheritance_diagram', - 'sphinx.ext.graphviz' - + "sphinx.ext.autodoc", + "sphinx.ext.viewcode", + "sphinx.ext.todo", + "sphinx_rtd_theme", + "sphinx.ext.inheritance_diagram", + "sphinx.ext.graphviz", ] -modindex_common_prefix = ['cimpy.'] +modindex_common_prefix = ["cimpy."] inheritance_graph_attrs = dict(rankdir="TB", size='""') # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = 'en' +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # -- Options for HTML output ------------------------------------------------- @@ -62,12 +62,12 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # -- Extension configuration ------------------------------------------------- diff --git a/documentation/set_inheritance_diagram.py b/documentation/set_inheritance_diagram.py index a17aa35e..dd4d8be5 100644 --- a/documentation/set_inheritance_diagram.py +++ b/documentation/set_inheritance_diagram.py @@ -3,7 +3,7 @@ from tempfile import mkstemp from shutil import move, copy -directory = os.path.abspath(os.path.join('..', 'documentation')) +directory = os.path.abspath(os.path.join("..", "documentation")) # if 'conf.py' in os.listdir(directory): # conf_file = os.path.abspath(os.path.join(directory, 'conf.py')) @@ -23,21 +23,21 @@ file_path = os.path.abspath(file) fh, abs_path = mkstemp() found_inheritance = False - with open(fh, 'w') as new_file: + with open(fh, "w") as new_file: with open(os.path.join(directory, file)) as old_file: for line in old_file: - if 'undoc-members' in line: + if "undoc-members" in line: continue else: - if 'automodule' in line: - name = line.split('::')[1] - if 'show-inheritance' in line: - new_file.write('\nInheritance Diagram:\n') + if "automodule" in line: + name = line.split("::")[1] + if "show-inheritance" in line: + new_file.write("\nInheritance Diagram:\n") new_file.write('""""""""""""""""""""\n') - new_file.write('.. inheritance-diagram:: ' + name) - new_file.write(' :parts: 1\n') - new_file.write('') - new_file.write('') + new_file.write(".. inheritance-diagram:: " + name) + new_file.write(" :parts: 1\n") + new_file.write("") + new_file.write("") found_inheritance = True else: new_file.write(line) @@ -45,4 +45,3 @@ if found_inheritance: remove(os.path.join(directory, file)) move(abs_path, os.path.join(directory, file)) - diff --git a/setup.py b/setup.py index 8ff4a9da..4ac7ea4e 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,16 @@ import setuptools setuptools.setup( - name = 'cimpy', - version = '1.0.2', - description = 'Python package for import, modification and export of CIM grid data', - author = 'Institute for Automation of Complex Power Systems', - include_package_data=True, - license = 'MPL-2.0', - packages = setuptools.find_packages(), - install_requires = [ - 'lxml', - 'xmltodict', - 'chevron', - ] + name="cimpy", + version="1.0.2", + description="Python package for import, modification and export of CIM grid data", + author="Institute for Automation of Complex Power Systems", + include_package_data=True, + license="MPL-2.0", + packages=setuptools.find_packages(), + install_requires=[ + "lxml", + "xmltodict", + "chevron", + ], ) diff --git a/tests/create_pickle_dump.py b/tests/create_pickle_dump.py index f08c97a5..04253735 100644 --- a/tests/create_pickle_dump.py +++ b/tests/create_pickle_dump.py @@ -1,25 +1,29 @@ - import pickle import sys + sys.path.append("/home/richard/cimpy") from pathlib import Path import cimpy -tests = Path('.').resolve().parent -example_path = tests / 'examples' / 'sampledata' / 'CIGRE_MV' +tests = Path(".").resolve().parent +example_path = tests / "examples" / "sampledata" / "CIGRE_MV" + def create_pickle(): test_files = [] - for file in example_path.glob('*.xml'): + for file in example_path.glob("*.xml"): print("file: ", file) test_files.append(str(file.absolute())) - imported_result = cimpy.cim_import(test_files, 'cgmes_v2_4_15') + imported_result = cimpy.cim_import(test_files, "cgmes_v2_4_15") + + CGMES_object = cimpy.cimexport._get_class_attributes_with_references( + imported_result, "cgmes_v2_4_15" + ) - CGMES_object = cimpy.cimexport._get_class_attributes_with_references(imported_result, 'cgmes_v2_4_15') + pickle.dump(CGMES_object, open("CIGREMV_import_reference_cgmes_v2_4_15.p1", "wb")) - pickle.dump( CGMES_object, open( 'CIGREMV_import_reference_cgmes_v2_4_15.p1', "wb" ) ) create_pickle() diff --git a/tests/test_export.py b/tests/test_export.py index bc21939b..b3485fab 100644 --- a/tests/test_export.py +++ b/tests/test_export.py @@ -8,38 +8,39 @@ from pathlib import Path import pytest -logging.basicConfig(filename='Test_export_with_exported_files.log', - level=logging.INFO, filemode='w') +logging.basicConfig( + filename="Test_export_with_exported_files.log", level=logging.INFO, filemode="w" +) @pytest.fixture def sample_cimdata(): - """ Import the sampledata using cimpy - """ - example_dir = Path(os.path.join(os.path.dirname( - __file__), '../cimpy/examples/sampledata/CIGRE_MV')).resolve() + """Import the sampledata using cimpy""" + example_dir = Path( + os.path.join(os.path.dirname(__file__), "../cimpy/examples/sampledata/CIGRE_MV") + ).resolve() import_files = [] - for file in example_dir.glob('*.xml'): + for file in example_dir.glob("*.xml"): import_files.append(str(file.absolute())) - return cimpy.cim_import(import_files, 'cgmes_v2_4_15') + return cimpy.cim_import(import_files, "cgmes_v2_4_15") def read_ref_xml(): - """ Read the reference xmls into a dict - """ + """Read the reference xmls into a dict""" test_list = [] test_dir = Path(os.path.dirname(__file__)).resolve() - for file in test_dir.glob('CIGREMV_reference*.xml'): - xmlstring = open(file, encoding='utf8').read() + for file in test_dir.glob("CIGREMV_reference*.xml"): + xmlstring = open(file, encoding="utf8").read() parsed_export_file = xmltodict.parse( - xmlstring, attr_prefix="$", cdata_key="_", dict_constructor=dict) - test_list.append(parsed_export_file['rdf:RDF']) + xmlstring, attr_prefix="$", cdata_key="_", dict_constructor=dict + ) + test_list.append(parsed_export_file["rdf:RDF"]) test_dict = {} for elem in test_list: - profile = elem['md:FullModel']['md:Model.profile'] + profile = elem["md:FullModel"]["md:Model.profile"] for key in short_profile_name.keys(): if key in profile: test_dict[key] = elem @@ -51,29 +52,32 @@ def read_exported_xml(directory): test_dir = Path(directory).resolve() - for file in test_dir.glob('*EXPORTED*.xml'): - xmlstring = open(file, encoding='utf8').read() + for file in test_dir.glob("*EXPORTED*.xml"): + xmlstring = open(file, encoding="utf8").read() parsed_export_file = xmltodict.parse( - xmlstring, attr_prefix="$", cdata_key="_", dict_constructor=dict) - export_list.append(parsed_export_file['rdf:RDF']) + xmlstring, attr_prefix="$", cdata_key="_", dict_constructor=dict + ) + export_list.append(parsed_export_file["rdf:RDF"]) export_dict = {} for export_file in export_list: - profile = export_file['md:FullModel']['md:Model.profile'] + profile = export_file["md:FullModel"]["md:Model.profile"] for key in short_profile_name.keys(): if key in profile: export_dict[key] = export_file return export_dict + # This test tests the export functionality of this package by first importing the CIGRE_MV_Rudion_With_LoadFlow_Results # example and exporting them. The exported files are compared with previously exported files which were checked manually def test_export_with_exported_files(sample_cimdata, tmpdir): - activeProfileList = ['DL', 'EQ', 'SV', 'TP'] + activeProfileList = ["DL", "EQ", "SV", "TP"] - cimpy.cim_export(sample_cimdata, tmpdir + '/EXPORTED_Test', - 'cgmes_v2_4_15', activeProfileList) + cimpy.cim_export( + sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", activeProfileList + ) test_dict = read_ref_xml() export_dict = read_exported_xml(tmpdir) @@ -83,7 +87,7 @@ def test_export_with_exported_files(sample_cimdata, tmpdir): if profile in export_dict.keys(): current_export_dict = export_dict[profile] for class_key in current_test_dict: - if 'cim:' not in class_key: + if "cim:" not in class_key: continue check.is_in(class_key, current_export_dict.keys()) if class_key in current_export_dict.keys(): @@ -97,10 +101,11 @@ def test_export_with_exported_files(sample_cimdata, tmpdir): def test_export_with_imported_files(sample_cimdata, tmpdir): - activeProfileList = ['DL', 'EQ', 'SSH', 'SV', 'TP'] + activeProfileList = ["DL", "EQ", "SSH", "SV", "TP"] - cimpy.cim_export(sample_cimdata, tmpdir + '/EXPORTED_Test', - 'cgmes_v2_4_15', activeProfileList) + cimpy.cim_export( + sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", activeProfileList + ) test_dict = read_ref_xml() export_dict = read_exported_xml(tmpdir) @@ -110,7 +115,7 @@ def test_export_with_imported_files(sample_cimdata, tmpdir): if profile in export_dict.keys(): current_export_dict = export_dict[profile] for class_key in current_test_dict: - if 'cim:' not in class_key or class_key in ['cim:NameType', 'cim:Name']: + if "cim:" not in class_key or class_key in ["cim:NameType", "cim:Name"]: continue check.is_in(class_key, current_export_dict.keys()) if class_key in current_export_dict.keys(): @@ -121,61 +126,58 @@ def test_export_with_imported_files(sample_cimdata, tmpdir): if isinstance(current_test_class, list): for obj in current_test_class: try: - test_mRIDs.append(obj['$rdf:ID']) - test_class_dict[obj['$rdf:ID']] = obj + test_mRIDs.append(obj["$rdf:ID"]) + test_class_dict[obj["$rdf:ID"]] = obj except KeyError: try: - test_mRIDs.append(obj['$rdf:about']) - test_class_dict[obj['$rdf:about']] = obj + test_mRIDs.append(obj["$rdf:about"]) + test_class_dict[obj["$rdf:about"]] = obj except KeyError: - check.is_in('$rdf:about', obj.keys()) - check.is_in('$rdf:ID', obj.keys()) + check.is_in("$rdf:about", obj.keys()) + check.is_in("$rdf:ID", obj.keys()) else: try: - test_mRIDs.append(current_test_class['$rdf:ID']) - test_class_dict[current_test_class['$rdf:ID'] - ] = current_test_class + test_mRIDs.append(current_test_class["$rdf:ID"]) + test_class_dict[ + current_test_class["$rdf:ID"] + ] = current_test_class except KeyError: try: - test_mRIDs.append( - current_test_class['$rdf:about']) - test_class_dict[current_test_class['$rdf:about']] = obj + test_mRIDs.append(current_test_class["$rdf:about"]) + test_class_dict[current_test_class["$rdf:about"]] = obj except KeyError: - check.is_in( - '$rdf:about', current_test_class.keys()) - check.is_in( - '$rdf:ID', current_test_class.keys()) + check.is_in("$rdf:about", current_test_class.keys()) + check.is_in("$rdf:ID", current_test_class.keys()) export_mRIDs = [] export_class_dict = {} if isinstance(current_export_class, list): for obj in current_export_class: try: - export_mRIDs.append(obj['$rdf:ID']) - export_class_dict[obj['$rdf:ID']] = obj + export_mRIDs.append(obj["$rdf:ID"]) + export_class_dict[obj["$rdf:ID"]] = obj except KeyError: try: - export_mRIDs.append(obj['$rdf:about']) - export_class_dict[obj['$rdf:about']] = obj + export_mRIDs.append(obj["$rdf:about"]) + export_class_dict[obj["$rdf:about"]] = obj except KeyError: - check.is_in('$rdf:about', obj.keys()) - check.is_in('$rdf:ID', obj.keys()) + check.is_in("$rdf:about", obj.keys()) + check.is_in("$rdf:ID", obj.keys()) else: try: - export_mRIDs.append( - current_export_class['$rdf:ID']) - export_class_dict[current_export_class['$rdf:ID'] - ] = current_export_class + export_mRIDs.append(current_export_class["$rdf:ID"]) + export_class_dict[ + current_export_class["$rdf:ID"] + ] = current_export_class except KeyError: try: - export_mRIDs.append( - current_export_class['$rdf:about']) - export_class_dict[current_export_class['$rdf:about']] = obj + export_mRIDs.append(current_export_class["$rdf:about"]) + export_class_dict[ + current_export_class["$rdf:about"] + ] = obj except KeyError: - check.is_in( - '$rdf:about', current_export_class.keys()) - check.is_in( - '$rdf:ID', current_export_class.keys()) + check.is_in("$rdf:about", current_export_class.keys()) + check.is_in("$rdf:ID", current_export_class.keys()) for mRID in test_mRIDs: check.is_in(mRID, export_mRIDs) @@ -183,31 +185,44 @@ def test_export_with_imported_files(sample_cimdata, tmpdir): test_attr = test_class_dict[mRID].items() export_attr = export_class_dict[mRID].items() for item in test_attr: - if item[0] in ['cim:NameType', 'cim:ExternalNetworkInjection.referencePriority', - 'cim:Terminal.connected']: + if item[0] in [ + "cim:NameType", + "cim:ExternalNetworkInjection.referencePriority", + "cim:Terminal.connected", + ]: continue - elif item[0] == 'cim:Terminal.sequenceNumber': - test_item = 'cim:ACDCTerminal.sequenceNumber' + elif item[0] == "cim:Terminal.sequenceNumber": + test_item = "cim:ACDCTerminal.sequenceNumber" else: test_item = item[0] - if item[1] in ['0', '0e+000', '0.0', '', 'false', 'None', 'list', - {'$rdf:resource': '#_32d6d32e-c3f0-43d4-8103-079a15594fc6'}]: + if item[1] in [ + "0", + "0e+000", + "0.0", + "", + "false", + "None", + "list", + { + "$rdf:resource": "#_32d6d32e-c3f0-43d4-8103-079a15594fc6" + }, + ]: continue - if isinstance(item[1], dict) or isinstance(item[1], list): + if isinstance(item[1], dict) or isinstance( + item[1], list + ): test_item = item - elif len(item[1].split('.')) > 1: + elif len(item[1].split(".")) > 1: try: - test_item = ( - item[0], str(float(item[1]))) + test_item = (item[0], str(float(item[1]))) except ValueError: continue except TypeError: pass - elif item[1] == 'true': - test_item = (test_item, 'True') + elif item[1] == "true": + test_item = (test_item, "True") else: test_item = (test_item, item[1]) check.is_in(test_item, export_attr) - diff --git a/tests/test_import.py b/tests/test_import.py index 69dff221..af36bbad 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -7,30 +7,33 @@ import pickle from pathlib import Path -logging.basicConfig(filename='Test_import.log', - level=logging.INFO, filemode='w') +logging.basicConfig(filename="Test_import.log", level=logging.INFO, filemode="w") -example_dir = Path(os.path.join(os.path.dirname(__file__), - '../cimpy/examples/sampledata/CIGRE_MV')).resolve() +example_dir = Path( + os.path.join(os.path.dirname(__file__), "../cimpy/examples/sampledata/CIGRE_MV") +).resolve() def test_import(): - """ This function tests the import functionality by importing files and comparing them to previously imported and pickled files. - """ + """This function tests the import functionality by importing files and comparing them to previously imported and pickled files.""" global example_dir test_files = [] - for file in example_dir.glob('*.xml'): + for file in example_dir.glob("*.xml"): test_files.append(str(file.absolute())) - imported_result = cimpy.cim_import(test_files, 'cgmes_v2_4_15') + imported_result = cimpy.cim_import(test_files, "cgmes_v2_4_15") import_resolved = cimpy.cimexport._get_class_attributes_with_references( - imported_result, 'cgmes_v2_4_15') - - ref_dict_path = Path(os.path.join(os.path.dirname( - __file__), 'CIGREMV_import_reference_cgmes_v2_4_15.p')) - check_dict_pickle = pickle.load(open(ref_dict_path, 'rb')) + imported_result, "cgmes_v2_4_15" + ) + + ref_dict_path = Path( + os.path.join( + os.path.dirname(__file__), "CIGREMV_import_reference_cgmes_v2_4_15.p" + ) + ) + check_dict_pickle = pickle.load(open(ref_dict_path, "rb")) for elem in import_resolved: check.is_in(elem, check_dict_pickle) From 807b1b6e209a414f0470ec578f8649dd1e8b6702 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 15:35:28 +0200 Subject: [PATCH 02/17] Add simple pre-commit config Signed-off-by: Steffen Vogel --- .pre-commit-config.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..baa0e516 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,19 @@ +exclude: ^cimpy/cgmes_ + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + + - repo: https://github.com/PyCQA/flake8 + rev: 7.1.0 + hooks: + - id: flake8 From 2c35fc95db59665763fe78b699037e16c2411c87 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 15:37:12 +0200 Subject: [PATCH 03/17] Add GitHub actions workflow for running pre-commit Signed-off-by: Steffen Vogel --- .github/workflows/pre-commit.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/pre-commit.yaml diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 00000000..d5b273ed --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,14 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [main] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - uses: pre-commit/action@v3.0.1 From d3784b6e362a1edae6ecb05496803ef54ca50626 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 15:39:54 +0200 Subject: [PATCH 04/17] Fix missing newline at end-of-file Signed-off-by: Steffen Vogel --- MANIFEST.in | 2 +- .../Bus-Branch/20191030T0924Z_XX_YYY_DL_.xml | 2 +- .../Bus-Branch/20191030T0924Z_XX_YYY_GL_.xml | 2 +- .../Bus-Branch/20191030T0924Z_XX_YYY_SSH_.xml | 2 +- .../Bus-Branch/20191030T0924Z_XX_YYY_SV_.xml | 2 +- .../Bus-Branch/20191030T0924Z_XX_YYY_TP_.xml | 2 +- .../Sample_Grid_Switches/Bus-Branch/20191030T0924Z_YYY_EQ_.xml | 2 +- .../sampledata/Sample_Grid_Switches/Bus-Branch/README.md | 2 +- .../Node-Breaker/20191030T0924Z_XX_YYY_DL_.xml | 2 +- .../Node-Breaker/20191030T0924Z_XX_YYY_GL_.xml | 2 +- .../Node-Breaker/20191030T0924Z_XX_YYY_SSH_.xml | 2 +- .../Node-Breaker/20191030T0924Z_XX_YYY_SV_.xml | 2 +- .../Node-Breaker/20191030T0924Z_XX_YYY_TP_.xml | 2 +- .../Node-Breaker/20191030T0924Z_YYY_EQ_.xml | 2 +- .../sampledata/Sample_Grid_Switches/Node-Breaker/README.md | 2 +- cimpy/export_template.mustache | 2 +- documentation/CIMpy.svg | 2 +- documentation/cimpy.cgmes_v2_4_15.rst | 2 +- documentation/conf.py | 2 +- tests/CIGREMV_reference_cgmes_v2_4_15_DiagramLayout.xml | 2 +- tests/CIGREMV_reference_cgmes_v2_4_15_Equipment.xml | 2 +- tests/CIGREMV_reference_cgmes_v2_4_15_StateVariables.xml | 2 +- tests/CIGREMV_reference_cgmes_v2_4_15_Topology.xml | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 2af219ee..ba74de8f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ recursive-include cimpy/examples *.xml *.py -recursive-include cimpy *.mustache \ No newline at end of file +recursive-include cimpy *.mustache diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_DL_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_DL_.xml index f455cf81..1e2a6b14 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_DL_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_DL_.xml @@ -485,4 +485,4 @@ 223.125 74.375 - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_GL_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_GL_.xml index 4421a304..e09f967c 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_GL_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_GL_.xml @@ -12,4 +12,4 @@ urn:ogc:def:crs:EPSG::4326 WGS-84 - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_SSH_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_SSH_.xml index 029f2085..c64f7582 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_SSH_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_SSH_.xml @@ -107,4 +107,4 @@ false - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_SV_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_SV_.xml index 884d77b6..5490d836 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_SV_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_SV_.xml @@ -9,4 +9,4 @@ http://entsoe.eu/CIM/StateVariables/4/1 2019-10-30T09:24:51Z - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_TP_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_TP_.xml index f0cf3fa1..e8e83ee1 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_TP_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_XX_YYY_TP_.xml @@ -152,4 +152,4 @@ - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_YYY_EQ_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_YYY_EQ_.xml index bd7e8880..4ed36cc8 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_YYY_EQ_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/20191030T0924Z_YYY_EQ_.xml @@ -573,4 +573,4 @@ 1. Line3 - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/README.md b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/README.md index f593deed..56d012a5 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/README.md +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Bus-Branch/README.md @@ -1,2 +1,2 @@ Export information: -- Tool: PowerFactory \ No newline at end of file +- Tool: PowerFactory diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_DL_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_DL_.xml index 4b955a56..801da0ae 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_DL_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_DL_.xml @@ -737,4 +737,4 @@ 83.125 74.375 - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_GL_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_GL_.xml index 5af55ac4..2aace45c 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_GL_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_GL_.xml @@ -12,4 +12,4 @@ urn:ogc:def:crs:EPSG::4326 WGS-84 - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_SSH_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_SSH_.xml index 953b8cdd..59cccf42 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_SSH_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_SSH_.xml @@ -152,4 +152,4 @@ false - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_SV_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_SV_.xml index 146feb6e..35c7d788 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_SV_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_SV_.xml @@ -9,4 +9,4 @@ http://entsoe.eu/CIM/StateVariables/4/1 2019-10-30T09:24:51Z - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_TP_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_TP_.xml index 0d02d449..d4abe1a5 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_TP_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_XX_YYY_TP_.xml @@ -266,4 +266,4 @@ Bus5 - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_YYY_EQ_.xml b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_YYY_EQ_.xml index bb88e0d2..273a1d62 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_YYY_EQ_.xml +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/20191030T0924Z_YYY_EQ_.xml @@ -742,4 +742,4 @@ 110.00 kV 110.00 kV - \ No newline at end of file + diff --git a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/README.md b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/README.md index 91fb7531..735b2049 100644 --- a/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/README.md +++ b/cimpy/examples/sampledata/Sample_Grid_Switches/Node-Breaker/README.md @@ -1,3 +1,3 @@ Export information: - Tool: PowerFactory -- ConnectivityNodes moved into Topology file for cgmes-v2_4_15 compliance \ No newline at end of file +- ConnectivityNodes moved into Topology file for cgmes-v2_4_15 compliance diff --git a/cimpy/export_template.mustache b/cimpy/export_template.mustache index caa7cb9f..39d2e3c9 100644 --- a/cimpy/export_template.mustache +++ b/cimpy/export_template.mustache @@ -21,4 +21,4 @@ {{/attributes}} {{/about}} - \ No newline at end of file + diff --git a/documentation/CIMpy.svg b/documentation/CIMpy.svg index 2dadf6f1..e6395c41 100644 --- a/documentation/CIMpy.svg +++ b/documentation/CIMpy.svg @@ -1,3 +1,3 @@ -
Import
Import
Export
Export
cgmes v2.4.15
cgmes...

CIMpy

CIMpy
Cim Codebase Generator
Cim Codebase Generator
CIM XML/
RDF Files
CIM XML/...
CIM XML/
RDF Files
CIM XML/...
RDFS files
RDFS files
Python class files
Python class fil...
Utils
Utils
Python objects
Python o...
Viewer does not support full SVG 1.1
\ No newline at end of file +
Import
Import
Export
Export
cgmes v2.4.15
cgmes...

CIMpy

CIMpy
Cim Codebase Generator
Cim Codebase Generator
CIM XML/
RDF Files
CIM XML/...
CIM XML/
RDF Files
CIM XML/...
RDFS files
RDFS files
Python class files
Python class fil...
Utils
Utils
Python objects
Python o...
Viewer does not support full SVG 1.1
diff --git a/documentation/cimpy.cgmes_v2_4_15.rst b/documentation/cimpy.cgmes_v2_4_15.rst index b56a6a8a..d7faf838 100644 --- a/documentation/cimpy.cgmes_v2_4_15.rst +++ b/documentation/cimpy.cgmes_v2_4_15.rst @@ -5,4 +5,4 @@ cimpy.cgmes\_v2\_4\_15 package :maxdepth: 1 :glob: - cimpy.cgmes_v2_4_15.* \ No newline at end of file + cimpy.cgmes_v2_4_15.* diff --git a/documentation/conf.py b/documentation/conf.py index 6b4c9671..36c513cd 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -75,4 +75,4 @@ # -- Options for todo extension ---------------------------------------------- # If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True \ No newline at end of file +todo_include_todos = True diff --git a/tests/CIGREMV_reference_cgmes_v2_4_15_DiagramLayout.xml b/tests/CIGREMV_reference_cgmes_v2_4_15_DiagramLayout.xml index aa2e8897..325806ea 100644 --- a/tests/CIGREMV_reference_cgmes_v2_4_15_DiagramLayout.xml +++ b/tests/CIGREMV_reference_cgmes_v2_4_15_DiagramLayout.xml @@ -1050,4 +1050,4 @@ 152.8 225.0 - \ No newline at end of file + diff --git a/tests/CIGREMV_reference_cgmes_v2_4_15_Equipment.xml b/tests/CIGREMV_reference_cgmes_v2_4_15_Equipment.xml index 9714abe1..6b4a679f 100644 --- a/tests/CIGREMV_reference_cgmes_v2_4_15_Equipment.xml +++ b/tests/CIGREMV_reference_cgmes_v2_4_15_Equipment.xml @@ -1063,4 +1063,4 @@ - \ No newline at end of file + diff --git a/tests/CIGREMV_reference_cgmes_v2_4_15_StateVariables.xml b/tests/CIGREMV_reference_cgmes_v2_4_15_StateVariables.xml index 703dd239..fe2203b6 100644 --- a/tests/CIGREMV_reference_cgmes_v2_4_15_StateVariables.xml +++ b/tests/CIGREMV_reference_cgmes_v2_4_15_StateVariables.xml @@ -268,4 +268,4 @@ - \ No newline at end of file + diff --git a/tests/CIGREMV_reference_cgmes_v2_4_15_Topology.xml b/tests/CIGREMV_reference_cgmes_v2_4_15_Topology.xml index 89bc7556..95d1d759 100644 --- a/tests/CIGREMV_reference_cgmes_v2_4_15_Topology.xml +++ b/tests/CIGREMV_reference_cgmes_v2_4_15_Topology.xml @@ -332,4 +332,4 @@ - \ No newline at end of file + From 360746e6ea89307ef0aa536ec0884bb8bfbd047e Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 15:40:57 +0200 Subject: [PATCH 05/17] Remove trailing whitespaces Signed-off-by: Steffen Vogel --- .github/workflows/pytest.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 07c54eb6..8f26e687 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -2,7 +2,7 @@ name: Pytest on: push jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-latest steps: - name: checkout repo content uses: actions/checkout@v3 # checkout the repository content to github runner. @@ -12,27 +12,27 @@ jobs: python-version: 3.8 - name: Install graphviz run: sudo apt install graphviz - shell: bash + shell: bash - name: Install dependencies run: | sudo pip3 install --upgrade pip sudo pip3 install sphinx_rtd_theme sudo pip3 install sphinx sudo pip3 install pytest - sudo pip3 install pytest-check + sudo pip3 install pytest-check - name: Execute py script env: working-directory: ${{runner.workspace}}/cimpy - run: | - sudo python3 setup.py install + run: | + sudo python3 setup.py install - name: Pytest env: working-directory: ${{runner.workspace}}/cimpy run: | - sudo pytest -v -cov --junitxml=report.xml + sudo pytest -v -cov --junitxml=report.xml - name: Upload pytest test results uses: actions/upload-artifact@v3 with: name: pytest-results - path: ${{runner.workspace}}/cimpy/report.xml + path: ${{runner.workspace}}/cimpy/report.xml if: ${{ always() }} From c90cd2a1d6c1662e7d854989be7294ee1aec8f17 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 15:42:08 +0200 Subject: [PATCH 06/17] Cleanup .gitignore Signed-off-by: Steffen Vogel --- .gitignore | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index ff7a24b8..9b3c8620 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,14 @@ -# python folders +# Python folders *.egg-info/ __pycache__ .idea +.venv ./cimpy/__pycache__ *.egg build/ dist/ -# log files *.log -# vs code +# VSCode .vscode/* - -# documentation -# documentation/ From aecadd01176f13ccccc11e2e430366e512df0281 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 16:05:36 +0200 Subject: [PATCH 07/17] Fix warnings and formatting from black and flake8 Signed-off-by: Steffen Vogel --- .flake8 | 2 + cimpy/__init__.py | 2 + cimpy/cimexport.py | 120 ++++++----------- cimpy/cimimport.py | 122 ++++++------------ cimpy/examples/addExternalNetworkInjection.py | 4 +- cimpy/examples/convertToBusBranch.py | 12 +- cimpy/examples/exportCIGREMV.py | 4 +- cimpy/utils.py | 28 +--- documentation/set_inheritance_diagram.py | 2 +- pyproject.toml | 2 + tests/create_pickle_dump.py | 7 +- tests/test_export.py | 45 ++----- tests/test_import.py | 19 +-- 13 files changed, 112 insertions(+), 257 deletions(-) create mode 100644 .flake8 create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..77cb4bcb --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 120 diff --git a/cimpy/__init__.py b/cimpy/__init__.py index 15a9ab7f..bfaddfee 100644 --- a/cimpy/__init__.py +++ b/cimpy/__init__.py @@ -1,3 +1,5 @@ +# flake8: noqa + from cimpy.cimexport import cim_export from cimpy.cimimport import cim_import import cimpy.utils diff --git a/cimpy/cimexport.py b/cimpy/cimexport.py index 35463b78..14d0c761 100644 --- a/cimpy/cimexport.py +++ b/cimpy/cimexport.py @@ -8,10 +8,11 @@ import sys from cimpy.cgmes_v2_4_15.Base import Base -cgmesProfile = Base.cgmesProfile from pathlib import Path import copy +cgmesProfile = Base.cgmesProfile + logger = logging.getLogger(__name__) @@ -28,9 +29,7 @@ def _get_class_attributes_with_references(import_result, version): attributes_dict = _get_attributes(topology[key]) # change attribute references to mRID of the object, res needed because classes like SvPowerFlow does not have # mRID as an attribute. Therefore the corresponding class has to be searched in the res dictionary - class_dict["attributes"] = _get_reference_uuid( - attributes_dict, version, topology, key, urls - ) + class_dict["attributes"] = _get_reference_uuid(attributes_dict, version, topology, key, urls) class_attributes_list.append(class_dict) del class_dict @@ -70,11 +69,7 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): array.append(UUID) else: - logger.warning( - "Reference object not subclass of Base class for object with UUID {}.".format( - mRID - ) - ) + logger.warning("Reference object not subclass of Base class for object with UUID {}.".format(mRID)) if len(array) == 1: attributes["value"] = array[0] else: @@ -101,9 +96,7 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): if key.split(".")[1] in urls.keys(): # value in urls dict? should always be true if attr_dict[key] in urls[key.split(".")[1]].keys(): - attributes["value"] = ( - "%URL%" + urls[key.split(".")[1]][attr_dict[key]] - ) + attributes["value"] = "%URL%" + urls[key.split(".")[1]][attr_dict[key]] else: logger.warning( "URL reference for attribute {} and value {} not found!".format( @@ -119,9 +112,7 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): for reference_item in attributes["value"]: # ignore default values if reference_item not in ["", None, 0.0, 0]: - reference_list.append( - {"value": reference_item, "attr_name": key} - ) + reference_list.append({"value": reference_item, "attr_name": key}) # ignore default values elif attributes["value"] not in ["", None, 0.0, 0, "list"]: reference_list.append(attributes) @@ -194,13 +185,10 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): about_dict = {} # store serializationProfile and possibleProfileList - # serializationProfile class attribute, same for multiple instances of same class, only last origin of variable stored - serializationProfile = copy.deepcopy( - klass["attributes"][0]["serializationProfile"] - ) - possibleProfileList = copy.deepcopy( - klass["attributes"][1]["possibleProfileList"] - ) + # serializationProfile class attribute, same for multiple instances + # of same class, only last origin of variable stored + serializationProfile = copy.deepcopy(klass["attributes"][0]["serializationProfile"]) + possibleProfileList = copy.deepcopy(klass["attributes"][1]["possibleProfileList"]) class_serializationProfile = "" @@ -208,10 +196,7 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): # class was imported if Profile[serializationProfile["class"]] in activeProfileList: # else: class origin profile not active for export, get active profile from possibleProfileList - if ( - Profile[serializationProfile["class"]].value - in possibleProfileList[klass["name"]]["class"] - ): + if Profile[serializationProfile["class"]].value in possibleProfileList[klass["name"]]["class"]: # profile active and in possibleProfileList # else: class should not have been imported from this profile, get allowed profile # from possibleProfileList @@ -225,9 +210,7 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): else: logger.info( "Class {} was read from profile {} but this profile is not active for the export. Use" - "default profile from possibleProfileList.".format( - klass["name"], serializationProfile["class"] - ) + "default profile from possibleProfileList.".format(klass["name"], serializationProfile["class"]) ) if class_serializationProfile == "": @@ -249,13 +232,9 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): ) continue else: - logger.warning( - "Class {} has no profile to export to.".format(klass["name"]) - ) + logger.warning("Class {} has no profile to export to.".format(klass["name"])) else: - logger.warning( - "Class {} has no profile to export to.".format(klass["name"]) - ) + logger.warning("Class {} has no profile to export to.".format(klass["name"])) # iterate over attributes for attribute in klass["attributes"]: @@ -271,35 +250,20 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): if attribute_name in serializationProfile.keys(): # attribute was imported - if ( - Profile[serializationProfile[attribute_name]] - in activeProfileList - ): + if Profile[serializationProfile[attribute_name]] in activeProfileList: attr_value = Profile[serializationProfile[attribute_name]].value - if ( - attr_value - in possibleProfileList[attribute_class][attribute_name] - ): - attribute_serializationProfile = serializationProfile[ - attribute_name - ] + if attr_value in possibleProfileList[attribute_class][attribute_name]: + attribute_serializationProfile = serializationProfile[attribute_name] if attribute_serializationProfile == "": # attribute was added if attribute_class in possibleProfileList.keys(): - if ( - attribute_name - in possibleProfileList[attribute_class].keys() - ): + if attribute_name in possibleProfileList[attribute_class].keys(): possibleProfileList[attribute_class][attribute_name].sort() - for attr_profile in possibleProfileList[attribute_class][ - attribute_name - ]: + for attr_profile in possibleProfileList[attribute_class][attribute_name]: if Profile(attr_profile) in activeProfileList: # active profile for class export found - attribute_serializationProfile = Profile( - attr_profile - ).name + attribute_serializationProfile = Profile(attr_profile).name break if attribute_serializationProfile == "": # no profile in possibleProfileList active, skip attribute @@ -336,15 +300,11 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): # add class with all attributes in the same profile to the export dict sorted by the profile if class_serializationProfile in export_dict.keys(): - export_class = dict( - name=klass["name"], mRID=klass["mRID"], attributes=same_package_list - ) + export_class = dict(name=klass["name"], mRID=klass["mRID"], attributes=same_package_list) export_dict[class_serializationProfile]["classes"].append(export_class) del export_class else: - export_class = dict( - name=klass["name"], mRID=klass["mRID"], attributes=same_package_list - ) + export_class = dict(name=klass["name"], mRID=klass["mRID"], attributes=same_package_list) export_dict[class_serializationProfile] = {"classes": [export_class]} # add class with all attributes defined in another profile to the about_key sorted by the profile @@ -376,12 +336,13 @@ def cim_export(import_result, file_name, version, activeProfileList): a reference to another class object or not. :param import_result: a dictionary containing the topology and meta information. The topology can be extracted via \ - :func:`~cimpy.cimimport.cim_import()`. The topology dictionary contains all objects accessible via their mRID. The meta \ - information can be extracted via import_result['meta_info']. The meta_info dictionary contains a new dictionary with \ - the keys: 'author', 'namespaces' and 'urls'. The last two are also dictionaries. 'urls' contains a mapping \ - between references to URLs and the extracted value of the URL, e.g. 'absoluteValue': \ - 'http://iec.ch/TC57/2012/CIM-schema-cim16#OperationalLimitDirectionKind.absoluteValue' These mappings are accessible \ - via the name of the attribute, e.g. import_result['meta_info']['urls'}[attr_name] = {mapping like example above}. \ + :func:`~cimpy.cimimport.cim_import()`. The topology dictionary contains all objects accessible via their mRID. \ + The meta information can be extracted via import_result['meta_info']. The meta_info dictionary contains a new \ + dictionary with the keys: 'author', 'namespaces' and 'urls'. The last two are also dictionaries. \ + 'urls' contains a mapping between references to URLs and the extracted value of the URL, e.g. 'absoluteValue': \ + 'http://iec.ch/TC57/2012/CIM-schema-cim16#OperationalLimitDirectionKind.absoluteValue' These mappings are \ + accessible via the name of the attribute, \ + e.g. import_result['meta_info']['urls'}[attr_name] = {mapping like example above}. \ 'namespaces' is a dictionary containing all RDF namespaces used in the imported xml files. :param file_name: a string with the name of the xml files which will be created :param version: cgmes version, e.g. version = "cgmes_v2_4_15" @@ -400,9 +361,7 @@ def cim_export(import_result, file_name, version, activeProfileList): full_file_name = file_name + "_" + profile.long_name() + ".xml" if not os.path.exists(full_file_name): - output = generate_xml( - import_result, version, file_name, profile, profile_list - ) + output = generate_xml(import_result, version, file_name, profile, profile_list) with open(full_file_name, "w") as file: logger.info('Write file "%s"', full_file_name) @@ -428,7 +387,8 @@ def generate_xml(cim_data, version, model_name, profile, available_profiles): This function serializes cgmes classes with the template engine chevron and returns them as a string. - :param cim_data: a dictionary containing the topology and meta information. It can be created via :func:`~cimpy.cimimport.cim_import()` + :param cim_data: a dictionary containing the topology and meta information. It can be created via \ + :func:`~cimpy.cimimport.cim_import()` :param version: cgmes version, e.g. ``version="cgmes_v2_4_15"`` :param profile: The :class:`~cimpy.cgmes_v2_4_15.Base.Profile` for which the serialization should be generated. :param model_name: a string with the name of the model. @@ -441,9 +401,7 @@ def generate_xml(cim_data, version, model_name, profile, available_profiles): # determine class and attribute export profiles. The export dict contains all classes and their attributes where # the class definition and the attribute definitions are in the same profile. Every entry in about_dict generates # a rdf:about in another profile - export_dict, about_dict = _sort_classes_to_profile( - class_attributes_list, available_profiles - ) + export_dict, about_dict = _sort_classes_to_profile(class_attributes_list, available_profiles) namespaces_list = _create_namespaces_list(cim_data["meta_info"]["namespaces"]) @@ -481,9 +439,7 @@ def generate_xml(cim_data, version, model_name, profile, available_profiles): {"attr_name": "profile", "value": profile.long_name()}, ], } - template_path = Path( - os.path.join(os.path.dirname(__file__), "export_template.mustache") - ).resolve() + template_path = Path(os.path.join(os.path.dirname(__file__), "export_template.mustache")).resolve() with open(template_path) as f: output = chevron.render( f, @@ -514,9 +470,7 @@ def _get_attributes(class_object): class_type = type(parent) # dictionary containing all attributes with key: 'Class_Name.Attribute_Name' - attributes_dict = dict( - serializationProfile=class_object.serializationProfile, possibleProfileList={} - ) + attributes_dict = dict(serializationProfile=class_object.serializationProfile, possibleProfileList={}) # __dict__ of a subclass returns also the attributes of the parent classes # to avoid multiple attributes create list with all attributes already processed @@ -541,8 +495,6 @@ def _get_attributes(class_object): # the serializationProfile from parent classes is not needed because entries in the serializationProfile # are only generated for the inherited class if class_name != "Base": - attributes_dict["possibleProfileList"][ - class_name - ] = parent_class.possibleProfileList + attributes_dict["possibleProfileList"][class_name] = parent_class.possibleProfileList return attributes_dict diff --git a/cimpy/cimimport.py b/cimpy/cimimport.py index 67b24b2a..7cd89f3f 100644 --- a/cimpy/cimimport.py +++ b/cimpy/cimimport.py @@ -2,8 +2,6 @@ from time import time import importlib import logging -import os -import cimpy logger = logging.getLogger(__name__) @@ -21,13 +19,14 @@ def cim_import(xml_files, cgmes_version, start_dict=None): :param start_dict: a list of classes which indicates which classes will be read e.g. elements=["BaseVoltage", "ACLineSegment"] * If start_dict=None the complete file will be read - :return: import_result: a dictionary containing the topology and meta information. The topology can be extracted via \ - import_result['topology']. The topology dictionary contains all objects accessible via their mRID. The meta \ - information can be extracted via import_result['meta_info']. The meta_info dictionary contains a new dictionary with \ - the keys: 'author', 'namespaces' and 'urls'. The last two are also dictionaries. 'urls' contains a mapping \ + :return: import_result: a dictionary containing the topology and meta information.The topology can be extracted \ + via import_result['topology']. The topology dictionary contains all objects accessible via their mRID. The meta \ + information can be extracted via import_result['meta_info']. The meta_info dictionary contains a new dictionary \ + with the keys: 'author', 'namespaces' and 'urls'. The last two are also dictionaries. 'urls' contains a mapping \ between references to URLs and the extracted value of the URL, e.g. 'absoluteValue': \ - 'http://iec.ch/TC57/2012/CIM-schema-cim16#OperationalLimitDirectionKind.absoluteValue' These mappings are accessible \ - via the name of the attribute, e.g. import_result['meta_info']['urls'}[attr_name] = {mapping like example above}. \ + 'http://iec.ch/TC57/2012/CIM-schema-cim16#OperationalLimitDirectionKind.absoluteValue' These mappings are \ + accessible via the name of the attribute, \ + e.g. import_result['meta_info']['urls'}[attr_name] = {mapping like example above}. \ 'namespaces' is a dictionary containing all RDF namespaces used in the imported xml files. """ @@ -41,9 +40,7 @@ def cim_import(xml_files, cgmes_version, start_dict=None): logger_grouped = dict(errors={}, info={}) # create a dict which will contain meta information and the topology - import_result = ( - start_dict if start_dict is not None else dict(meta_info={}, topology={}) - ) + import_result = start_dict if start_dict is not None else dict(meta_info={}, topology={}) # create sub-dictionaries import_result["meta_info"] = dict(namespaces=_get_namespaces(xml_files[0]), urls={}) @@ -61,9 +58,7 @@ def cim_import(xml_files, cgmes_version, start_dict=None): logger_grouped, ) - import_result, logger_grouped = _set_attributes( - import_result, xml_files, namespace_rdf, base, logger_grouped - ) + import_result, logger_grouped = _set_attributes(import_result, xml_files, namespace_rdf, base, logger_grouped) if logger_grouped["errors"]: for error, count in logger_grouped["errors"].items(): @@ -79,17 +74,9 @@ def cim_import(xml_files, cgmes_version, start_dict=None): print(logging_message) elapsed_time = time() - t0 - logger.info( - "Created totally {} CIM objects in {}s\n\n".format( - len(import_result["topology"]), elapsed_time - ) - ) + logger.info("Created totally {} CIM objects in {}s\n\n".format(len(import_result["topology"]), elapsed_time)) # print info of how many classes in total were instantiated to terminal - print( - "Created totally {} CIM objects in {}s".format( - len(import_result["topology"]), elapsed_time - ) - ) + print("Created totally {} CIM objects in {}s".format(len(import_result["topology"]), elapsed_time)) return import_result @@ -99,9 +86,7 @@ def cim_import(xml_files, cgmes_version, start_dict=None): # are set in the _set_attributes function because some attributes might be stored in one package and the class in # another. Since after this function all classes are instantiated, there should be no problem in setting the attributes. # Also the information from which package file a class was read is stored in the serializationProfile dictionary. -def _instantiate_classes( - import_result, xml_files, cgmes_version_path, namespace_rdf, base, logger_grouped -): +def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace_rdf, base, logger_grouped): # extract topology from import_result topology = import_result["topology"] @@ -158,9 +143,7 @@ def _instantiate_classes( # Instantiate the class and map it to the uuid. # res[uuid] = klass(UUID=uuid) topology[uuid] = klass() - info_msg = "CIM object {} created".format( - module_name.split(".")[-1] - ) + info_msg = "CIM object {} created".format(module_name.split(".")[-1]) try: logger_grouped["info"][info_msg] += 1 except KeyError: @@ -172,13 +155,9 @@ def _instantiate_classes( topology[uuid].mRID = uuid if package != "": - topology[uuid].serializationProfile[ - "class" - ] = short_package_name[package] + topology[uuid].serializationProfile["class"] = short_package_name[package] else: - error_msg = "Package information not found for class {}".format( - klass.__class__.__name__ - ) + error_msg = "Package information not found for class {}".format(klass.__class__.__name__) try: logger_grouped["errors"][error_msg] += 1 except KeyError: @@ -245,9 +224,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe try: obj = topology[uuid] except KeyError: - error_msg = "Missing {} object with uuid: {}".format( - elem.tag[m:], uuid - ) + error_msg = "Missing {} object with uuid: {}".format(elem.tag[m:], uuid) try: logger_grouped["errors"][error_msg] += 1 except KeyError: @@ -285,9 +262,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Convert value type using the default value. try: typ = type(getattr(obj, attr)) - if isinstance( - getattr(obj, attr), bool - ): # if typ== + if isinstance(getattr(obj, attr), bool): # if typ== # The function bool("false") returns True, # because it is called upon non-empty string! # This means that it wrongly reads "false" value as boolean True. @@ -308,9 +283,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Use the '#' prefix to distinguish between references and enumerations. if uuid2[0] == "#": # reference try: - val = topology[ - uuid2[1:] - ] # remove '#' prefix + val = topology[uuid2[1:]] # remove '#' prefix except KeyError: error_msg = "Referenced {} [{}] object missing.".format( obj.__class__.__name__, uuid2[1:] @@ -338,8 +311,11 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe pass else: # note here - error_msg = "Multiplicity Error for class {} [{}], attribute {}. Multiplicity should be 1..1 or 0..1".format( - obj.__class__.__name__, uuid, attr + error_msg = ( + "Multiplicity Error for class {} [{}], attribute {}. ".format( + obj.__class__.__name__, uuid, attr + ) + + "Multiplicity should be 1..1 or 0..1" ) try: logger_grouped["errors"][error_msg] += 1 @@ -347,21 +323,13 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe logger_grouped["errors"][error_msg] = 1 if hasattr(val, obj.__class__.__name__): - default1 = getattr( - val, obj.__class__.__name__ - ) + default1 = getattr(val, obj.__class__.__name__) if default1 is None: - setattr( - val, obj.__class__.__name__, obj - ) + setattr(val, obj.__class__.__name__, obj) elif default1 == "list": # many - setattr( - val, obj.__class__.__name__, [obj] - ) + setattr(val, obj.__class__.__name__, [obj]) elif isinstance(default1, list): # many - attribute2 = getattr( - val, obj.__class__.__name__ - ) + attribute2 = getattr(val, obj.__class__.__name__) if obj not in attribute2: attribute2.append(obj) setattr( @@ -372,44 +340,34 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe elif default1 == obj: pass else: - error_msg = "Multiplicity Error for class {} [{}], attribute {}. Multiplicity should be 1..1 or 0..1".format( - val.__class__.__name__, - uuid2[1:], - obj.__class__.__name__, + error_msg = ( + "Multiplicity Error for class {} [{}], attribute {}. ".format( + val.__class__.__name__, + uuid2[1:], + obj.__class__.__name__, + ) + + "Multiplicity should be 1..1 or 0..1" ) try: - logger_grouped["errors"][ - error_msg - ] += 1 + logger_grouped["errors"][error_msg] += 1 except KeyError: - logger_grouped["errors"][ - error_msg - ] = 1 + logger_grouped["errors"][error_msg] = 1 else: # enum # if http in uuid2 reference to URL, create mapping if "http" in uuid2: if attr in urls.keys(): - if ( - uuid2.rsplit(".", 1)[1] - not in urls[attr].keys() - ): - urls[attr][ - uuid2.rsplit(".", 1)[1] - ] = uuid2 + if uuid2.rsplit(".", 1)[1] not in urls[attr].keys(): + urls[attr][uuid2.rsplit(".", 1)[1]] = uuid2 else: - urls[attr] = { - uuid2.rsplit(".", 1)[1]: uuid2 - } + urls[attr] = {uuid2.rsplit(".", 1)[1]: uuid2} # url_reference_dict[uuid2.rsplit(".", 1)[1]] = uuid2 val = uuid2.rsplit(".", 1)[1] setattr(obj, attr, val) if package != "": - obj.serializationProfile[attr] = short_package_name[ - package - ] + obj.serializationProfile[attr] = short_package_name[package] else: error_msg = "Package information not found for class {}, attribute {}".format( obj.__class__.__name__, attr diff --git a/cimpy/examples/addExternalNetworkInjection.py b/cimpy/examples/addExternalNetworkInjection.py index 45fe8bf7..5402c892 100644 --- a/cimpy/examples/addExternalNetworkInjection.py +++ b/cimpy/examples/addExternalNetworkInjection.py @@ -20,9 +20,7 @@ import_result = cimpy.cim_import(xml_files, "cgmes_v2_4_15") -import_result = cimpy.utils.add_external_network_injection( - import_result, "cgmes_v2_4_15", "N1", 20.0 -) +import_result = cimpy.utils.add_external_network_injection(import_result, "cgmes_v2_4_15", "N1", 20.0) activeProfileList = ["DL", "EQ", "SV", "TP"] diff --git a/cimpy/examples/convertToBusBranch.py b/cimpy/examples/convertToBusBranch.py index e4eecf31..477a5bfe 100644 --- a/cimpy/examples/convertToBusBranch.py +++ b/cimpy/examples/convertToBusBranch.py @@ -2,17 +2,13 @@ import cimpy from pathlib import Path -logging.basicConfig( - filename="Convert_to_Bus_Branch.log", level=logging.INFO, filemode="w" -) +logging.basicConfig(filename="Convert_to_Bus_Branch.log", level=logging.INFO, filemode="w") example = Path(__file__).resolve().parent # called as cimpy.examples.convertBusBranch() or file run from quickstart directory? if "cimexamples.py" in str(__file__): - sample_folder = ( - example / "examples" / "sampledata" / "Sample_Grid_Switches" / "Node-Breaker" - ) + sample_folder = example / "examples" / "sampledata" / "Sample_Grid_Switches" / "Node-Breaker" else: sample_folder = example / "sampledata" / "Sample_Grid_Switches" / "Node-Breaker" @@ -29,6 +25,4 @@ activeProfileList = ["DL", "EQ", "TP"] -cimpy.cim_export( - import_result, "Bus_Branch_Converted", "cgmes_v2_4_15", activeProfileList -) +cimpy.cim_export(import_result, "Bus_Branch_Converted", "cgmes_v2_4_15", activeProfileList) diff --git a/cimpy/examples/exportCIGREMV.py b/cimpy/examples/exportCIGREMV.py index 777e4e1f..d2452b22 100644 --- a/cimpy/examples/exportCIGREMV.py +++ b/cimpy/examples/exportCIGREMV.py @@ -22,6 +22,4 @@ activeProfileList = ["DL", "EQ", "SV", "TP"] -cimpy.cim_export( - import_result, "CIGREMV_reference_cgmes_v2_4_15", "cgmes_v2_4_15", activeProfileList -) +cimpy.cim_export(import_result, "CIGREMV_reference_cgmes_v2_4_15", "cgmes_v2_4_15", activeProfileList) diff --git a/cimpy/utils.py b/cimpy/utils.py index 38cfb82e..b7a39d89 100644 --- a/cimpy/utils.py +++ b/cimpy/utils.py @@ -63,12 +63,8 @@ def node_breaker_to_bus_branch(import_result): continue if "ConnectivityNode" in str(type(res[diagram_object].IdentifiedObject)): if res[diagram_object].IdentifiedObject.TopologicalNode is not None: - keep_diagram.append( - res[diagram_object].IdentifiedObject.TopologicalNode - ) - elif res[diagram_object].IdentifiedObject.mRID in ( - open_breakers + del_terminals_list - ): + keep_diagram.append(res[diagram_object].IdentifiedObject.TopologicalNode) + elif res[diagram_object].IdentifiedObject.mRID in (open_breakers + del_terminals_list): del_diagram_object.append(diagram_object) del_diagram_object_points = [] @@ -119,16 +115,10 @@ def add_external_network_injection(import_result, version, mRID, voltage_set_poi terminal_module = importlib.import_module((module_name + "Terminal")) terminal_class = getattr(terminal_module, "Terminal") - res[terminal_name] = terminal_class( - mRID=terminal_name, name=terminal_name, TopologicalNode=TopologicalNode - ) + res[terminal_name] = terminal_class(mRID=terminal_name, name=terminal_name, TopologicalNode=TopologicalNode) - regulating_control_module = importlib.import_module( - module_name + "RegulatingControl" - ) - regulating_control_class = getattr( - regulating_control_module, "RegulatingControl" - ) + regulating_control_module = importlib.import_module(module_name + "RegulatingControl") + regulating_control_class = getattr(regulating_control_module, "RegulatingControl") res[reg_name] = regulating_control_class( mRID=reg_name, name=reg_name, @@ -136,12 +126,8 @@ def add_external_network_injection(import_result, version, mRID, voltage_set_poi Terminal=res[terminal_name], ) - network_injection_module = importlib.import_module( - module_name + "ExternalNetworkInjection" - ) - network_injection_class = getattr( - network_injection_module, "ExternalNetworkInjection" - ) + network_injection_module = importlib.import_module(module_name + "ExternalNetworkInjection") + network_injection_class = getattr(network_injection_module, "ExternalNetworkInjection") res[inj_name] = network_injection_class( mRID=inj_name, name=inj_name, diff --git a/documentation/set_inheritance_diagram.py b/documentation/set_inheritance_diagram.py index dd4d8be5..4976f9e2 100644 --- a/documentation/set_inheritance_diagram.py +++ b/documentation/set_inheritance_diagram.py @@ -1,7 +1,7 @@ from os import remove import os from tempfile import mkstemp -from shutil import move, copy +from shutil import move directory = os.path.abspath(os.path.join("..", "documentation")) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..5194238d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.black] +line-length = 120 diff --git a/tests/create_pickle_dump.py b/tests/create_pickle_dump.py index 04253735..89ec43a6 100644 --- a/tests/create_pickle_dump.py +++ b/tests/create_pickle_dump.py @@ -1,7 +1,4 @@ import pickle -import sys - -sys.path.append("/home/richard/cimpy") from pathlib import Path import cimpy @@ -19,9 +16,7 @@ def create_pickle(): imported_result = cimpy.cim_import(test_files, "cgmes_v2_4_15") - CGMES_object = cimpy.cimexport._get_class_attributes_with_references( - imported_result, "cgmes_v2_4_15" - ) + CGMES_object = cimpy.cimexport._get_class_attributes_with_references(imported_result, "cgmes_v2_4_15") pickle.dump(CGMES_object, open("CIGREMV_import_reference_cgmes_v2_4_15.p1", "wb")) diff --git a/tests/test_export.py b/tests/test_export.py index b3485fab..078294ff 100644 --- a/tests/test_export.py +++ b/tests/test_export.py @@ -3,22 +3,17 @@ from cimpy.cgmes_v2_4_15.Base import short_profile_name import xmltodict import os -import glob import pytest_check as check from pathlib import Path import pytest -logging.basicConfig( - filename="Test_export_with_exported_files.log", level=logging.INFO, filemode="w" -) +logging.basicConfig(filename="Test_export_with_exported_files.log", level=logging.INFO, filemode="w") @pytest.fixture def sample_cimdata(): """Import the sampledata using cimpy""" - example_dir = Path( - os.path.join(os.path.dirname(__file__), "../cimpy/examples/sampledata/CIGRE_MV") - ).resolve() + example_dir = Path(os.path.join(os.path.dirname(__file__), "../cimpy/examples/sampledata/CIGRE_MV")).resolve() import_files = [] for file in example_dir.glob("*.xml"): import_files.append(str(file.absolute())) @@ -33,9 +28,7 @@ def read_ref_xml(): for file in test_dir.glob("CIGREMV_reference*.xml"): xmlstring = open(file, encoding="utf8").read() - parsed_export_file = xmltodict.parse( - xmlstring, attr_prefix="$", cdata_key="_", dict_constructor=dict - ) + parsed_export_file = xmltodict.parse(xmlstring, attr_prefix="$", cdata_key="_", dict_constructor=dict) test_list.append(parsed_export_file["rdf:RDF"]) test_dict = {} @@ -54,9 +47,7 @@ def read_exported_xml(directory): for file in test_dir.glob("*EXPORTED*.xml"): xmlstring = open(file, encoding="utf8").read() - parsed_export_file = xmltodict.parse( - xmlstring, attr_prefix="$", cdata_key="_", dict_constructor=dict - ) + parsed_export_file = xmltodict.parse(xmlstring, attr_prefix="$", cdata_key="_", dict_constructor=dict) export_list.append(parsed_export_file["rdf:RDF"]) export_dict = {} @@ -75,9 +66,7 @@ def read_exported_xml(directory): def test_export_with_exported_files(sample_cimdata, tmpdir): activeProfileList = ["DL", "EQ", "SV", "TP"] - cimpy.cim_export( - sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", activeProfileList - ) + cimpy.cim_export(sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", activeProfileList) test_dict = read_ref_xml() export_dict = read_exported_xml(tmpdir) @@ -103,9 +92,7 @@ def test_export_with_exported_files(sample_cimdata, tmpdir): def test_export_with_imported_files(sample_cimdata, tmpdir): activeProfileList = ["DL", "EQ", "SSH", "SV", "TP"] - cimpy.cim_export( - sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", activeProfileList - ) + cimpy.cim_export(sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", activeProfileList) test_dict = read_ref_xml() export_dict = read_exported_xml(tmpdir) @@ -138,9 +125,7 @@ def test_export_with_imported_files(sample_cimdata, tmpdir): else: try: test_mRIDs.append(current_test_class["$rdf:ID"]) - test_class_dict[ - current_test_class["$rdf:ID"] - ] = current_test_class + test_class_dict[current_test_class["$rdf:ID"]] = current_test_class except KeyError: try: test_mRIDs.append(current_test_class["$rdf:about"]) @@ -166,15 +151,11 @@ def test_export_with_imported_files(sample_cimdata, tmpdir): else: try: export_mRIDs.append(current_export_class["$rdf:ID"]) - export_class_dict[ - current_export_class["$rdf:ID"] - ] = current_export_class + export_class_dict[current_export_class["$rdf:ID"]] = current_export_class except KeyError: try: export_mRIDs.append(current_export_class["$rdf:about"]) - export_class_dict[ - current_export_class["$rdf:about"] - ] = obj + export_class_dict[current_export_class["$rdf:about"]] = obj except KeyError: check.is_in("$rdf:about", current_export_class.keys()) check.is_in("$rdf:ID", current_export_class.keys()) @@ -204,14 +185,10 @@ def test_export_with_imported_files(sample_cimdata, tmpdir): "false", "None", "list", - { - "$rdf:resource": "#_32d6d32e-c3f0-43d4-8103-079a15594fc6" - }, + {"$rdf:resource": "#_32d6d32e-c3f0-43d4-8103-079a15594fc6"}, ]: continue - if isinstance(item[1], dict) or isinstance( - item[1], list - ): + if isinstance(item[1], dict) or isinstance(item[1], list): test_item = item elif len(item[1].split(".")) > 1: try: diff --git a/tests/test_import.py b/tests/test_import.py index af36bbad..32908477 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -1,21 +1,18 @@ import logging import cimpy -from cimpy.cgmes_v2_4_15.Base import short_profile_name import os -import glob import pytest_check as check import pickle from pathlib import Path logging.basicConfig(filename="Test_import.log", level=logging.INFO, filemode="w") -example_dir = Path( - os.path.join(os.path.dirname(__file__), "../cimpy/examples/sampledata/CIGRE_MV") -).resolve() +example_dir = Path(os.path.join(os.path.dirname(__file__), "../cimpy/examples/sampledata/CIGRE_MV")).resolve() def test_import(): - """This function tests the import functionality by importing files and comparing them to previously imported and pickled files.""" + """This function tests the import functionality by importing files and comparing them to previously + imported and pickled files.""" global example_dir test_files = [] @@ -24,15 +21,9 @@ def test_import(): imported_result = cimpy.cim_import(test_files, "cgmes_v2_4_15") - import_resolved = cimpy.cimexport._get_class_attributes_with_references( - imported_result, "cgmes_v2_4_15" - ) + import_resolved = cimpy.cimexport._get_class_attributes_with_references(imported_result, "cgmes_v2_4_15") - ref_dict_path = Path( - os.path.join( - os.path.dirname(__file__), "CIGREMV_import_reference_cgmes_v2_4_15.p" - ) - ) + ref_dict_path = Path(os.path.join(os.path.dirname(__file__), "CIGREMV_import_reference_cgmes_v2_4_15.p")) check_dict_pickle = pickle.load(open(ref_dict_path, "rb")) for elem in import_resolved: From 824caa64da76958571d6c3d1f71782d05972d973 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 16:11:38 +0200 Subject: [PATCH 08/17] Make all filenames PEP-8 compliant Signed-off-by: Steffen Vogel --- cimpy/__init__.py | 4 ++-- cimpy/cimexamples.py | 12 ++++++------ ...njection.py => add_external_network_injection.py} | 2 +- ...onvertToBusBranch.py => convert_to_bus_branch.py} | 0 .../{exportCIGREMV.py => export_cigre_mv.py} | 0 .../{importCIGREMV.py => import_cigre_mv.py} | 0 6 files changed, 9 insertions(+), 9 deletions(-) rename cimpy/examples/{addExternalNetworkInjection.py => add_external_network_injection.py} (89%) rename cimpy/examples/{convertToBusBranch.py => convert_to_bus_branch.py} (100%) rename cimpy/examples/{exportCIGREMV.py => export_cigre_mv.py} (100%) rename cimpy/examples/{importCIGREMV.py => import_cigre_mv.py} (100%) diff --git a/cimpy/__init__.py b/cimpy/__init__.py index bfaddfee..061e9d82 100644 --- a/cimpy/__init__.py +++ b/cimpy/__init__.py @@ -5,5 +5,5 @@ import cimpy.utils from cimpy.cimexamples import import_example from cimpy.cimexamples import export_example -from cimpy.cimexamples import addExternalNetworkInjection_example -from cimpy.cimexamples import convertToBusBranch_example +from cimpy.cimexamples import add_external_network_injection_example +from cimpy.cimexamples import convert_to_bus_branch_example diff --git a/cimpy/cimexamples.py b/cimpy/cimexamples.py index b8fe1980..8c048840 100644 --- a/cimpy/cimexamples.py +++ b/cimpy/cimexamples.py @@ -4,26 +4,26 @@ def import_example(): """TODO: Add documentation""" base = Path(__file__).resolve().parent - example = base / "examples" / "importCIGREMV.py" + example = base / "examples" / "import_cigre_mv.py" exec(open(example).read()) def export_example(): """TODO: Add documentation""" base = Path(__file__).resolve().parent - example = base / "examples" / "exportCIGREMV.py" + example = base / "examples" / "export_cigre_mv.py" exec(open(example).read()) -def convertToBusBranch_example(): +def convert_to_bus_branch_example(): """TODO: Add documentation""" base = Path(__file__).resolve().parent - example = base / "examples" / "convertToBusBranch.py" + example = base / "examples" / "convert_to_bus_branch.py" exec(open(example).read()) -def addExternalNetworkInjection_example(): +def add_external_network_injection_example(): """TODO: Add documentation""" base = Path(__file__).resolve().parent - example = base / "examples" / "addExternalNetworkInjection.py" + example = base / "examples" / "add_external_network_injection.py" exec(open(example).read()) diff --git a/cimpy/examples/addExternalNetworkInjection.py b/cimpy/examples/add_external_network_injection.py similarity index 89% rename from cimpy/examples/addExternalNetworkInjection.py rename to cimpy/examples/add_external_network_injection.py index 5402c892..a918fa8a 100644 --- a/cimpy/examples/addExternalNetworkInjection.py +++ b/cimpy/examples/add_external_network_injection.py @@ -6,7 +6,7 @@ example = Path(__file__).resolve().parent -# called as cimpy.examples.addExternalNetworkInjection() or file run from quickstart directory? +# called as cimpy.examples.convert_to_bus_branch() or file run from quickstart directory? if "cimexamples.py" in str(__file__): sample_folder = example / "examples" / "sampledata" / "CIGRE_MV" else: diff --git a/cimpy/examples/convertToBusBranch.py b/cimpy/examples/convert_to_bus_branch.py similarity index 100% rename from cimpy/examples/convertToBusBranch.py rename to cimpy/examples/convert_to_bus_branch.py diff --git a/cimpy/examples/exportCIGREMV.py b/cimpy/examples/export_cigre_mv.py similarity index 100% rename from cimpy/examples/exportCIGREMV.py rename to cimpy/examples/export_cigre_mv.py diff --git a/cimpy/examples/importCIGREMV.py b/cimpy/examples/import_cigre_mv.py similarity index 100% rename from cimpy/examples/importCIGREMV.py rename to cimpy/examples/import_cigre_mv.py From 9a99426d75fba414a2964ade318118c9fe6a6e18 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 16:36:17 +0200 Subject: [PATCH 09/17] Update Python packaging files to use new best practices Signed-off-by: Steffen Vogel --- documentation/Install.rst | 17 ++++++++++--- pyproject.toml | 52 ++++++++++++++++++++++++++++++++++++++- setup.py | 16 ------------ 3 files changed, 64 insertions(+), 21 deletions(-) delete mode 100644 setup.py diff --git a/documentation/Install.rst b/documentation/Install.rst index bba0ea89..686a91e6 100644 --- a/documentation/Install.rst +++ b/documentation/Install.rst @@ -9,13 +9,13 @@ User Installation $ git clone https://github.com/sogno-platform/cimpy.git $ cd cimpy - $ python setup.py install + $ python3 -m pip install . or .. code-block:: bash - $ pip install cimpy + $ python3 -m pip install cimpy Developer Installation ---------------------- @@ -24,10 +24,19 @@ Developer Installation $ git clone https://github.com/sogno-platform/cimpy.git $ cd cimpy - $ python setup.py develop + $ python3 -m pip install -e .[dev] or .. code-block:: bash - $ pip install --pre cimpy + $ python3 -m pip install --pre cimpy + + +Building a distributable package +-------------------------------- + +.. code-block:: bash + + $ python3 -m pip install --upgrade build + $ python3 -m build diff --git a/pyproject.toml b/pyproject.toml index 5194238d..59805049 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,52 @@ -[tool.black] +[project] +name = "cimpy" +version = "1.0.2" +description = "Python package for import, modification and export of CIM grid data" +authors = [ + {name="Institute for Automation of Complex Power Systems"}, + {name="OPAL-RT Technologies"} +] +readme = "README.md" +keywords = ["cim", "cgmes"] + +dependencies = [ + "lxml", + "xmltodict", + "chevron", +] + +requires-python = ">=3.8" + +classifiers = [ + "Development Status :: 5 - Production/Stable", + + "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", + + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11" +] + +[project.urls] +Homepage = "https://sogno.energy/cimpy/" +Documentation = "https://sogno.energy/cimpy/" +Repository = "https://github.com/sogno-platform/cimpy" +Issues = "https://github.com/sogno-platform/cimpy/issues" + +[project.optional-dependencies] +dev = [ + "pytest", + "pytest-check" +] + +[build-system] +requires = ["setuptools >= 61.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +include-package-data = true + + +[tool.black] line-length = 120 diff --git a/setup.py b/setup.py deleted file mode 100644 index 4ac7ea4e..00000000 --- a/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -import setuptools - -setuptools.setup( - name="cimpy", - version="1.0.2", - description="Python package for import, modification and export of CIM grid data", - author="Institute for Automation of Complex Power Systems", - include_package_data=True, - license="MPL-2.0", - packages=setuptools.find_packages(), - install_requires=[ - "lxml", - "xmltodict", - "chevron", - ], -) From f37a21768d5f9f11a62e85e4604fef6b94068c23 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 16:42:15 +0200 Subject: [PATCH 10/17] Replace print with logging package Signed-off-by: Steffen Vogel --- cimpy/cimexport.py | 19 +++++++------------ cimpy/cimimport.py | 11 ++--------- cimpy/utils.py | 5 ++++- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/cimpy/cimexport.py b/cimpy/cimexport.py index 14d0c761..e6e980fd 100644 --- a/cimpy/cimexport.py +++ b/cimpy/cimexport.py @@ -1,15 +1,15 @@ -import os -import importlib -import chevron from datetime import datetime +from pathlib import Path from time import time -from cimpy.cgmes_v2_4_15.Base import Profile +import chevron +import copy +import importlib import logging -import sys +import os + +from cimpy.cgmes_v2_4_15.Base import Profile from cimpy.cgmes_v2_4_15.Base import Base -from pathlib import Path -import copy cgmesProfile = Base.cgmesProfile @@ -372,11 +372,6 @@ def cim_export(import_result, file_name, version, activeProfileList): "File {} already exists. Delete file or change file name to serialize CGMES " "classes.".format(full_file_name) ) - print( - "[ERROR:] File {} already exists. Delete file or change file name to serialize CGMES " - "classes.".format(full_file_name), - file=sys.stderr, - ) exit(-1) logger.info("End export procedure. Elapsed time: {}".format(time() - t0)) diff --git a/cimpy/cimimport.py b/cimpy/cimimport.py index 7cd89f3f..4446144a 100644 --- a/cimpy/cimimport.py +++ b/cimpy/cimimport.py @@ -62,21 +62,14 @@ def cim_import(xml_files, cgmes_version, start_dict=None): if logger_grouped["errors"]: for error, count in logger_grouped["errors"].items(): - logging_message = "{} : {} times".format(error, count) - logger.warning(logging_message) + logger.warning("{} : {} times".format(error, count)) if logger_grouped["info"]: for info, count in logger_grouped["info"].items(): - logging_message = "{} : {} times".format(info, count) - logger.info(logging_message) - - # print info which classes and how many were instantiated - print(logging_message) + logger.info("{} : {} times".format(info, count)) elapsed_time = time() - t0 logger.info("Created totally {} CIM objects in {}s\n\n".format(len(import_result["topology"]), elapsed_time)) - # print info of how many classes in total were instantiated to terminal - print("Created totally {} CIM objects in {}s".format(len(import_result["topology"]), elapsed_time)) return import_result diff --git a/cimpy/utils.py b/cimpy/utils.py index b7a39d89..60502b9c 100644 --- a/cimpy/utils.py +++ b/cimpy/utils.py @@ -1,5 +1,8 @@ +import logging import importlib +logger = logging.getLogger(__name__) + def node_breaker_to_bus_branch(import_result): """TODO: Add documentation""" @@ -139,7 +142,7 @@ def add_external_network_injection(import_result, version, mRID, voltage_set_poi res[terminal_name].ConductingEquipment = res[inj_name] res[terminal_name].RegulatingControl = res[reg_name] else: - print("No Terminal with mRID ", mRID, " found in object list!") + logger.warning("No Terminal with mRID %s found in object list!", mRID) import_result["topology"] = res From 4f63a10ef2fbb86fae14f959381ef2ad364f4a64 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 16:51:14 +0200 Subject: [PATCH 11/17] Use proper printf-style formatting for logging Signed-off-by: Steffen Vogel --- cimpy/cimexport.py | 66 +++++++++++++++++++++++----------------------- cimpy/cimimport.py | 10 +++---- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/cimpy/cimexport.py b/cimpy/cimexport.py index e6e980fd..8fe9daf7 100644 --- a/cimpy/cimexport.py +++ b/cimpy/cimexport.py @@ -60,16 +60,16 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): UUID = "%" + _search_mRID(elem, topology) if UUID == "%": logger.warning( - "Object of type {} not found as reference for object with UUID {}.".format( - elem.__class__.__name__, mRID - ) + "Object of type %s not found as reference for object with UUID %s.", + elem.__class__.__name__, + mRID, ) else: UUID = "%" + elem.mRID array.append(UUID) else: - logger.warning("Reference object not subclass of Base class for object with UUID {}.".format(mRID)) + logger.warning("Reference object not subclass of Base class for object with UUID %s.", mRID) if len(array) == 1: attributes["value"] = array[0] else: @@ -82,9 +82,9 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): UUID = "%" + _search_mRID(attr_dict[key], topology) if UUID == "%": logger.warning( - "Object of type {} not found as reference for object with UUID {}.".format( - attr_dict[key].__class__.__name__, mRID - ) + "Object of type %s not found as reference for object with UUID %s.", + attr_dict[key].__class__.__name__, + mRID, ) else: UUID = "%" + attr_dict[key].mRID @@ -99,9 +99,7 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): attributes["value"] = "%URL%" + urls[key.split(".")[1]][attr_dict[key]] else: logger.warning( - "URL reference for attribute {} and value {} not found!".format( - key.split(".")[1], attr_dict[key] - ) + "URL reference for attribute %s and value %s not found!", key.split(".")[1], attr_dict[key] ) else: attributes["value"] = attr_dict[key] @@ -203,14 +201,16 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): class_serializationProfile = serializationProfile["class"] else: logger.warning( - "Class {} was read from profile {} but this profile is not possible for this class".format( - klass["name"], serializationProfile["class"] - ) + "Class %s was read from profile %s but this profile is not possible for this class", + klass["name"], + serializationProfile["class"], ) else: logger.info( - "Class {} was read from profile {} but this profile is not active for the export. Use" - "default profile from possibleProfileList.".format(klass["name"], serializationProfile["class"]) + "Class %s was read from profile %s but this profile is not active for the export. " + + "Use default profile from possibleProfileList.", + klass["name"], + serializationProfile["class"], ) if class_serializationProfile == "": @@ -226,15 +226,14 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): if class_serializationProfile == "": # no profile in possibleProfileList active logger.warning( - "All possible export profiles for class {} not active. Skip class for export.".format( - klass["name"] - ) + "All possible export profiles for class %s not active. Skip class for export.", + klass["name"], ) continue else: - logger.warning("Class {} has no profile to export to.".format(klass["name"])) + logger.warning("Class %s has no profile to export to.", klass["name"]) else: - logger.warning("Class {} has no profile to export to.".format(klass["name"])) + logger.warning("Class %s has no profile to export to.", klass["name"]) # iterate over attributes for attribute in klass["attributes"]: @@ -268,23 +267,25 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): if attribute_serializationProfile == "": # no profile in possibleProfileList active, skip attribute logger.warning( - "All possible export profiles for attribute {}.{} of class {} " - "not active. Skip attribute for export.".format( - attribute_class, attribute_name, klass["name"] - ) + "All possible export profiles for attribute %s.%s of class %s not active. " + + "Skip attribute for export.", + attribute_class, + attribute_name, + klass["name"], ) continue else: logger.warning( - "Attribute {}.{} of class {} has no profile to export to.".format( - attribute_class, attribute_name, klass["name"] - ) + "Attribute %s.%s of class %s has no profile to export to.", + attribute_class, + attribute_name, + klass["name"], ) else: logger.warning( - "The class {} for attribute {} is not in the possibleProfileList".format( - attribute_class, attribute_name - ) + "The class %s for attribute %s is not in the possibleProfileList", + attribute_class, + attribute_name, ) if attribute_serializationProfile == class_serializationProfile: @@ -369,12 +370,11 @@ def cim_export(import_result, file_name, version, activeProfileList): file.write(output) else: logger.error( - "File {} already exists. Delete file or change file name to serialize CGMES " - "classes.".format(full_file_name) + "File %s already exists. Delete file or change file name to serialize CGMES classes.", full_file_name ) exit(-1) - logger.info("End export procedure. Elapsed time: {}".format(time() - t0)) + logger.info("End export procedure. Elapsed time: %s", time() - t0) def generate_xml(cim_data, version, model_name, profile, available_profiles): diff --git a/cimpy/cimimport.py b/cimpy/cimimport.py index 4446144a..124676eb 100644 --- a/cimpy/cimimport.py +++ b/cimpy/cimimport.py @@ -62,14 +62,14 @@ def cim_import(xml_files, cgmes_version, start_dict=None): if logger_grouped["errors"]: for error, count in logger_grouped["errors"].items(): - logger.warning("{} : {} times".format(error, count)) + logger.warning("%s: %d times", error, count) if logger_grouped["info"]: for info, count in logger_grouped["info"].items(): - logger.info("{} : {} times".format(info, count)) + logger.info("%s: %d times", info, count) elapsed_time = time() - t0 - logger.info("Created totally {} CIM objects in {}s\n\n".format(len(import_result["topology"]), elapsed_time)) + logger.info("Created totally %s CIM objects in %.2f s\n\n", len(import_result["topology"]), elapsed_time) return import_result @@ -383,7 +383,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Clear children of the root element to minimise memory usage. root.clear() - logger.info('END of parsing file "{}"'.format(xml_file)) + logger.info('END of parsing file "{}"', xml_file) return import_result, logger_grouped @@ -411,7 +411,7 @@ def _get_rdf_namespace(namespaces): namespace = namespaces["rdf"] except KeyError: ns = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" - logger.warning("No rdf namespace found. Using %s" % ns) + logger.warning("No rdf namespace found. Using %s", ns) return namespace From 8feb082e7000b3831ed27093d2372acbc31e3642 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 16:58:10 +0200 Subject: [PATCH 12/17] Fix capitalization of comments Signed-off-by: Steffen Vogel --- .github/workflows/pytest.yaml | 70 ++++++++-------- cimpy/cimexport.py | 80 +++++++++---------- cimpy/cimimport.py | 43 +++++----- .../add_external_network_injection.py | 2 +- cimpy/examples/convert_to_bus_branch.py | 2 +- cimpy/examples/export_cigre_mv.py | 2 +- cimpy/examples/import_cigre_mv.py | 2 +- cimpy/utils.py | 6 +- 8 files changed, 105 insertions(+), 102 deletions(-) diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 8f26e687..45c8bdad 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -1,38 +1,38 @@ name: Pytest on: push jobs: - test: - runs-on: ubuntu-latest - steps: - - name: checkout repo content - uses: actions/checkout@v3 # checkout the repository content to github runner. - - name: setup python - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Install graphviz - run: sudo apt install graphviz - shell: bash - - name: Install dependencies - run: | - sudo pip3 install --upgrade pip - sudo pip3 install sphinx_rtd_theme - sudo pip3 install sphinx - sudo pip3 install pytest - sudo pip3 install pytest-check - - name: Execute py script - env: - working-directory: ${{runner.workspace}}/cimpy - run: | - sudo python3 setup.py install - - name: Pytest - env: - working-directory: ${{runner.workspace}}/cimpy - run: | - sudo pytest -v -cov --junitxml=report.xml - - name: Upload pytest test results - uses: actions/upload-artifact@v3 - with: - name: pytest-results - path: ${{runner.workspace}}/cimpy/report.xml - if: ${{ always() }} + test: + runs-on: ubuntu-latest + steps: + - name: checkout repo content + uses: actions/checkout@v3 # Checkout the repository content to github runner. + - name: setup python + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Install graphviz + run: sudo apt install graphviz + shell: bash + - name: Install dependencies + run: | + sudo pip3 install --upgrade pip + sudo pip3 install sphinx_rtd_theme + sudo pip3 install sphinx + sudo pip3 install pytest + sudo pip3 install pytest-check + - name: Execute py script + env: + working-directory: ${{runner.workspace}}/cimpy + run: | + sudo python3 setup.py install + - name: Pytest + env: + working-directory: ${{runner.workspace}}/cimpy + run: | + sudo pytest -v -cov --junitxml=report.xml + - name: Upload pytest test results + uses: actions/upload-artifact@v3 + with: + name: pytest-results + path: ${{runner.workspace}}/cimpy/report.xml + if: ${{ always() }} diff --git a/cimpy/cimexport.py b/cimpy/cimexport.py index 8fe9daf7..50890ce3 100644 --- a/cimpy/cimexport.py +++ b/cimpy/cimexport.py @@ -19,15 +19,15 @@ # This function gets all attributes of an object and resolves references to other objects def _get_class_attributes_with_references(import_result, version): class_attributes_list = [] - # extract topology and urls + # Extract topology and urls topology = import_result["topology"] urls = import_result["meta_info"]["urls"] for key in topology.keys(): class_dict = dict(name=topology[key].__class__.__name__) class_dict["mRID"] = key - # array containing all attributes, attribute references to objects + # Array containing all attributes, attribute references to objects attributes_dict = _get_attributes(topology[key]) - # change attribute references to mRID of the object, res needed because classes like SvPowerFlow does not have + # Change attribute references to mRID of the object, res needed because classes like SvPowerFlow does not have # mRID as an attribute. Therefore the corresponding class has to be searched in the res dictionary class_dict["attributes"] = _get_reference_uuid(attributes_dict, version, topology, key, urls) class_attributes_list.append(class_dict) @@ -48,15 +48,15 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): continue attributes = {} - if isinstance(attr_dict[key], list): # many + if isinstance(attr_dict[key], list): # Many array = [] for elem in attr_dict[key]: if issubclass(type(elem), base_class): - # classes like SvVoltage does not have an attribute called mRID, the mRID is only stored as a key + # Classes like SvVoltage does not have an attribute called mRID, the mRID is only stored as a key # for this object in the res dictionary # The % added before the mRID is used in the lambda _set_attribute_or_reference if not hasattr(elem, "mRID"): - # search for the object in the res dictionary and return the mRID + # Search for the object in the res dictionary and return the mRID UUID = "%" + _search_mRID(elem, topology) if UUID == "%": logger.warning( @@ -77,7 +77,7 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): elif issubclass(type(attr_dict[key]), base_class): # 0..1, 1..1 # resource = key + ' rdf:resource=' if not hasattr(attr_dict[key], "mRID"): - # search for object in res dict and return mRID + # Search for object in res dict and return mRID # The % added before the mRID is used in the lambda _set_attribute_or_reference UUID = "%" + _search_mRID(attr_dict[key], topology) if UUID == "%": @@ -92,9 +92,9 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): elif attr_dict[key] == "" or attr_dict[key] is None: pass else: - # attribute in urls dict? + # Attribute in urls dict? if key.split(".")[1] in urls.keys(): - # value in urls dict? should always be true + # Value in urls dict? should always be true if attr_dict[key] in urls[key.split(".")[1]].keys(): attributes["value"] = "%URL%" + urls[key.split(".")[1]][attr_dict[key]] else: @@ -108,10 +108,10 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): if "value" in attributes.keys(): if isinstance(attributes["value"], list): for reference_item in attributes["value"]: - # ignore default values + # Ignore default values if reference_item not in ["", None, 0.0, 0]: reference_list.append({"value": reference_item, "attr_name": key}) - # ignore default values + # Ignore default values elif attributes["value"] not in ["", None, 0.0, 0, "list"]: reference_list.append(attributes) @@ -177,12 +177,12 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): export_dict = {} export_about_dict = {} - # iterate over classes + # Iterate over classes for klass in class_attributes_list: same_package_list = [] about_dict = {} - # store serializationProfile and possibleProfileList + # Store serializationProfile and possibleProfileList # serializationProfile class attribute, same for multiple instances # of same class, only last origin of variable stored serializationProfile = copy.deepcopy(klass["attributes"][0]["serializationProfile"]) @@ -191,12 +191,12 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): class_serializationProfile = "" if "class" in serializationProfile.keys(): - # class was imported + # Class was imported if Profile[serializationProfile["class"]] in activeProfileList: - # else: class origin profile not active for export, get active profile from possibleProfileList + # Else: class origin profile not active for export, get active profile from possibleProfileList if Profile[serializationProfile["class"]].value in possibleProfileList[klass["name"]]["class"]: - # profile active and in possibleProfileList - # else: class should not have been imported from this profile, get allowed profile + # Profile active and in possibleProfileList + # Else: class should not have been imported from this profile, get allowed profile # from possibleProfileList class_serializationProfile = serializationProfile["class"] else: @@ -214,17 +214,17 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): ) if class_serializationProfile == "": - # class was created + # Class was created if klass["name"] in possibleProfileList.keys(): if "class" in possibleProfileList[klass["name"]].keys(): possibleProfileList[klass["name"]]["class"].sort() for klass_profile in possibleProfileList[klass["name"]]["class"]: if Profile(klass_profile).name in activeProfileList: - # active profile for class export found + # Active profile for class export found class_serializationProfile = Profile(klass_profile).name break if class_serializationProfile == "": - # no profile in possibleProfileList active + # No profile in possibleProfileList active logger.warning( "All possible export profiles for class %s not active. Skip class for export.", klass["name"], @@ -235,7 +235,7 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): else: logger.warning("Class %s has no profile to export to.", klass["name"]) - # iterate over attributes + # Iterate over attributes for attribute in klass["attributes"]: if "attr_name" in attribute.keys(): attribute_class = attribute["attr_name"].split(".")[0] @@ -248,24 +248,24 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): attribute_serializationProfile = "" if attribute_name in serializationProfile.keys(): - # attribute was imported + # Attribute was imported if Profile[serializationProfile[attribute_name]] in activeProfileList: attr_value = Profile[serializationProfile[attribute_name]].value if attr_value in possibleProfileList[attribute_class][attribute_name]: attribute_serializationProfile = serializationProfile[attribute_name] if attribute_serializationProfile == "": - # attribute was added + # Attribute was added if attribute_class in possibleProfileList.keys(): if attribute_name in possibleProfileList[attribute_class].keys(): possibleProfileList[attribute_class][attribute_name].sort() for attr_profile in possibleProfileList[attribute_class][attribute_name]: if Profile(attr_profile) in activeProfileList: - # active profile for class export found + # Active profile for class export found attribute_serializationProfile = Profile(attr_profile).name break if attribute_serializationProfile == "": - # no profile in possibleProfileList active, skip attribute + # No profile in possibleProfileList active, skip attribute logger.warning( "All possible export profiles for attribute %s.%s of class %s not active. " + "Skip attribute for export.", @@ -289,17 +289,17 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): ) if attribute_serializationProfile == class_serializationProfile: - # class and current attribute belong to same profile + # Class and current attribute belong to same profile same_package_list.append(attribute) else: - # class and current attribute does not belong to same profile -> rdf:about in + # Class and current attribute does not belong to same profile -> rdf:about in # attribute origin profile if attribute_serializationProfile in about_dict.keys(): about_dict[attribute_serializationProfile].append(attribute) else: about_dict[attribute_serializationProfile] = [attribute] - # add class with all attributes in the same profile to the export dict sorted by the profile + # Add class with all attributes in the same profile to the export dict sorted by the profile if class_serializationProfile in export_dict.keys(): export_class = dict(name=klass["name"], mRID=klass["mRID"], attributes=same_package_list) export_dict[class_serializationProfile]["classes"].append(export_class) @@ -308,7 +308,7 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): export_class = dict(name=klass["name"], mRID=klass["mRID"], attributes=same_package_list) export_dict[class_serializationProfile] = {"classes": [export_class]} - # add class with all attributes defined in another profile to the about_key sorted by the profile + # Add class with all attributes defined in another profile to the about_key sorted by the profile for about_key in about_dict.keys(): if about_key in export_about_dict.keys(): export_about_class = dict( @@ -355,7 +355,7 @@ def cim_export(import_result, file_name, version, activeProfileList): profile_list = list(map(lambda a: Profile[a], activeProfileList)) - # iterate over all profiles + # Iterate over all profiles for profile in profile_list: # File name @@ -390,10 +390,10 @@ def generate_xml(cim_data, version, model_name, profile, available_profiles): :param available_profiles: a list of all :class:`~cimpy.cgmes_v2_4_15.Base.Profile`s in `cim_data` """ - # returns all classes with their attributes and resolved references + # Returns all classes with their attributes and resolved references class_attributes_list = _get_class_attributes_with_references(cim_data, version) - # determine class and attribute export profiles. The export dict contains all classes and their attributes where + # Determine class and attribute export profiles. The export dict contains all classes and their attributes where # the class definition and the attribute definitions are in the same profile. Every entry in about_dict generates # a rdf:about in another profile export_dict, about_dict = _sort_classes_to_profile(class_attributes_list, available_profiles) @@ -411,7 +411,7 @@ def generate_xml(cim_data, version, model_name, profile, available_profiles): + "." ) - # extract class lists from export_dict and about_dict + # Extract class lists from export_dict and about_dict if profile.name in export_dict.keys(): classes = export_dict[profile.name]["classes"] else: @@ -457,27 +457,27 @@ def _get_attributes(class_object): class_type = type(class_object) parent = class_object - # get parent classes + # Get parent classes while "Base.Base" not in str(class_type): parent = parent.__class__.__bases__[0]() - # insert parent class at beginning of list, classes inherit from top to bottom + # Insert parent class at beginning of list, classes inherit from top to bottom inheritance_list.insert(0, parent) class_type = type(parent) - # dictionary containing all attributes with key: 'Class_Name.Attribute_Name' + # Dictionary containing all attributes with key: 'Class_Name.Attribute_Name' attributes_dict = dict(serializationProfile=class_object.serializationProfile, possibleProfileList={}) # __dict__ of a subclass returns also the attributes of the parent classes # to avoid multiple attributes create list with all attributes already processed attributes_list = [] - # iterate over parent classes from top to bottom + # Iterate over parent classes from top to bottom for parent_class in inheritance_list: - # get all attributes of the current parent class + # Get all attributes of the current parent class parent_attributes_dict = parent_class.__dict__ class_name = parent_class.__class__.__name__ - # check if new attribute or old attribute + # Check if new attribute or old attribute for key in parent_attributes_dict.keys(): if key not in attributes_list: attributes_list.append(key) @@ -486,7 +486,7 @@ def _get_attributes(class_object): else: continue - # get all possibleProfileLists from all parent classes except the Base class (no attributes) + # Get all possibleProfileLists from all parent classes except the Base class (no attributes) # the serializationProfile from parent classes is not needed because entries in the serializationProfile # are only generated for the inherited class if class_name != "Base": diff --git a/cimpy/cimimport.py b/cimpy/cimimport.py index 124676eb..2a4fc350 100644 --- a/cimpy/cimimport.py +++ b/cimpy/cimimport.py @@ -36,13 +36,13 @@ def cim_import(xml_files, cgmes_version, start_dict=None): # Start the clock. t0 = time() - # map used to group errors and infos + # Map used to group errors and infos logger_grouped = dict(errors={}, info={}) - # create a dict which will contain meta information and the topology + # Create a dict which will contain meta information and the topology import_result = start_dict if start_dict is not None else dict(meta_info={}, topology={}) - # create sub-dictionaries + # Create sub-dictionaries import_result["meta_info"] = dict(namespaces=_get_namespaces(xml_files[0]), urls={}) namespace_rdf = _get_rdf_namespace(import_result["meta_info"]["namespaces"]) @@ -81,12 +81,13 @@ def cim_import(xml_files, cgmes_version, start_dict=None): # Also the information from which package file a class was read is stored in the serializationProfile dictionary. def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace_rdf, base, logger_grouped): - # extract topology from import_result + # Extract topology from import_result topology = import_result["topology"] - # length of element tag base + # Length of element tag base m = len(base) - # first step: create the dict res{uuid}=instance_of_the_cim_class + + # First step: create the dict res{uuid}=instance_of_the_cim_class for xml_file in xml_files: logger.info('START of parsing file "%s"', xml_file) @@ -95,7 +96,7 @@ def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace if hasattr(xml_file, "seek"): xml_file.seek(0) - # get an iterable + # Get an iterable context = etree.iterparse(xml_file, ("start", "end")) # Turn it into an iterator (required for cElementTree). @@ -111,9 +112,9 @@ def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace # Process 'end' elements in the CGMES namespace. if event == "end" and elem.tag[:m] == base: - # check if the element has the attribute "rdf:ID" --> CGMES class located + # Check if the element has the attribute "rdf:ID" --> CGMES class located uuid = elem.get("{%s}ID" % namespace_rdf) - if uuid is not None: # cim class + if uuid is not None: # CIM class # Element tag without namespace (e.g. VoltageLevel). tag = elem.tag[m:] try: @@ -142,7 +143,7 @@ def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace except KeyError: logger_grouped["info"][info_msg] = 1 - # check if the class has the attribute mRID and set the mRID to the read in UUID. If the class + # Check if the class has the attribute mRID and set the mRID to the read in UUID. If the class # does not has this attribute, the UUID is only stored in the res dictionary. if hasattr(topology[uuid], "mRID"): topology[uuid].mRID = uuid @@ -163,10 +164,12 @@ def _instantiate_classes(import_result, xml_files, cgmes_version_path, namespace if package_key in elem.text: package = package_key break - # the author of all imported files should be the same, avoid multiple entries + + # The author of all imported files should be the same, avoid multiple entries elif "author" in import_result["meta_info"].keys(): pass - # extract author + + # Extract author elif "Model.createdBy" in elem.tag: import_result["meta_info"]["author"] = elem.text elif "Model.modelingAuthoritySet" in elem.tag: @@ -191,7 +194,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Second step pass sets attributes and references. for xml_file in xml_files: - # get an iterable and turn it into an iterator (required for cElementTree). + # Get an iterable and turn it into an iterator (required for cElementTree). context = iter(etree.iterparse(xml_file, ("start", "end"))) # Reset stream @@ -292,18 +295,18 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe if default is None: # 1..1 or 0..1 # Rely on properties to set any bi-directional references. setattr(obj, attr, val) - elif default == "list": # many + elif default == "list": # Many setattr(obj, attr, [val]) - elif isinstance(default, list): # many + elif isinstance(default, list): # Many attribute = getattr(obj, attr) if val not in attribute: attribute.append(val) setattr(obj, attr, attribute) elif default == val: - # attribute reference already resolved + # Attribute reference already resolved pass else: - # note here + # Note here error_msg = ( "Multiplicity Error for class {} [{}], attribute {}. ".format( obj.__class__.__name__, uuid, attr @@ -319,9 +322,9 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe default1 = getattr(val, obj.__class__.__name__) if default1 is None: setattr(val, obj.__class__.__name__, obj) - elif default1 == "list": # many + elif default1 == "list": # Many setattr(val, obj.__class__.__name__, [obj]) - elif isinstance(default1, list): # many + elif isinstance(default1, list): # Many attribute2 = getattr(val, obj.__class__.__name__) if obj not in attribute2: attribute2.append(obj) @@ -346,7 +349,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe except KeyError: logger_grouped["errors"][error_msg] = 1 - else: # enum + else: # Enum # if http in uuid2 reference to URL, create mapping if "http" in uuid2: if attr in urls.keys(): diff --git a/cimpy/examples/add_external_network_injection.py b/cimpy/examples/add_external_network_injection.py index a918fa8a..7aaba654 100644 --- a/cimpy/examples/add_external_network_injection.py +++ b/cimpy/examples/add_external_network_injection.py @@ -6,7 +6,7 @@ example = Path(__file__).resolve().parent -# called as cimpy.examples.convert_to_bus_branch() or file run from quickstart directory? +# Called as cimpy.examples.convert_to_bus_branch() or file run from quickstart directory? if "cimexamples.py" in str(__file__): sample_folder = example / "examples" / "sampledata" / "CIGRE_MV" else: diff --git a/cimpy/examples/convert_to_bus_branch.py b/cimpy/examples/convert_to_bus_branch.py index 477a5bfe..edba9fb4 100644 --- a/cimpy/examples/convert_to_bus_branch.py +++ b/cimpy/examples/convert_to_bus_branch.py @@ -6,7 +6,7 @@ example = Path(__file__).resolve().parent -# called as cimpy.examples.convertBusBranch() or file run from quickstart directory? +# Called as cimpy.examples.convertBusBranch() or file run from quickstart directory? if "cimexamples.py" in str(__file__): sample_folder = example / "examples" / "sampledata" / "Sample_Grid_Switches" / "Node-Breaker" else: diff --git a/cimpy/examples/export_cigre_mv.py b/cimpy/examples/export_cigre_mv.py index d2452b22..17e36e40 100644 --- a/cimpy/examples/export_cigre_mv.py +++ b/cimpy/examples/export_cigre_mv.py @@ -6,7 +6,7 @@ example = Path(__file__).resolve().parent -# called as cimpy.examples.import_example() or file run from quickstart directory? +# Called as cimpy.examples.import_example() or file run from quickstart directory? if "cimexamples.py" in str(__file__): sample_folder = example / "examples" / "sampledata" / "CIGRE_MV" else: diff --git a/cimpy/examples/import_cigre_mv.py b/cimpy/examples/import_cigre_mv.py index 2898dfaa..ff3b3af2 100644 --- a/cimpy/examples/import_cigre_mv.py +++ b/cimpy/examples/import_cigre_mv.py @@ -6,7 +6,7 @@ example = Path(__file__).resolve().parent -# called as cimpy.examples.import_example() or file run from quickstart directory? +# Called as cimpy.examples.import_example() or file run from quickstart directory? if "cimexamples.py" in str(__file__): sample_folder = example / "examples" / "sampledata" / "CIGRE_MV" else: diff --git a/cimpy/utils.py b/cimpy/utils.py index 60502b9c..15d1e9d5 100644 --- a/cimpy/utils.py +++ b/cimpy/utils.py @@ -31,14 +31,14 @@ def node_breaker_to_bus_branch(import_result): elif class_name == "ConnectivityNode": connect_nodes.append(mRID) - # search for open breakers + # Search for open breakers open_breakers = [] for breaker in breaker_list: if res[breaker].open: if not res[breaker].retained: open_breakers.append(breaker) - # check terminals for reference to open breakers and delete references to Connectivity Nodes + # Check terminals for reference to open breakers and delete references to Connectivity Nodes del_terminals_list = [] for terminal in terminals_list: cond_eq = res[terminal].ConductingEquipment @@ -47,7 +47,7 @@ def node_breaker_to_bus_branch(import_result): else: res[terminal].ConnectivityNode = None - # check for OperationalLimitSet with references to deleted Terminals + # Check for OperationalLimitSet with references to deleted Terminals del_operationallimitset = [] for operational_limit in operational_limit_set_list: if res[operational_limit].Terminal.mRID in del_terminals_list: From 0e893792b22bb88cdf65ffcd6fe10e1463bf5172 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 17:02:44 +0200 Subject: [PATCH 13/17] Fix camelCase naming of variables Signed-off-by: Steffen Vogel --- cimpy/cimexport.py | 88 +++++++++---------- .../add_external_network_injection.py | 4 +- cimpy/examples/convert_to_bus_branch.py | 4 +- cimpy/examples/export_cigre_mv.py | 4 +- cimpy/utils.py | 10 +-- tests/test_export.py | 34 +++---- 6 files changed, 72 insertions(+), 72 deletions(-) diff --git a/cimpy/cimexport.py b/cimpy/cimexport.py index 50890ce3..1e46a220 100644 --- a/cimpy/cimexport.py +++ b/cimpy/cimexport.py @@ -57,17 +57,17 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): # The % added before the mRID is used in the lambda _set_attribute_or_reference if not hasattr(elem, "mRID"): # Search for the object in the res dictionary and return the mRID - UUID = "%" + _search_mRID(elem, topology) - if UUID == "%": + uuid = "%" + _search_mRID(elem, topology) + if uuid == "%": logger.warning( "Object of type %s not found as reference for object with UUID %s.", elem.__class__.__name__, mRID, ) else: - UUID = "%" + elem.mRID + uuid = "%" + elem.mRID - array.append(UUID) + array.append(uuid) else: logger.warning("Reference object not subclass of Base class for object with UUID %s.", mRID) if len(array) == 1: @@ -79,16 +79,16 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): if not hasattr(attr_dict[key], "mRID"): # Search for object in res dict and return mRID # The % added before the mRID is used in the lambda _set_attribute_or_reference - UUID = "%" + _search_mRID(attr_dict[key], topology) - if UUID == "%": + uuid = "%" + _search_mRID(attr_dict[key], topology) + if uuid == "%": logger.warning( "Object of type %s not found as reference for object with UUID %s.", attr_dict[key].__class__.__name__, mRID, ) else: - UUID = "%" + attr_dict[key].mRID - attributes["value"] = UUID + uuid = "%" + attr_dict[key].mRID + attributes["value"] = uuid elif attr_dict[key] == "" or attr_dict[key] is None: pass else: @@ -185,45 +185,45 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): # Store serializationProfile and possibleProfileList # serializationProfile class attribute, same for multiple instances # of same class, only last origin of variable stored - serializationProfile = copy.deepcopy(klass["attributes"][0]["serializationProfile"]) - possibleProfileList = copy.deepcopy(klass["attributes"][1]["possibleProfileList"]) + serialization_profile = copy.deepcopy(klass["attributes"][0]["serializationProfile"]) + possible_profile_list = copy.deepcopy(klass["attributes"][1]["possibleProfileList"]) - class_serializationProfile = "" + class_serialization_profile = "" - if "class" in serializationProfile.keys(): + if "class" in serialization_profile.keys(): # Class was imported - if Profile[serializationProfile["class"]] in activeProfileList: + if Profile[serialization_profile["class"]] in activeProfileList: # Else: class origin profile not active for export, get active profile from possibleProfileList - if Profile[serializationProfile["class"]].value in possibleProfileList[klass["name"]]["class"]: + if Profile[serialization_profile["class"]].value in possible_profile_list[klass["name"]]["class"]: # Profile active and in possibleProfileList # Else: class should not have been imported from this profile, get allowed profile # from possibleProfileList - class_serializationProfile = serializationProfile["class"] + class_serialization_profile = serialization_profile["class"] else: logger.warning( "Class %s was read from profile %s but this profile is not possible for this class", klass["name"], - serializationProfile["class"], + serialization_profile["class"], ) else: logger.info( "Class %s was read from profile %s but this profile is not active for the export. " + "Use default profile from possibleProfileList.", klass["name"], - serializationProfile["class"], + serialization_profile["class"], ) - if class_serializationProfile == "": + if class_serialization_profile == "": # Class was created - if klass["name"] in possibleProfileList.keys(): - if "class" in possibleProfileList[klass["name"]].keys(): - possibleProfileList[klass["name"]]["class"].sort() - for klass_profile in possibleProfileList[klass["name"]]["class"]: + if klass["name"] in possible_profile_list.keys(): + if "class" in possible_profile_list[klass["name"]].keys(): + possible_profile_list[klass["name"]]["class"].sort() + for klass_profile in possible_profile_list[klass["name"]]["class"]: if Profile(klass_profile).name in activeProfileList: # Active profile for class export found - class_serializationProfile = Profile(klass_profile).name + class_serialization_profile = Profile(klass_profile).name break - if class_serializationProfile == "": + if class_serialization_profile == "": # No profile in possibleProfileList active logger.warning( "All possible export profiles for class %s not active. Skip class for export.", @@ -245,26 +245,26 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): if attribute_name == "mRID": continue - attribute_serializationProfile = "" + attribute_serialization_profile = "" - if attribute_name in serializationProfile.keys(): + if attribute_name in serialization_profile.keys(): # Attribute was imported - if Profile[serializationProfile[attribute_name]] in activeProfileList: - attr_value = Profile[serializationProfile[attribute_name]].value - if attr_value in possibleProfileList[attribute_class][attribute_name]: - attribute_serializationProfile = serializationProfile[attribute_name] + if Profile[serialization_profile[attribute_name]] in activeProfileList: + attr_value = Profile[serialization_profile[attribute_name]].value + if attr_value in possible_profile_list[attribute_class][attribute_name]: + attribute_serialization_profile = serialization_profile[attribute_name] - if attribute_serializationProfile == "": + if attribute_serialization_profile == "": # Attribute was added - if attribute_class in possibleProfileList.keys(): - if attribute_name in possibleProfileList[attribute_class].keys(): - possibleProfileList[attribute_class][attribute_name].sort() - for attr_profile in possibleProfileList[attribute_class][attribute_name]: + if attribute_class in possible_profile_list.keys(): + if attribute_name in possible_profile_list[attribute_class].keys(): + possible_profile_list[attribute_class][attribute_name].sort() + for attr_profile in possible_profile_list[attribute_class][attribute_name]: if Profile(attr_profile) in activeProfileList: # Active profile for class export found - attribute_serializationProfile = Profile(attr_profile).name + attribute_serialization_profile = Profile(attr_profile).name break - if attribute_serializationProfile == "": + if attribute_serialization_profile == "": # No profile in possibleProfileList active, skip attribute logger.warning( "All possible export profiles for attribute %s.%s of class %s not active. " @@ -288,25 +288,25 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList): attribute_name, ) - if attribute_serializationProfile == class_serializationProfile: + if attribute_serialization_profile == class_serialization_profile: # Class and current attribute belong to same profile same_package_list.append(attribute) else: # Class and current attribute does not belong to same profile -> rdf:about in # attribute origin profile - if attribute_serializationProfile in about_dict.keys(): - about_dict[attribute_serializationProfile].append(attribute) + if attribute_serialization_profile in about_dict.keys(): + about_dict[attribute_serialization_profile].append(attribute) else: - about_dict[attribute_serializationProfile] = [attribute] + about_dict[attribute_serialization_profile] = [attribute] # Add class with all attributes in the same profile to the export dict sorted by the profile - if class_serializationProfile in export_dict.keys(): + if class_serialization_profile in export_dict.keys(): export_class = dict(name=klass["name"], mRID=klass["mRID"], attributes=same_package_list) - export_dict[class_serializationProfile]["classes"].append(export_class) + export_dict[class_serialization_profile]["classes"].append(export_class) del export_class else: export_class = dict(name=klass["name"], mRID=klass["mRID"], attributes=same_package_list) - export_dict[class_serializationProfile] = {"classes": [export_class]} + export_dict[class_serialization_profile] = {"classes": [export_class]} # Add class with all attributes defined in another profile to the about_key sorted by the profile for about_key in about_dict.keys(): diff --git a/cimpy/examples/add_external_network_injection.py b/cimpy/examples/add_external_network_injection.py index 7aaba654..ff99188b 100644 --- a/cimpy/examples/add_external_network_injection.py +++ b/cimpy/examples/add_external_network_injection.py @@ -22,6 +22,6 @@ import_result = cimpy.utils.add_external_network_injection(import_result, "cgmes_v2_4_15", "N1", 20.0) -activeProfileList = ["DL", "EQ", "SV", "TP"] +active_profile_list = ["DL", "EQ", "SV", "TP"] -cimpy.cim_export(import_result, "ExternalInjection", "cgmes_v2_4_15", activeProfileList) +cimpy.cim_export(import_result, "ExternalInjection", "cgmes_v2_4_15", active_profile_list) diff --git a/cimpy/examples/convert_to_bus_branch.py b/cimpy/examples/convert_to_bus_branch.py index edba9fb4..d9b9f490 100644 --- a/cimpy/examples/convert_to_bus_branch.py +++ b/cimpy/examples/convert_to_bus_branch.py @@ -23,6 +23,6 @@ import_result = cimpy.utils.node_breaker_to_bus_branch(import_result) -activeProfileList = ["DL", "EQ", "TP"] +active_profile_list = ["DL", "EQ", "TP"] -cimpy.cim_export(import_result, "Bus_Branch_Converted", "cgmes_v2_4_15", activeProfileList) +cimpy.cim_export(import_result, "Bus_Branch_Converted", "cgmes_v2_4_15", active_profile_list) diff --git a/cimpy/examples/export_cigre_mv.py b/cimpy/examples/export_cigre_mv.py index 17e36e40..694e88b8 100644 --- a/cimpy/examples/export_cigre_mv.py +++ b/cimpy/examples/export_cigre_mv.py @@ -20,6 +20,6 @@ import_result = cimpy.cim_import(xml_files, "cgmes_v2_4_15") -activeProfileList = ["DL", "EQ", "SV", "TP"] +active_profile_list = ["DL", "EQ", "SV", "TP"] -cimpy.cim_export(import_result, "CIGREMV_reference_cgmes_v2_4_15", "cgmes_v2_4_15", activeProfileList) +cimpy.cim_export(import_result, "CIGREMV_reference_cgmes_v2_4_15", "cgmes_v2_4_15", active_profile_list) diff --git a/cimpy/utils.py b/cimpy/utils.py index 15d1e9d5..933249be 100644 --- a/cimpy/utils.py +++ b/cimpy/utils.py @@ -98,14 +98,14 @@ def node_breaker_to_bus_branch(import_result): def add_external_network_injection(import_result, version, mRID, voltage_set_point): """TODO: Add documentation""" res = import_result["topology"] - TopologicalNode = "" + topological_node = "" if mRID in res: if "TopologicalNode" in str(type(res[mRID])): - TopologicalNode = res[mRID] + topological_node = res[mRID] elif "ConnectivityNode" in str(type(res[mRID])): - TopologicalNode = res[mRID].TopologicalNode.mRID + topological_node = res[mRID].TopologicalNode.mRID - if TopologicalNode != "": + if topological_node != "": i = 1 while "Injection " + str(i) in res.keys(): i += 1 @@ -118,7 +118,7 @@ def add_external_network_injection(import_result, version, mRID, voltage_set_poi terminal_module = importlib.import_module((module_name + "Terminal")) terminal_class = getattr(terminal_module, "Terminal") - res[terminal_name] = terminal_class(mRID=terminal_name, name=terminal_name, TopologicalNode=TopologicalNode) + res[terminal_name] = terminal_class(mRID=terminal_name, name=terminal_name, TopologicalNode=topological_node) regulating_control_module = importlib.import_module(module_name + "RegulatingControl") regulating_control_class = getattr(regulating_control_module, "RegulatingControl") diff --git a/tests/test_export.py b/tests/test_export.py index 078294ff..25503b40 100644 --- a/tests/test_export.py +++ b/tests/test_export.py @@ -64,9 +64,9 @@ def read_exported_xml(directory): def test_export_with_exported_files(sample_cimdata, tmpdir): - activeProfileList = ["DL", "EQ", "SV", "TP"] + active_profile_list = ["DL", "EQ", "SV", "TP"] - cimpy.cim_export(sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", activeProfileList) + cimpy.cim_export(sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", active_profile_list) test_dict = read_ref_xml() export_dict = read_exported_xml(tmpdir) @@ -90,9 +90,9 @@ def test_export_with_exported_files(sample_cimdata, tmpdir): def test_export_with_imported_files(sample_cimdata, tmpdir): - activeProfileList = ["DL", "EQ", "SSH", "SV", "TP"] + active_profile_list = ["DL", "EQ", "SSH", "SV", "TP"] - cimpy.cim_export(sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", activeProfileList) + cimpy.cim_export(sample_cimdata, tmpdir + "/EXPORTED_Test", "cgmes_v2_4_15", active_profile_list) test_dict = read_ref_xml() export_dict = read_exported_xml(tmpdir) @@ -108,61 +108,61 @@ def test_export_with_imported_files(sample_cimdata, tmpdir): if class_key in current_export_dict.keys(): current_export_class = current_export_dict[class_key] current_test_class = current_test_dict[class_key] - test_mRIDs = [] + test_mrids = [] test_class_dict = {} if isinstance(current_test_class, list): for obj in current_test_class: try: - test_mRIDs.append(obj["$rdf:ID"]) + test_mrids.append(obj["$rdf:ID"]) test_class_dict[obj["$rdf:ID"]] = obj except KeyError: try: - test_mRIDs.append(obj["$rdf:about"]) + test_mrids.append(obj["$rdf:about"]) test_class_dict[obj["$rdf:about"]] = obj except KeyError: check.is_in("$rdf:about", obj.keys()) check.is_in("$rdf:ID", obj.keys()) else: try: - test_mRIDs.append(current_test_class["$rdf:ID"]) + test_mrids.append(current_test_class["$rdf:ID"]) test_class_dict[current_test_class["$rdf:ID"]] = current_test_class except KeyError: try: - test_mRIDs.append(current_test_class["$rdf:about"]) + test_mrids.append(current_test_class["$rdf:about"]) test_class_dict[current_test_class["$rdf:about"]] = obj except KeyError: check.is_in("$rdf:about", current_test_class.keys()) check.is_in("$rdf:ID", current_test_class.keys()) - export_mRIDs = [] + export_mrids = [] export_class_dict = {} if isinstance(current_export_class, list): for obj in current_export_class: try: - export_mRIDs.append(obj["$rdf:ID"]) + export_mrids.append(obj["$rdf:ID"]) export_class_dict[obj["$rdf:ID"]] = obj except KeyError: try: - export_mRIDs.append(obj["$rdf:about"]) + export_mrids.append(obj["$rdf:about"]) export_class_dict[obj["$rdf:about"]] = obj except KeyError: check.is_in("$rdf:about", obj.keys()) check.is_in("$rdf:ID", obj.keys()) else: try: - export_mRIDs.append(current_export_class["$rdf:ID"]) + export_mrids.append(current_export_class["$rdf:ID"]) export_class_dict[current_export_class["$rdf:ID"]] = current_export_class except KeyError: try: - export_mRIDs.append(current_export_class["$rdf:about"]) + export_mrids.append(current_export_class["$rdf:about"]) export_class_dict[current_export_class["$rdf:about"]] = obj except KeyError: check.is_in("$rdf:about", current_export_class.keys()) check.is_in("$rdf:ID", current_export_class.keys()) - for mRID in test_mRIDs: - check.is_in(mRID, export_mRIDs) - if mRID in export_mRIDs: + for mRID in test_mrids: + check.is_in(mRID, export_mrids) + if mRID in export_mrids: test_attr = test_class_dict[mRID].items() export_attr = export_class_dict[mRID].items() for item in test_attr: From a79f8eb9afb09edfdf297520761048b5e8a37423 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 17:07:57 +0200 Subject: [PATCH 14/17] Fix pytest CI workflow Signed-off-by: Steffen Vogel --- .github/workflows/pytest.yaml | 38 --------------------------------- .github/workflows/test.yaml | 40 +++++++++++++++++++++++++++++++++++ pyproject.toml | 5 +++++ 3 files changed, 45 insertions(+), 38 deletions(-) delete mode 100644 .github/workflows/pytest.yaml create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml deleted file mode 100644 index 45c8bdad..00000000 --- a/.github/workflows/pytest.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: Pytest -on: push -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: checkout repo content - uses: actions/checkout@v3 # Checkout the repository content to github runner. - - name: setup python - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Install graphviz - run: sudo apt install graphviz - shell: bash - - name: Install dependencies - run: | - sudo pip3 install --upgrade pip - sudo pip3 install sphinx_rtd_theme - sudo pip3 install sphinx - sudo pip3 install pytest - sudo pip3 install pytest-check - - name: Execute py script - env: - working-directory: ${{runner.workspace}}/cimpy - run: | - sudo python3 setup.py install - - name: Pytest - env: - working-directory: ${{runner.workspace}}/cimpy - run: | - sudo pytest -v -cov --junitxml=report.xml - - name: Upload pytest test results - uses: actions/upload-artifact@v3 - with: - name: pytest-results - path: ${{runner.workspace}}/cimpy/report.xml - if: ${{ always() }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..165a4a75 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,40 @@ +name: Pytest + +on: + push: + branches: + - master + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: 3.8 + + - name: Install Graphviz + shell: bash + run: | + sudo apt-get -y install graphviz + + - name: Install Python dependencies + run: | + pip install .[dev,doc] + + - name: Run pytest + run: | + pytest -v -cov --junitxml=report.xml + + - name: Upload pytest test results + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: pytest-results + path: cimpy/report.xml diff --git a/pyproject.toml b/pyproject.toml index 59805049..dac3e782 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,11 @@ dev = [ "pytest-check" ] +doc = [ + "sphinx", + "sphinx_rtd_theme" +] + [build-system] requires = ["setuptools >= 61.0"] build-backend = "setuptools.build_meta" From 5caeeead29378293dca491e5c42e9f10c5dcd340 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 17:32:52 +0200 Subject: [PATCH 15/17] Simplify CI action for building docs Signed-off-by: Steffen Vogel --- .github/workflows/docs.yaml | 61 +++++++++++++++++++++++++++++++++ .github/workflows/test.yaml | 2 +- .github/workflows/workflow.yaml | 35 ------------------- Dockerfile | 32 ----------------- action.yaml | 7 ---- documentation/docu.sh | 12 ------- entrypoint.sh | 8 ----- 7 files changed, 62 insertions(+), 95 deletions(-) create mode 100644 .github/workflows/docs.yaml delete mode 100644 .github/workflows/workflow.yaml delete mode 100644 Dockerfile delete mode 100644 action.yaml delete mode 100755 documentation/docu.sh delete mode 100755 entrypoint.sh diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 00000000..0833a00a --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,61 @@ +name: Documentation + +on: + pull_request: + + push: + branches: + - master + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.8 + + - name: Install Graphviz + shell: bash + run: | + sudo apt-get -y install graphviz + + - name: Install Python dependencies + run: | + pip install .[doc] + + - name: Build documentation + working-directory: documentation + run: | + sphinx-apidoc \ + --full \ + --doc-project "cimpy" \ + --separate \ + --output-dir "." \ + "../" + + python3 set_inheritance_diagram.py + + make html + + touch _build/html/.nojekyll + + ls -l _build/html + + - name: Upload Artifact + uses: actions/upload-pages-artifact@v3 + with: + path: documentation/_build/html + + - name: Deploy to GitHub Pages + if: github.ref == 'refs/heads/master' + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 165a4a75..6cbc1fc0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -26,7 +26,7 @@ jobs: - name: Install Python dependencies run: | - pip install .[dev,doc] + pip install .[dev] - name: Run pytest run: | diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml deleted file mode 100644 index 17ed7b0f..00000000 --- a/.github/workflows/workflow.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: Documentation - -on: - push: - branches: [ master ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - build-docs: - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - - name: git-checkout - uses: actions/checkout@v3 - - - name: build-documentation - uses: ./ - - - name: Create doc redirect - uses: "finnp/create-file-action@master" - env: - FILE_NAME: ${{ github.workspace }}/built_documentation/.nojekyll - FILE_DATA: "" - - - name: Push - uses: s0/git-publish-subdir-action@develop - env: - REPO: self - BRANCH: gh-pages - FOLDER: ${{ github.workspace }}/built_documentation - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub will automatically add this - you don't need to get a token - MESSAGE: "Build documentation: ({sha}) {msg}" diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 2f66bb5c..00000000 --- a/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM fedora:29 - -LABEL \ - org.label-schema.schema-version = "1.0" \ - org.label-schema.name = "cimpy" \ - org.label-schema.license = "Mozilla Public License Version 2.0" \ - org.label-schema.vendor = "Institute for Automation of Complex Power Systems, RWTH Aachen University" \ - org.label-schema.author.name = "Jan Dinkelbach" - -RUN dnf -y update - -RUN dnf -y install \ - make \ - python3-pip \ - graphviz-devel - -RUN dnf --refresh -y install \ - python3-devel - -RUN pip3 install sphinx_rtd_theme - -RUN pip3 install sphinx - -RUN pip3 install pytest - -RUN pip3 install pytest-check - -ADD . /cimpy - -WORKDIR /cimpy - -RUN python3 setup.py install diff --git a/action.yaml b/action.yaml deleted file mode 100644 index b751e892..00000000 --- a/action.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# action.yml -name: 'Build Documentation' -description: 'We are going to build the documentation!' -runs: - using: 'docker' - image: 'Dockerfile' - entrypoint: './entrypoint.sh' diff --git a/documentation/docu.sh b/documentation/docu.sh deleted file mode 100755 index 720e233c..00000000 --- a/documentation/docu.sh +++ /dev/null @@ -1,12 +0,0 @@ -if [ "$1" = "--release" ] || [ "$1" == "" ]; then - python3 ../setup.py develop - sphinx-apidoc -F -H "cimpy" --separate -o "../documentation" "../" "../setup.py" - python3 set_inheritance_diagram.py - if [ "$1" = "--release" ] ; then - make html - else - make text - fi -else - echo "Usage: $0 [--release]" -fi diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 9c267ecc..00000000 --- a/entrypoint.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if [ -n "${GITHUB_WORKSPACE}" ]; -then - cd documentation - ./docu.sh --release - cp -a ${GITHUB_WORKSPACE}/documentation/_build/html ${GITHUB_WORKSPACE}/built_documentation -fi From 5ae145b62e6f95a08c5b067a201544ff045b65e9 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 17:47:42 +0200 Subject: [PATCH 16/17] docs: Move workflow figure into images folder Signed-off-by: Steffen Vogel --- documentation/{CIMpy.svg => images/cimpy_workflow.svg} | 0 documentation/index.rst | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename documentation/{CIMpy.svg => images/cimpy_workflow.svg} (100%) diff --git a/documentation/CIMpy.svg b/documentation/images/cimpy_workflow.svg similarity index 100% rename from documentation/CIMpy.svg rename to documentation/images/cimpy_workflow.svg diff --git a/documentation/index.rst b/documentation/index.rst index 2a98af48..d05f73f0 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -7,7 +7,7 @@ The processing of grid data is based on CIM compatible Python classes. The codeb The focus of CIMpy is on the support of the Common Grid Model Exchange Standard (CGMES) specified by the European Network of Transmission System Operators for Electricity (ENTSO-E). However, the CIMpy package can readily support further as well as new CIM versions if required. -.. image:: CIMpy.svg +.. image:: images/cimpy_workflow.svg Installation ------------- From 5eb9238a4492f81e2a5d481d8eaf8a99354fe33b Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 17 Jun 2024 18:00:38 +0200 Subject: [PATCH 17/17] Fix code-smells detected by Sonar Cloud Signed-off-by: Steffen Vogel --- cimpy/cimexport.py | 4 ++-- cimpy/cimimport.py | 2 +- cimpy/utils.py | 18 +++++++++--------- tests/create_pickle_dump.py | 4 ++-- tests/test_export.py | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cimpy/cimexport.py b/cimpy/cimexport.py index 1e46a220..97795962 100644 --- a/cimpy/cimexport.py +++ b/cimpy/cimexport.py @@ -121,9 +121,9 @@ def _get_reference_uuid(attr_dict, version, topology, mRID, urls): # This function searches a class_object in the res dictionary and returns the corresponding key (the mRID). Necessary # for classes without mRID as attribute like SvVoltage def _search_mRID(class_object, topology): - for mRID, class_obj in topology.items(): + for id, class_obj in topology.items(): if class_object == class_obj: - return mRID + return id return "" diff --git a/cimpy/cimimport.py b/cimpy/cimimport.py index 2a4fc350..ef2aa701 100644 --- a/cimpy/cimimport.py +++ b/cimpy/cimimport.py @@ -386,7 +386,7 @@ def _set_attributes(import_result, xml_files, namespace_rdf, base, logger_groupe # Clear children of the root element to minimise memory usage. root.clear() - logger.info('END of parsing file "{}"', xml_file) + logger.info('END of parsing file "%s"', xml_file) return import_result, logger_grouped diff --git a/cimpy/utils.py b/cimpy/utils.py index 933249be..ca668979 100644 --- a/cimpy/utils.py +++ b/cimpy/utils.py @@ -14,22 +14,22 @@ def node_breaker_to_bus_branch(import_result): diagram_objects_list = [] diagram_object_points_list = [] connect_nodes = [] - for mRID in res.keys(): - class_name = res[mRID].__class__.__name__ + for id in res.keys(): + class_name = res[id].__class__.__name__ if class_name == "Breaker": - breaker_list.append(mRID) + breaker_list.append(id) elif class_name == "OperationalLimitSet": - operational_limit_set_list.append(mRID) + operational_limit_set_list.append(id) elif class_name == "Terminal": - terminals_list.append(mRID) + terminals_list.append(id) elif class_name == "VoltageLimit": - voltage_limit_list.append(mRID) + voltage_limit_list.append(id) elif class_name == "DiagramObject": - diagram_objects_list.append(mRID) + diagram_objects_list.append(id) elif class_name == "DiagramObjectPoint": - diagram_object_points_list.append(mRID) + diagram_object_points_list.append(id) elif class_name == "ConnectivityNode": - connect_nodes.append(mRID) + connect_nodes.append(id) # Search for open breakers open_breakers = [] diff --git a/tests/create_pickle_dump.py b/tests/create_pickle_dump.py index 89ec43a6..eb11b742 100644 --- a/tests/create_pickle_dump.py +++ b/tests/create_pickle_dump.py @@ -16,9 +16,9 @@ def create_pickle(): imported_result = cimpy.cim_import(test_files, "cgmes_v2_4_15") - CGMES_object = cimpy.cimexport._get_class_attributes_with_references(imported_result, "cgmes_v2_4_15") + cgmes_object = cimpy.cimexport._get_class_attributes_with_references(imported_result, "cgmes_v2_4_15") - pickle.dump(CGMES_object, open("CIGREMV_import_reference_cgmes_v2_4_15.p1", "wb")) + pickle.dump(cgmes_object, open("CIGREMV_import_reference_cgmes_v2_4_15.p1", "wb")) create_pickle() diff --git a/tests/test_export.py b/tests/test_export.py index 25503b40..9e0c4112 100644 --- a/tests/test_export.py +++ b/tests/test_export.py @@ -160,11 +160,11 @@ def test_export_with_imported_files(sample_cimdata, tmpdir): check.is_in("$rdf:about", current_export_class.keys()) check.is_in("$rdf:ID", current_export_class.keys()) - for mRID in test_mrids: - check.is_in(mRID, export_mrids) - if mRID in export_mrids: - test_attr = test_class_dict[mRID].items() - export_attr = export_class_dict[mRID].items() + for id in test_mrids: + check.is_in(id, export_mrids) + if id in export_mrids: + test_attr = test_class_dict[id].items() + export_attr = export_class_dict[id].items() for item in test_attr: if item[0] in [ "cim:NameType",