Skip to content

Commit 962ce62

Browse files
committed
Include instances as attributes of the relevant class
1 parent 8f9cadd commit 962ce62

File tree

4 files changed

+113
-23
lines changed

4 files changed

+113
-23
lines changed

build.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections import defaultdict
2+
import json
23
import os.path
34
import shutil
45
import subprocess
@@ -7,25 +8,35 @@
78
from jinja2 import Environment, select_autoescape, FileSystemLoader
89

910
from pipeline.translator import PythonBuilder
10-
from pipeline.utils import clone_sources, SchemaLoader
11+
from pipeline.utils import clone_sources, SchemaLoader, InstanceLoader
1112

12-
print("***************************************")
13+
print("*********************************************************")
1314
print(f"Triggering the generation of Python package for openMINDS")
14-
print("***************************************")
15+
print("*********************************************************")
1516

1617
# Step 1 - clone central repository in main branch to get the latest sources
1718
clone_sources()
1819
schema_loader = SchemaLoader()
20+
instance_loader = InstanceLoader()
1921
if os.path.exists("target"):
2022
shutil.rmtree("target")
2123

24+
# Step 2 - load instances
25+
instances = {}
26+
for version in instance_loader.get_instance_versions():
27+
instances[version] = defaultdict(list)
28+
for instance_path in instance_loader.find_instances(version):
29+
with open(instance_path) as fp:
30+
instance_data = json.load(fp)
31+
instances[version][instance_data["@type"]].append(instance_data)
32+
2233
python_modules = defaultdict(list)
2334
for schema_version in schema_loader.get_schema_versions():
2435

25-
# Step 2 - find all involved schemas for the current version
36+
# Step 3 - find all involved schemas for the current version
2637
schemas_file_paths = schema_loader.find_schemas(schema_version)
2738

28-
# Step 3a - figure out which schemas are embedded and which are linked
39+
# Step 4a - figure out which schemas are embedded and which are linked
2940
embedded = set()
3041
linked = set()
3142
for schema_file_path in schemas_file_paths:
@@ -42,17 +53,17 @@
4253
for schema_identifier in conflicts:
4354
linked.remove(schema_identifier)
4455

45-
# Step 3b - translate and build each openMINDS schema as JSON-Schema
56+
# Step 4b - translate and build each openMINDS schema as JSON-Schema
4657
for schema_file_path in schemas_file_paths:
4758
module_path, class_name = PythonBuilder(
48-
schema_file_path, schema_loader.schemas_sources
59+
schema_file_path, schema_loader.schemas_sources, instances=instances.get(schema_version, None)
4960
).build(embedded=embedded)
5061

5162
parts = module_path.split(".")
5263
parent_path = ".".join(parts[:-1])
5364
python_modules[parent_path].append((parts[-1], class_name))
5465

55-
# Step 4 - create additional files, e.g. __init__.py
66+
# Step 5 - create additional files, e.g. __init__.py
5667
openminds_modules = defaultdict(set)
5768
for path, classes in python_modules.items():
5869
dir_path = ["target", "openminds"] + path.split(".")
@@ -96,5 +107,5 @@
96107
shutil.copy("pipeline/src/README.md", "target/README.md")
97108
shutil.copy("./LICENSE", "target/LICENSE")
98109

99-
# Step 5 - run formatter
110+
# Step 6 - run formatter
100111
subprocess.call([sys.executable, "-m", "black", "--quiet", "target"])

pipeline/src/module_template.py.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,10 @@ class {{ class_name }}({{ base_class }}):
4747
return super().__init__({%- if base_class == "LinkedMetadata" %}id=id, {%- endif -%}{%- for property in properties -%}{{property.name}}={{property.name}}, {%- endfor -%})
4848

4949
{{ additional_methods }}
50+
51+
{% for instance_name, instance in instances.items() %}
52+
{{ class_name }}.{{ instance_name }} = {{ class_name }}(
53+
{% for key, value in instance.items() -%}
54+
{% if value is string %}{{key}}="{{value}}",{% else %}{{key}}={{value}},{% endif %}
55+
{% endfor -%}
56+
){% endfor %}

pipeline/translator.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,27 @@
66
from jinja2 import Environment, select_autoescape, FileSystemLoader
77

88

9+
number_names = {
10+
"0": "zero",
11+
"1": "one",
12+
"2": "two",
13+
"3": "three",
14+
"4": "four",
15+
"5": "five",
16+
"6": "six",
17+
"7": "seven",
18+
"8": "eight",
19+
"9": "nine"
20+
}
21+
22+
923
def generate_python_name(json_name, allow_multiple=False):
1024
python_name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", json_name)
1125
python_name = re.sub("([a-z0-9])([A-Z])", r"\1_\2", python_name).lower()
12-
python_name = python_name.replace("-", "_")
26+
python_name = python_name.replace("-", "_").replace(".", "_").replace("+", "plus").replace("#", "sharp")
27+
if python_name[0] in number_names: # Python variables can't start with a number
28+
python_name = number_names[python_name[0]] + python_name[1:]
29+
1330
# if (
1431
# allow_multiple
1532
# and python_name[-1] != "s"
@@ -32,7 +49,7 @@ def generate_python_name(json_name, allow_multiple=False):
3249
class PythonBuilder(object):
3350
"""docstring"""
3451

