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

Primitives, enum and CIM_datatype #39

Merged
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bc81346
Added identification of CIMDatatypes and Primitive classes
HUG0-D Nov 15, 2024
6db0aad
Decomposition of the run_template function, template files are now de…
HUG0-D Nov 15, 2024
5713723
Added Primitive class and template
HUG0-D Nov 15, 2024
e2cba25
Adding CIMDatatype classes and template
HUG0-D Nov 15, 2024
7ab6fe7
Adding attribute dataclass in json_extra
HUG0-D Nov 15, 2024
e13cdc8
Adding Enum classes
HUG0-D Nov 15, 2024
a0a3624
Fixing template formating
HUG0-D Nov 15, 2024
d5469ad
Fixing template formating
HUG0-D Nov 15, 2024
5d675a3
Corrected header in template (unspecifying the CGMES version)
HUG0-D Nov 18, 2024
900cdd9
Simplified _set_instance by replacing text before exception and added…
HUG0-D Nov 18, 2024
1ca6bb1
Imports sorted by alphabetical order
HUG0-D Nov 18, 2024
08834d9
Rename class and attribute property "is_a_primitive"-->"is_a_primitiv…
HUG0-D Nov 18, 2024
2c3d74c
Adapted terminology of variable and function name: cim_data_type as d…
HUG0-D Nov 18, 2024
b9f262c
Adapted terminology of variable and function name: cim_data_type as a…
HUG0-D Nov 18, 2024
58e0c21
Renaming mustache template files and variables to be more consistent
HUG0-D Nov 19, 2024
e681a4f
Enum class generation using chevron
HUG0-D Nov 19, 2024
a931467
removing pycgmes from base, and chevron_writer imports
HUG0-D Nov 19, 2024
dd5e0ee
Removed need of setAttributeClass by handling attribute_class direclt…
HUG0-D Nov 20, 2024
6c6c674
Fix sonar issue
HUG0-D Nov 20, 2024
29833d0
Fix datatype attributes for cgmes_v2 and simplifed imports
HUG0-D Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ __pycache__/
.vscode/*
*/.DS_Store
.DS_Store
tests/
153 changes: 128 additions & 25 deletions cimgen/languages/modernpython/lang_pack.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import os
import re
import ast
from distutils.dir_util import copy_tree
from pathlib import Path
from importlib.resources import files
Expand Down Expand Up @@ -35,9 +36,12 @@ def location(version): # NOSONAR
base = {"base_class": "Base", "class_location": location}

# These are the files that are used to generate the python files.
template_files = [{"filename": "cimpy_class_template.mustache", "ext": ".py"}]
constants_template_files = [{"filename": "cimpy_constants_template.mustache", "ext": ".py"}]
profile_template_files = [{"filename": "cimpy_cgmesProfile_template.mustache", "ext": ".py"}]
template_files = {"filename": "cimpy_class_template.mustache", "ext": ".py"}
constants_template_files = {"filename": "cimpy_constants_template.mustache", "ext": ".py"}
profile_template_files = {"filename": "cimpy_cgmesProfile_template.mustache", "ext": ".py"}
enum_template_files = {"filename": "enum_class_template.mustache", "ext": ".py"}
primitive_template_files = {"filename": "primitive_template.mustache", "ext": ".py"}
cimdatatype_template_files = {"filename": "cimdatatype_template.mustache", "ext": ".py"}
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved


def get_class_location(class_name, class_map, version): # NOSONAR
Expand Down Expand Up @@ -76,23 +80,124 @@ def _get_type_and_default(text, render) -> tuple[str, str]:
return ("str", 'default=""')


def _primitive_to_data_type(datatype):
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved
if datatype.lower() == "integer":
return "int"
if datatype.lower() == "float":
return "float"
if datatype.lower() == "boolean":
return "bool"
if datatype.lower() == "datetime":
return "datetime"
if datatype.lower() == "date":
return "date"
if datatype.lower() == "time":
return "time"
if datatype.lower() == "monthday":
return "str" # TO BE FIXED? I could not find a datatype in python that holds only month and day.
if datatype.lower() == "string":
return "str"
else:
# this actually never happens
return "float"
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved


