Skip to content

Commit 58dd355

Browse files
committed
Restructuring to have only a single file per module, rather than one per class
1 parent e97934b commit 58dd355

File tree

3 files changed

+115
-45
lines changed

3 files changed

+115
-45
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
Complete collection of schemas for the metadata models of the openMINDS metadata framework. All schemas are formatted in [LinkML](https://linkml.io) (*.yaml).
44

5+
**Warning: these schemas are auto-generated.
6+
In case of any discrepancy between the LinkML representation of the schemas
7+
and the original openMINDS schema definitions,
8+
the original openMINDS schema definitions should be considered as the correct reference.**
9+
510
To learn more about the openMINDS metadata framework please go to :arrow_right: [**ReadTheDocs**](https://openminds-documentation.readthedocs.io).
611

712

build.py

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,14 @@
6060
schema_file_path, schema_loader.schemas_sources, slots
6161
).update_slots()
6262

63+
linkml_classes = defaultdict(list)
64+
linkml_enum_imports = []
6365
for schema_file_path in schemas_file_paths:
6466
# Step 5 - translate and build each openMINDS schema as a LinkML schema
67+
relative_path = os.path.relpath(
68+
schema_file_path, start=f"_sources/schemas/{schema_version}"
69+
)
70+
module = relative_path.split(os.path.sep)[0]
6571
instances_for_version = instances.get(schema_version, None)
6672
if (
6773
terms_as_enums
@@ -73,35 +79,69 @@
7379
schema_loader.schemas_sources,
7480
instances_for_version,
7581
).build()
76-
imports.append(
77-
os.path.join(
78-
"enums",
79-
os.path.relpath(
80-
schema_file_path, start=f"_sources/schemas/{schema_version}"
81-
).replace(".schema.omi.json", ""),
82-
)
82+
linkml_enum_imports.append(
83+
os.path.join("enums", relative_path.replace(".schema.omi.json", ""))
8384
)
8485
else:
85-
LinkMLClassBuilder(
86+
linkml_class = LinkMLClassBuilder(
8687
schema_file_path, schema_loader.schemas_sources, slots
8788
).build()
88-
imports.append(
89-
os.path.relpath(
90-
schema_file_path, start=f"_sources/schemas/{schema_version}"
91-
).replace(".schema.omi.json", "")
92-
)
89+
linkml_classes[module].append(linkml_class)
90+
enum_schema = {
91+
"id": f"https://openminds.ebrains.eu/schemas/latest/enums?format=linkml",
92+
"name": "openMINDS-enums",
93+
"title": f'OpenMINDS instance library as LinkML enums, version "{schema_version}"',
94+
"description": f'Enums for the LinkML representation of openMINDS metadata framework, version "{schema_version}"',
95+
"license": "https://spdx.org/licenses/MIT.html",
96+
"prefixes": {
97+
"linkml": "https://w3id.org/linkml/",
98+
"schema": "http://schema.org/",
99+
"omi": "https://openminds.ebrains.eu",
100+
},
101+
"default_prefix": "omi",
102+
"imports": ["linkml:types"] + linkml_enum_imports,
103+
}
104+
target_file = os.path.join("target", "schemas", schema_version, f"enums.yaml")
105+
os.makedirs(os.path.dirname(target_file), exist_ok=True)
106+
with open(target_file, "w") as fp:
107+
yaml.dump(enum_schema, fp, sort_keys=False)
108+
109+
for module_name, class_list in linkml_classes.items():
110+
target_file = os.path.join(
111+
"target", "schemas", schema_version, f"{module_name}.yaml"
112+
)
113+
os.makedirs(os.path.dirname(target_file), exist_ok=True)
114+
module_schema = {
115+
"id": f"https://openminds.ebrains.eu/schemas/latest/{module_name}?format=linkml",
116+
"name": f"openMINDS-{module_name}",
117+
"title": f'OpenMINDS {module_name} module, version "{schema_version}"',
118+
"description": f'Schemas for the {module_name} module of the openMINDS metadata framework, version "{schema_version}"',
119+
"license": "https://spdx.org/licenses/MIT.html",
120+
"imports": ["linkml:types", "slots", "enums"],
121+
"prefixes": {
122+
"linkml": "https://w3id.org/linkml/",
123+
"schema": "http://schema.org/",
124+
"omi": "https://openminds.ebrains.eu",
125+
},
126+
"default_prefix": "omi",
127+
"classes": class_list,
128+
}
129+
imports.append(module_name)
130+
with open(target_file, "w") as fp:
131+
yaml.dump(module_schema, fp, sort_keys=False)
93132

94133
# Step 6 - write slots file
95134
with open(
96-
os.path.join("target", "schemas", schema_version, f"slots.yml"),
135+
os.path.join("target", "schemas", schema_version, f"slots.yaml"),
97136
"w",
98137
) as fp:
99-
yaml.dump({"slots": slots}, fp)
138+
yaml.dump({"slots": slots}, fp, sort_keys=False)
100139

101140
# Step 7 - create overall schema file
102141
schema_metadata = {
103142
"id": f"https://openminds.ebrains.eu/schemas/latest/?format=linkml",
104-
"name": f'OpenMINDS version "{schema_version}"',
143+
"name": "openMINDS",
144+
"title": f'OpenMINDS version "{schema_version}"',
105145
"description": f'The complete collection of schemas for all metadata models of the openMINDS metadata framework, version "{schema_version}"',
106146
"license": "https://spdx.org/licenses/MIT.html",
107147
"imports": ["linkml:types", "slots"] + imports,
@@ -118,4 +158,4 @@
118158
),
119159
"w",
120160
) as fp:
121-
yaml.dump(schema_metadata, fp)
161+
yaml.dump(schema_metadata, fp, sort_keys=False)

pipeline/translator.py

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ def __init__(self, schema_file_path: str, root_path: str, slots: Dict):
2020
self.slots = slots
2121

2222
def _target_file_without_extension(self) -> str:
23-
return os.path.join(
24-
self.version, *self.relative_path_without_extension
25-
)
23+
return os.path.join(self.version, *self.relative_path_without_extension)
2624

2725
def _resolve_string_property(self, property) -> Dict:
2826
format_map = { # see https://linkml.io/linkml-registry/
@@ -141,7 +139,7 @@ def _translate_property_specifications(
141139
"float": self._resolve_number_property,
142140
"integer": self._resolve_number_property,
143141
"object": self._resolve_object_property,
144-
"array": self._resolve_array_property
142+
"array": self._resolve_array_property,
145143
}
146144

147145
if isinstance(prop_type, list):
@@ -207,44 +205,52 @@ def update_slots(self):
207205
if value != slot_definition["range"]:
208206
slot_definition["any_of"] = [
209207
{"range": slot_definition.pop("range")},
210-
{"range": value}
208+
{"range": value},
211209
]
212210
elif "any_of" in slot_definition:
213-
allowed_values = [item["range"] for item in slot_definition["any_of"]]
211+
allowed_values = [
212+
item["range"] for item in slot_definition["any_of"]
213+
]
214214
if value not in allowed_values:
215215
slot_definition["any_of"].append({"range": value})
216216
else:
217217
slot_definition["range"] = value
218218
elif key == "any_of":
219219
if "range" in slot_definition:
220-
slot_definition["any_of"] = value + [{"range": slot_definition.pop("range")}]
220+
slot_definition["any_of"] = value + [
221+
{"range": slot_definition.pop("range")}
222+
]
221223
elif "any_of" in slot_definition:
222-
allowed_values = [item["range"] for item in slot_definition["any_of"]]
224+
allowed_values = [
225+
item["range"] for item in slot_definition["any_of"]
226+
]
223227
used_values = [item["range"] for item in slot_usage["any_of"]]
224-
slot_definition["any_of"] = [{"range": item} for item in set(allowed_values + used_values)]
228+
slot_definition["any_of"] = [
229+
{"range": item}
230+
for item in set(allowed_values + used_values)
231+
]
225232
else:
226233
slot_definition["any_of"] = value
227234
else:
228235
if key in slot_definition:
229-
if isinstance(slot_definition[key], dict) and "ALTERNATES" in slot_definition[key]:
236+
if (
237+
isinstance(slot_definition[key], dict)
238+
and "ALTERNATES" in slot_definition[key]
239+
):
230240
slot_definition[key]["ALTERNATES"].append(value)
231241
elif slot_definition[key] == value:
232242
pass
233243
else:
234-
slot_definition[key] = {"ALTERNATES": [slot_definition[key], value]}
244+
slot_definition[key] = {
245+
"ALTERNATES": [slot_definition[key], value]
246+
}
235247
else:
236248
slot_definition[key] = value
237249

238250
def build(self):
239-
target_file = os.path.join(
240-
"target", "schemas", f"{self._target_file_without_extension()}.yaml"
241-
)
242-
os.makedirs(os.path.dirname(target_file), exist_ok=True)
243251
short_type = get_short_name(self._schema_payload["_type"])
244252
self.translate()
245-
246-
with open(target_file, "w") as fp:
247-
yaml.dump({short_type: self._translated_schema}, fp)
253+
return {short_type: self._translated_schema}
248254

249255

250256
class LinkMLEnumBuilder(object):
@@ -260,11 +266,8 @@ def __init__(self, schema_file_path: str, root_path: str, instances: Dict):
260266
self._schema_payload = json.load(schema_f)
261267
self.instances = instances
262268

263-
264269
def _target_file_without_extension(self) -> str:
265-
return os.path.join(
266-
*self.relative_path_without_extension
267-
)
270+
return os.path.join(*self.relative_path_without_extension)
268271

269272
def translate(self):
270273
def build_enum(instance):
@@ -273,27 +276,47 @@ def build_enum(instance):
273276
enum["description"] = instance["definition"]
274277
"meaning"
275278
return enum
279+
276280
instances_payload = self.instances[self._schema_payload["_type"]]
277281
self._translated_schema = {
278282
"enum_uri": self._schema_payload["_type"],
279283
"title": self._schema_payload["label"],
280-
"permissible_values": {
281-
instance["name"]: build_enum(instance) for instance in instances_payload
282-
}
283284
}
284285
if "description" in self._schema_payload:
285286
self._translated_schema["description"] = self._schema_payload["description"]
287+
self._translated_schema["permissible_values"] = {
288+
instance["name"]: build_enum(instance) for instance in instances_payload
289+
}
286290

287291
def build(self):
288292
target_file = os.path.join(
289-
"target", "schemas", self.version, "enums", f"{self._target_file_without_extension()}.yaml"
293+
"target",
294+
"schemas",
295+
self.version,
296+
"enums",
297+
f"{self._target_file_without_extension()}.yaml",
290298
)
291299
os.makedirs(os.path.dirname(target_file), exist_ok=True)
292300
short_type = get_short_name(self._schema_payload["_type"])
293301
self.translate()
294302

303+
enum_schema = {
304+
"id": f"https://openminds.ebrains.eu/schemas/latest/enums/{short_type}?format=linkml",
305+
"name": f"openMINDS-enums-{short_type}",
306+
"title": f"OpenMINDS enum for {short_type}",
307+
"description": f"OpenMINDS enum for {short_type}",
308+
"license": "https://spdx.org/licenses/MIT.html",
309+
"prefixes": {
310+
"linkml": "https://w3id.org/linkml/",
311+
"schema": "http://schema.org/",
312+
"omi": "https://openminds.ebrains.eu",
313+
},
314+
"default_prefix": "omi",
315+
"enums": {short_type: self._translated_schema},
316+
}
317+
295318
with open(target_file, "w") as fp:
296-
yaml.dump({short_type: self._translated_schema}, fp)
319+
yaml.dump(enum_schema, fp, sort_keys=False)
297320

298321

299322
class LinkMLSlotBuilder:
@@ -315,7 +338,9 @@ def build(self):
315338
slot["range"] = get_short_name(target_classes[0])
316339
elif len(target_classes) > 1:
317340
assert "range" not in slot
318-
slot["any_of"] = [{"range": get_short_name(tc)} for tc in target_classes]
341+
slot["any_of"] = [
342+
{"range": get_short_name(tc)} for tc in target_classes
343+
]
319344
if "asString" in property:
320345
# can't do anything yet, because string can be used to represent various LinkML types
321346
# todo: fix this

0 commit comments

Comments
 (0)