35-
def __init__(self, schema_file_path: str, root_path: str):
52+
def __init__(self, schema_file_path: str, root_path: str, instances: Optional[dict] = None):
3653
self.template_name = "src/module_template.py.txt"
3754
self.env = Environment(
3855
loader=FileSystemLoader(os.path.dirname(os.path.realpath(__file__))), autoescape=select_autoescape()
@@ -46,6 +63,7 @@ def __init__(self, schema_file_path: str, root_path: str):
4663
]
4764
with open(schema_file_path, "r") as schema_f:
4865
self._schema_payload = json.load(schema_f)
66+
self.instances = instances or {}
4967

5068
@property
5169
def _version_module(self):
@@ -106,16 +124,40 @@ def get_type(property):
106124
else:
107125
raise NotImplementedError
108126

109-
if self._schema_payload["_type"] in embedded:
127+
openminds_type = self._schema_payload["_type"]
128+
if openminds_type in embedded:
110129
base_class = "EmbeddedMetadata"
111130
else:
112131
base_class = "LinkedMetadata"
132+
133+
def filter_value(value):
134+
if isinstance(value, str):
135+
return value.replace('"', "'").replace("\n", " ")
136+
return value
137+
138+
def filter_instance(instance):
139+
filtered_instance = {
140+
generate_python_name(k): filter_value(v)
141+
for k, v in instance.items()
142+
if k[0] != "@" and k[:4] != "http" and v is not None
143+
}
144+
filtered_instance["id"] = instance["@id"]
145+
return filtered_instance
146+
147+
instances = {
148+
generate_python_name(instance["@id"].split("/")[-1]) : filter_instance(instance)
149+
for instance in self.instances.get(openminds_type, [])
150+
}
151+
instances = { # sort by key
152+
name: instances[name] for name in sorted(instances)
153+
}
154+
113155
self.context = {
114156
"docstring": self._schema_payload.get("description", "<description not available>"),
115157
"base_class": base_class,
116158
"preamble": "", # todo: e.g. extra imports
117159
"class_name": self._schema_payload["name"],
118-
"openminds_type": self._schema_payload["_type"],
160+
"openminds_type": openminds_type,
119161
"schema_version": self.version,
120162
"properties": [ # call this "properties"
121163
{
@@ -136,6 +178,7 @@ def get_type(property):
136178
], # todo
137179
# unused in property: "nameForReverseLink"
138180
"additional_methods": "",
181+
"instances": instances
139182
}
140183
import_map = {
141184
"date": "from datetime import date",

pipeline/utils.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,51 @@
55

66
from git import Repo, GitCommandError
77

8-
source_url = "https://github.com/openMetadataInitiative/openMINDS.git"
9-
108

119
def clone_sources():
12-
if os.path.exists("sources"):
13-
shutil.rmtree("sources")
14-
Repo.clone_from(source_url, to_path="sources", depth=1)
15-
16-
17-
class SchemaLoader(object):
10+
if os.path.exists("_sources"):
11+
shutil.rmtree("_sources")
12+
Repo.clone_from(
13+
"https://github.com/openMetadataInitiative/openMINDS.git",
14+
to_path="_sources",
15+
depth=1,
16+
)
17+
if os.path.exists("_instances"):
18+
shutil.rmtree("_instances")
19+
Repo.clone_from(
20+
"https://github.com/openMetadataInitiative/openMINDS_instances.git",
21+
to_path="_instances",
22+
depth=1,
23+
)
24+
25+
26+
class SchemaLoader:
1827
def __init__(self):
1928
self._root_directory = os.path.realpath(".")
20-
self.schemas_sources = os.path.join(self._root_directory, "sources", "schemas")
29+
self.schemas_sources = os.path.join(self._root_directory, "_sources", "schemas")
2130

2231
def get_schema_versions(self) -> List[str]:
2332
return os.listdir(self.schemas_sources)
2433

2534
def find_schemas(self, version: str) -> List[str]:
26-
return glob.glob(os.path.join(self.schemas_sources, version, f"**/*.schema.omi.json"), recursive=True)
35+
return glob.glob(
36+
os.path.join(self.schemas_sources, version, f"**/*.schema.omi.json"),
37+
recursive=True,
38+
)
39+
40+
41+
class InstanceLoader:
42+
def __init__(self):
43+
self._root_directory = os.path.realpath(".")
44+
self.instances_sources = os.path.join(
45+
self._root_directory, "_instances", "instances"
46+
)
47+
48+
def get_instance_versions(self) -> List[str]:
49+
return os.listdir(self.instances_sources)
50+
51+
def find_instances(self, version: str) -> List[str]:
52+
return glob.glob(
53+
os.path.join(self.instances_sources, version, f"**/*.jsonld"),
54+
recursive=True,
55+
)

0 commit comments

Comments
 (0)