Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relationship support #20

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
69 changes: 60 additions & 9 deletions CIMgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import os
import textwrap
from time import time

from distutils.dir_util import remove_tree
from pathlib import Path
import xmltodict
from bs4 import BeautifulSoup

Expand Down Expand Up @@ -208,6 +209,7 @@ def __init__(self, rdfsEntry):
self.origin_list = []
self.super = rdfsEntry.subClassOf()
self.subclasses = []
self.stereotype = rdfsEntry.stereotype()

def attributes(self):
return self.attribute_list
Expand Down Expand Up @@ -275,6 +277,12 @@ def is_a_float(self):
return False
return True

def is_a_primitive(self):
return self.stereotype == 'Primitive'

def is_a_cim_datatype(self):
return self.stereotype == 'CIMDatatype'

def get_profile_name(descriptions):
for list_elem in descriptions:
# only for CGMES-Standard
Expand Down Expand Up @@ -446,16 +454,24 @@ def _write_python_files(elem_dict, langPack, outputPath, version):

float_classes = {}
enum_classes = {}
primitive_classes = {}
cim_data_type_classes = {}

# Iterate over Classes
for class_definition in elem_dict:
if elem_dict[class_definition].is_a_float():
float_classes[class_definition] = True
if elem_dict[class_definition].has_instances():
enum_classes[class_definition] = True
if elem_dict[class_definition].is_a_primitive():
primitive_classes[class_definition] = True
if elem_dict[class_definition].is_a_cim_datatype():
cim_data_type_classes[class_definition] = True

langPack.set_float_classes(float_classes)
langPack.set_enum_classes(enum_classes)
langPack.set_primitive_classes(primitive_classes)
langPack.set_cim_data_type_classes(cim_data_type_classes)

for class_name in elem_dict.keys():

Expand All @@ -467,6 +483,8 @@ def _write_python_files(elem_dict, langPack, outputPath, version):
"instances": elem_dict[class_name].instances(),
"has_instances": elem_dict[class_name].has_instances(),
"is_a_float": elem_dict[class_name].is_a_float(),
"is_a_primitive": elem_dict[class_name].is_a_primitive(),
"is_a_cim_data_type": elem_dict[class_name].is_a_cim_datatype(),
"langPack": langPack,
"sub_class_of": elem_dict[class_name].superClass(),
"sub_classes": elem_dict[class_name].subClasses(),
Expand Down Expand Up @@ -638,20 +656,44 @@ def _merge_classes(profiles_dict):
class_dict[class_key].addAttribute(attr)
return class_dict

def recursivelyAddSubClasses(class_dict, class_name):
def recursively_add_sub_classes(class_dict, class_name):
newSubClasses = []
theClass = class_dict[class_name]
for name in theClass.subClasses():
newSubClasses.append(name)
newNewSubClasses = recursivelyAddSubClasses(class_dict, name)
newNewSubClasses = recursively_add_sub_classes(class_dict, name)
newSubClasses = newSubClasses + newNewSubClasses
return newSubClasses

def addSubClassesOfSubClasses(class_dict):
for className in class_dict:
class_dict[className].setSubClasses(recursivelyAddSubClasses(class_dict, className))
def add_sub_classes_of_sub_classes(class_dict):
for class_name in class_dict:
class_dict[class_name].setSubClasses(recursively_add_sub_classes(class_dict, class_name))

def add_sub_classes_of_sub_classes_clean(class_dict, source):
temp = {}
for class_name in class_dict:
for name in class_dict[class_name].subClasses():
if name not in class_dict:
temp[name] = source[name]
add_sub_classes_of_sub_classes_clean(temp, source)
class_dict.update(temp)

# Order classes based on dependency order

def generate_clean_sub_classes(class_dict_with_origins, clean_class_dict):
for class_name in class_dict_with_origins:
super_class_name = class_dict_with_origins[class_name].superClass()
if super_class_name is None and class_dict_with_origins[class_name].has_instances():
clean_class_dict[class_name] = class_dict_with_origins[class_name]

def cim_generate(directory, outputPath, version, langPack):
for class_name in class_dict_with_origins:
super_class_name = class_dict_with_origins[class_name].superClass()
if super_class_name == None and not class_dict_with_origins[class_name].has_instances():
clean_class_dict[class_name] = class_dict_with_origins[class_name]