def _set_imports(attributes):
classes = set()
for attribute in attributes:
if attribute["is_datatype_attribute"] or attribute["is_primitive_attribute"]:
classes.add(attribute["attribute_class"])

result = ""
for val in sorted(classes):
result += "from ." + val + " import " + val + "\n"
return result + "\n"


def _set_datatype_attributes(attributes) -> dict:
datatype_attributes = {}
datatype_attributes["python_type"] = "None"
datatype_attributes["unit"] = "UnitSymbol.none"
datatype_attributes["multiplier"] = "UnitMultiplier.none"
tom-hg57 marked this conversation as resolved.
Show resolved Hide resolved

for attribute in attributes:
if (
"about" in attribute
and attribute["about"]
and "value" in attribute["about"]
and "attribute_class" in attribute
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved
):
datatype_attributes["python_type"] = _primitive_to_data_type(attribute["attribute_class"])
if (
"about" in attribute
and attribute["about"]
and "multiplier" in attribute["about"]
and "isFixed" in attribute
):
datatype_attributes["multiplier"] = "UnitMultiplier." + attribute["isFixed"]
if "about" in attribute and attribute["about"] and "unit" in attribute["about"] and "isFixed" in attribute:
datatype_attributes["unit"] = "UnitSymbol." + attribute["isFixed"]
return datatype_attributes


def _set_attribute_class(text, render) -> str:
attribute = eval(render(text))
attribute_class = ""
if attribute["is_datatype_attribute"] or attribute["is_primitive_attribute"]:
attribute_class += "\n "
attribute_class += """"attribute_class": """ + attribute["attribute_class"] + ","
return attribute_class


