Skip to content

Commit

Permalink
Change creation of matlab class names
Browse files Browse the repository at this point in the history
Build class names from file paths instead of the type IRI
Add exception for class name AnatomicalEntity of v2.0
Fix bug preventing creating of ParameterSetting for v1.0
  • Loading branch information
ehennestad committed Nov 13, 2024
1 parent e3fdba1 commit 6935d27
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 14 deletions.
7 changes: 5 additions & 2 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import shutil

from pipeline.translator import MATLABSchemaBuilder
from pipeline.utils import clone_sources, SchemaLoader, initialise_jinja_templates, save_resource_files, save_enumeration_classes
from pipeline.utils import clone_sources, SchemaLoader, initialise_jinja_templates, save_resource_files, save_enumeration_classes, get_class_name_map

print("***************************************")
print(f"Triggering the generation of MATLAB-Classes for openMINDS")
Expand All @@ -23,11 +23,14 @@
schemas_file_paths = schema_loader.find_schemas(schema_version)
# schemas_file_paths = [path for path in schemas_file_paths if "person" in path] # testing

class_name_map = get_class_name_map(schema_loader, schema_version)

for schema_file_path in schemas_file_paths:
# Step 4 - translate and build each openMINDS schema as MATLAB class
schema_root_path = schema_loader.schemas_sources

try:
MATLABSchemaBuilder(schema_file_path, schema_root_path, jinja_templates).build()
MATLABSchemaBuilder(schema_file_path, schema_root_path, class_name_map, jinja_templates).build()
except Exception as e:
print(f"Error while building schema {schema_file_path}: {e}")

Expand Down
42 changes: 31 additions & 11 deletions pipeline/translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@
class MATLABSchemaBuilder(object):
""" Class for building MATLAB schema classes """

def __init__(self, schema_file_path:str, root_path:str, jinja_templates:Dict[str, Template]):
def __init__(self, schema_file_path:str, root_path:str, class_name_map:Dict[str, str], jinja_templates:Dict[str, Template]):

self._parse_source_file_path(schema_file_path, root_path)
self._class_name_map = class_name_map

with open(schema_file_path, "r") as schema_file:
self._schema_payload = json.load(schema_file)
Expand Down Expand Up @@ -128,22 +129,26 @@ def _extract_template_variables(self):
# Resolve property class name in matlab
if has_linked_type:
possible_types = [
f'{_generate_class_name(iri)}'
f'{_generate_class_name(iri, self._class_name_map)}'
for iri in property_info[SCHEMA_PROPERTY_LINKED_TYPES]
]
elif has_embedded_type:
possible_types = [
f'{_generate_class_name(iri)}'
f'{_generate_class_name(iri, self._class_name_map)}'
for iri in property_info[SCHEMA_PROPERTY_EMBEDDED_TYPES]
] # todo: handle minItems maxItems, e.g. for axesOrigin
elif "_formats" in property_info:
assert property_info["type"] == "string"
possible_types = sorted(set([format_map[item] for item in property_info["_formats"]]))
elif property_info.get("type") == "array":
possible_types = [type_name_map[property_info["items"]["type"]]]
elif isinstance( property_info.get("type"), list ):
possible_types = []
else:
possible_types = [type_name_map[property_info["type"]]]



# Resolve property dimension in matlab
if allow_multiple:
size_attribute = "(1,:)"
Expand Down Expand Up @@ -173,15 +178,20 @@ def _extract_template_variables(self):

mixed_types_list = sorted(possible_types)

if len(possible_types) == 1:
if len(possible_types) == 0:
# Exception: ParameterSetting from v1. Uses validator instead of type restriction
possible_types = ''
possible_types_str = ''
possible_types_docstr = ", ".join(property_info.get("type"))
elif len(possible_types) == 1:
possible_types = possible_types[0]
possible_types_str = f'"{possible_types}"'
possible_types_docstr = possible_types_docstr[0]
else:
possible_types_str = _list_to_string_array(possible_types, do_sort=True)
possible_types_docstr = ", ".join(sorted(possible_types_docstr))