add_sub_classes_of_sub_classes_clean(clean_class_dict, class_dict_with_origins)

def cim_generate(directory, outputPath, version, langPack, clean_outdir):
"""Generates cgmes python classes from cgmes ontology

This function uses package xmltodict to parse the RDF files. The parse_rdf function sorts the classes to
Expand All @@ -668,6 +710,11 @@ def cim_generate(directory, outputPath, version, langPack):
:param outputPath: CGMES version, e.g. version = "cgmes_v2_4_15"
:param langPack: python module containing language specific functions
"""

# clean directory
if clean_outdir:
remove_tree(Path(outputPath))

profiles_array = []

t0 = time()
Expand All @@ -691,6 +738,8 @@ def cim_generate(directory, outputPath, version, langPack):
# merge classes from different profiles into one class and track origin of the classes and their attributes
class_dict_with_origins = _merge_classes(profiles_dict)

clean_class_dict = {}

# work out the subclasses for each class by noting the reverse relationship
for className in class_dict_with_origins:
superClassName = class_dict_with_origins[className].superClass()
Expand All @@ -702,10 +751,12 @@ def cim_generate(directory, outputPath, version, langPack):
print("No match for superClass in dict: :", superClassName)

# recursively add the subclasses of subclasses
addSubClassesOfSubClasses(class_dict_with_origins)
add_sub_classes_of_sub_classes(class_dict_with_origins)

generate_clean_sub_classes(class_dict_with_origins, clean_class_dict)

# get information for writing python files and write python files
_write_python_files(class_dict_with_origins, langPack, outputPath, version)
_write_python_files(clean_class_dict, langPack, outputPath, version)

if "modernpython" in langPack.__name__:
langPack.resolve_headers(outputPath, version)
Expand Down
3 changes: 2 additions & 1 deletion build.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
parser.add_argument('--schemadir', type=str, help='The schema directory', required=True)
parser.add_argument('--langdir', type=str, help='The langpack directory', required=True)
parser.add_argument('--cgmes_version', type=str, choices=['cgmes_v2_4_13', 'cgmes_v2_4_15', 'cgmes_v3_0_0'], default='cgmes_v2_4_15', help='CGMES Version')
parser.add_argument('--clean_outdir', type=bool, help='Clean the output directory', required=False, default=False)
args = parser.parse_args()

langPack = importlib.import_module(args.langdir + ".langPack")
schema_path = os.path.join(os.getcwd(), args.schemadir)
CIMgen.cim_generate(schema_path, args.outdir, args.cgmes_version, langPack)
CIMgen.cim_generate(schema_path, args.outdir, args.cgmes_version, langPack, args.clean_outdir)
6 changes: 6 additions & 0 deletions cpp/langPack.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ def is_an_enum_class(name):
if name in enum_classes:
return enum_classes[name]

def set_primitive_classes(new_primitive_classes):
return

def set_cim_data_type_classes(new_cim_data_type_classes):
return

# These insert_ functions are used to generate the entries in the dynamic_switch
# maps, for use in assignments.cpp and Task.cpp
# TODO: implement this as one function, determine in template if it should be called.
Expand Down
6 changes: 6 additions & 0 deletions java/langPack.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ def is_an_enum_class(name):
if name in enum_classes:
return enum_classes[name]

def set_primitive_classes(new_primitive_classes):
return

def set_cim_data_type_classes(new_cim_data_type_classes):
return

# These insert_ functions are used to generate the entries in the dynamic_switch
# maps, for use in assignments.cpp and Task.cpp
# TODO: implement this as one function, determine in template if it should be called.
Expand Down
6 changes: 6 additions & 0 deletions javascript/langPack.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ def is_an_enum_class(name):
if name in enum_classes:
return enum_classes[name]

def set_primitive_classes(new_primitive_classes):
return

def set_cim_data_type_classes(new_cim_data_type_classes):
return

def get_class_location(class_name, class_map, version):
pass

Expand Down
3 changes: 3 additions & 0 deletions modernpython/cimdatatype_header.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from ..utils.datatypes import CIMDatatype
from ..utils.profile import Profile
from .enum import UnitMultiplier, UnitSymbol
2 changes: 2 additions & 0 deletions modernpython/enum_header.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from enum import Enum

Loading