# called by chevron, text contains the label {{dataType}}, which is evaluated by the renderer (see class template)
def _set_instances(text, render):
instance = None
instance = ast.literal_eval(render(text).replace(""", '"'))
if "label" in instance:
value = instance["label"] + ' = "' + instance["label"] + '"'
if "comment" in instance:
value += " # " + instance["comment"] + " noqa: E501"
return value
else:
return ""


def run_template(output_path, class_details):
if class_details["is_a_primitive_class"] or class_details["is_a_datatype_class"]:
return
for template_info in template_files:
resource_file = Path(
os.path.join(
output_path,
"resources",
class_details["class_name"] + template_info["ext"],
)
)
if not resource_file.exists():
if not (parent := resource_file.parent).exists():
parent.mkdir()
if class_details["is_a_primitive_class"]:
# Primitives are never used in the in memory representation but only for
# the schema
template = primitive_template_files
class_details["python_type"] = _primitive_to_data_type(class_details["class_name"])
elif class_details["is_a_datatype_class"]:
# Datatypes based on primitives are never used in the in memory
# representation but only for the schema
template = cimdatatype_template_files
class_details.update(_set_datatype_attributes(class_details["attributes"]))
elif class_details["is_an_enum_class"]:
template = enum_template_files
class_details["setInstances"] = _set_instances
else:
template = template_files
class_details["setDefault"] = _set_default
class_details["setType"] = _set_type
_write_templated_file(resource_file, class_details, template_info["filename"])
class_details["setAttributeClass"] = _set_attribute_class
class_details["setImports"] = _set_imports(class_details["attributes"])
resource_file = _create_file(output_path, class_details, template)
_write_templated_file(resource_file, class_details, template["filename"])


def _create_file(output_path, class_details, template) -> str:
resource_file = Path(
os.path.join(
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved
output_path,
"resources",
class_details["class_name"] + template["ext"],
)
)
if not resource_file.exists():
if not (parent := resource_file.parent).exists():
parent.mkdir()
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved
return resource_file
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved


def _write_templated_file(class_file, class_details, template_filename):
Expand All @@ -109,17 +214,15 @@ def _write_templated_file(class_file, class_details, template_filename):


def _create_constants(output_path: str, cim_namespace: str):
for template_info in constants_template_files:
class_file = os.path.join(output_path, "utils", "constants" + template_info["ext"])
class_details = {"cim_namespace": cim_namespace}
_write_templated_file(class_file, class_details, template_info["filename"])
class_file = os.path.join(output_path, "utils", "constants" + constants_template_files["ext"])
class_details = {"cim_namespace": cim_namespace}
_write_templated_file(class_file, class_details, constants_template_files["filename"])


def _create_cgmes_profile(output_path: str, profile_details: list):
for template_info in profile_template_files:
class_file = os.path.join(output_path, "utils", "profile" + template_info["ext"])
class_details = {"profiles": profile_details}
_write_templated_file(class_file, class_details, template_info["filename"])
class_file = os.path.join(output_path, "utils", "profile" + profile_template_files["ext"])
class_details = {"profiles": profile_details}
_write_templated_file(class_file, class_details, profile_template_files["filename"])


def resolve_headers(path: str, version: str):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
Generated from the CGMES files via cimgen: https://github.com/sogno-platform/cimgen
"""

from ..utils.datatypes import CIMDatatype
from ..utils.profile import Profile
from .UnitMultiplier import UnitMultiplier
from .UnitSymbol import UnitSymbol

{{class_name}} = CIMDatatype(
name="{{class_name}}",
type={{python_type}},
symbol={{unit}},
multiplier={{multiplier}},
profiles=[{{#class_origin}}
Profile.{{origin}},{{/class_origin}}
],
)

"""
{{{wrapped_class_comment}}}
"""
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ from pydantic.dataclasses import dataclass

from ..utils.profile import BaseProfile, Profile
from {{class_location}} import {{sub_class_of}}


{{setImports}}
@dataclass
class {{class_name}}({{sub_class_of}}):
"""
Expand All @@ -36,7 +35,7 @@ class {{class_name}}({{sub_class_of}}):
"is_datatype_attribute": {{#is_datatype_attribute}}True{{/is_datatype_attribute}}{{^is_datatype_attribute}}False{{/is_datatype_attribute}},
"is_enum_attribute": {{#is_enum_attribute}}True{{/is_enum_attribute}}{{^is_enum_attribute}}False{{/is_enum_attribute}},
"is_list_attribute": {{#is_list_attribute}}True{{/is_list_attribute}}{{^is_list_attribute}}False{{/is_list_attribute}},
"is_primitive_attribute": {{#is_primitive_attribute}}True{{/is_primitive_attribute}}{{^is_primitive_attribute}}False{{/is_primitive_attribute}},
"is_primitive_attribute": {{#is_primitive_attribute}}True{{/is_primitive_attribute}}{{^is_primitive_attribute}}False{{/is_primitive_attribute}},{{#setAttributeClass}}{{.}}{{/setAttributeClass}}
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved
},
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
Generated from the CGMES files via cimgen: https://github.com/sogno-platform/cimgen
"""

from enum import Enum


class {{class_name}}(str, Enum):
"""
{{{class_comment}}}
"""

{{#enum_instances}}
{{#setInstances}}{{.}}{{/setInstances}}
HUG0-D marked this conversation as resolved.
Show resolved Hide resolved
{{/enum_instances}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
Generated from the CGMES files via cimgen: https://github.com/sogno-platform/cimgen
"""

from datetime import date, datetime, time
from ..utils.datatypes import Primitive
from ..utils.profile import Profile

{{class_name}} = Primitive(
name="{{class_name}}",
type={{python_type}},
profiles=[{{#class_origin}}
Profile.{{origin}},{{/class_origin}}
],
)

"""
{{{wrapped_class_comment}}}
"""
26 changes: 26 additions & 0 deletions cimgen/languages/modernpython/utils/datatypes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from pydantic import Field
from typing import List

from .constants import NAMESPACES
from pydantic.dataclasses import dataclass

from .config import cgmes_resource_config
from .profile import BaseProfile
from ..resources.UnitMultiplier import UnitMultiplier
from ..resources.UnitSymbol import UnitSymbol


@dataclass(config=cgmes_resource_config)
class Primitive:

name: str = Field(frozen=True)
type: object = Field(frozen=True)
namespace: str = Field(frozen=True, default=NAMESPACES["cim"])
profiles: List[BaseProfile] = Field(frozen=True)


@dataclass(config=cgmes_resource_config)
class CIMDatatype(Primitive):

multiplier: UnitMultiplier = Field(frozen=True)
symbol: UnitSymbol = Field(frozen=True)