class_name = _generate_class_name(schema[SCHEMA_PROPERTY_TYPE]).split(".")[-1]
class_name = _generate_class_name(schema[SCHEMA_PROPERTY_TYPE], self._class_name_map).split(".")[-1]
possible_types = _create_mixedtype_full_class_name(class_name, property_name)

template_property_attributes = {
Expand All @@ -207,7 +217,7 @@ def _extract_template_variables(self):
embedded_types = [ {'name':prop["name"],'types':prop["type_list"]} for prop in props if prop["is_embedded"] ]

# Some schemas had the wrong type in older model versions, so this is unreliable
#class_name = _generate_class_name(schema[SCHEMA_PROPERTY_TYPE]).split(".")[-1]
#class_name = _generate_class_name(schema[SCHEMA_PROPERTY_TYPE], self._class_name_map).split(".")[-1]
class_name = self._schema_class_name

display_label_method_expression = _get_display_label_method_expression(class_name, schema["properties"].keys())
Expand Down Expand Up @@ -292,7 +302,7 @@ def _create_matlab_name(json_name):
"""Remove the openMINDS prefix from a name"""
return json_name.split('/')[-1]

def _generate_class_name(iri):
def _generate_class_name(iri, class_name_map):
"""
Generate a class name from an IRI.
E.g https://openminds.ebrains.eu/core/Subject -> openminds.core.Subject
Expand All @@ -302,9 +312,16 @@ def _generate_class_name(iri):
else: # v4 and higher
parts = iri.split(':')

for i in range(len(parts) - 1):
parts[i] = parts[i].lower()
return "openminds." + ".".join(parts)
type_name = parts[-1]

# Ensure first letter of type_name is capitalized
type_name = type_name[0].upper() + type_name[1:]

return class_name_map[type_name]

#for i in range(len(parts) - 1):
# parts[i] = parts[i].lower()
# return "openminds." + ".".join(parts)


def _to_label(class_name_list):
Expand Down Expand Up @@ -477,6 +494,9 @@ def _create_property_validator_functions(name, property_info):
elif "time" in property_info.get("_formats") == "time":
validation_functions += [f"mustBeValidTime({property_name})"]

if isinstance( property_info.get("type"), list ):
validation_functions += [f'mustBeA({property_name}, ["numeric", "string"])']

if has_linked_type or has_embedded_type:
if not allow_multiple:
validation_functions += [f'mustBeSpecifiedLength({property_name}, 0, 1)']
Expand Down Expand Up @@ -546,4 +566,4 @@ def _parse_schema_type(type_specifier):


if __name__ == "__main__":
error('Not implemented yet')
raise NotImplementedError
23 changes: 22 additions & 1 deletion pipeline/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,27 @@ def initialise_jinja_templates(autoescape:bool=None):
return jinja_templates


def get_class_name_map(schema_loader, version):
# Extract all schema files
root_path = schema_loader.schemas_sources
schema_files = schema_loader.find_schemas(version)
schema_files.sort()

# Build a list for all the enumeration members
class_name_map = {}

for schema_file in schema_files:
schema_info = _parse_source_file_path(schema_file, root_path)
class_name_map[schema_info['type_name']] = _get_matlab_class_name(schema_info)

# Add some exceptions
if version == "v2.0":
print(class_name_map["CustomAnatomicalEntity"])
class_name_map["AnatomicalEntity"] = class_name_map["CustomAnatomicalEntity"]


return class_name_map

def camel_case(text_string: str):
return text_string[0].lower() + text_string[1:]

Expand Down Expand Up @@ -292,4 +313,4 @@ def _get_template_variables(enum_type, schema_files, root_path):
return {'types': template_variable_list }

elif enum_type == "Models":
return {'models': sorted(set(template_variable_list)) }
return {'models': sorted(set(template_variable_list)) }

0 comments on commit 6935d27

Please sign in to comment.