Skip to content

Commit 16b522b

Browse files
author
lzehl
committed
first steps towards a correct translation from openMINDS-Schema to JSON-Schema
1 parent 6a3b52c commit 16b522b

File tree

2 files changed

+162
-5
lines changed

2 files changed

+162
-5
lines changed

build.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
for schema_version in schema_loader.get_schema_versions():
1818

1919
# Step 2 - find all involved schemas for the current version
20-
schemas = schema_loader.find_schemas(schema_version)
20+
schemas_file_paths = schema_loader.find_schemas(schema_version)
21+
22+
for schema_file_path in schemas_file_paths:
23+
# Step 3 - translate and build each openMINDS schema as JSON-Schema
24+
JSONSchemaBuilder(schema_file_path, schema_loader.schemas_sources).build()
25+
26+
27+
2128

22-
for schema in schemas:
23-
# Step 3 - build documentation for version specific schema
24-
JSONSchemaBuilder(schema, schema_loader.schemas_sources).build()

pipeline/translator.py

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,162 @@ def __init__(self, schema_file_path:str, root_path:str):
1414
def _target_file_without_extension(self) -> str:
1515
return os.path.join(self.version, "/".join(self.relative_path_without_extension))
1616

17+
def _resolve_string_property(self, property) -> Dict:
18+
string_property_spec = {}
19+
if "_formats" in property and property["_formats"]:
20+
if len(property["_formats"]) == 1:
21+
string_property_spec["format"] = property["_formats"][0]
22+
string_property_spec["type"] = property["type"]
23+
elif len(property["_formats"]) > 1:
24+
string_property_spec["anyOf"] = []
25+
for format in property["_formats"]:
26+
string_property_spec["anyOf"].append({
27+
"type": property["type"],
28+
"format": format
29+
})
30+
else:
31+
string_property_spec["type"] = property["type"]
32+
if "pattern" in property and property["pattern"]:
33+
string_property_spec["pattern"] = property["pattern"]
34+
if "minLength" in property and property["minLength"]:
35+
string_property_spec["minLength"] = property["minLength"]
36+
if "maxLength" in property and property["maxLength"]:
37+
string_property_spec["maxLength"] = property["maxLength"]
38+
return string_property_spec
39+
40+
def _resolve_number_property(self, property) -> Dict:
41+
number_property_spec = {"type": property["type"]}
42+
if "multipleOf" in property and property["multipleOf"]:
43+
number_property_spec["multipleOf"] = property["multipleOf"]
44+
if "minimum" in property and property["minimum"]:
45+
number_property_spec["minimum"] = property["minimum"]
46+
if "maximum" in property and property["maximum"]:
47+
number_property_spec["maximum"] = property["maximum"]
48+
return number_property_spec
49+
50+
def _resolve_object_property(self, property):
51+
object_property_spec = {}
52+
if "_embeddedTypes" in property and property["_embeddedTypes"]:
53+
if len(property["_embeddedTypes"]) == 1:
54+
object_property_spec["type"] = "object"
55+
object_property_spec["$ref"] = f"{property['_embeddedTypes'][0]}?format=json-schema"
56+
elif len(property["_embeddedTypes"]) > 1:
57+
object_property_spec["anyOf"] = []
58+
for embedded_type in property["_embeddedTypes"]:
59+
object_property_spec["anyOf"].append({
60+
"type": "object",
61+
"$ref": f"{embedded_type}?format=json-schema"
62+
})
63+
elif "_linkedTypes" in property and property["_linkedTypes"]:
64+
object_property_spec["type"] = "object"
65+
object_property_spec["if"] = {
66+
"required": ["@type"]
67+
}
68+
object_property_spec["then"] = {
69+
"properties": {
70+
"@id": {
71+
"type": "string",
72+
"format": "iri"
73+
},
74+
"@type": {
75+
"type": "string",
76+
"format": "iri",
77+
"enum": property["_linkedTypes"]
78+
}},
79+
"required": ["@id"]
80+
}
81+
object_property_spec["else"] = {
82+
"properties": {
83+
"@id": {
84+
"type": "string",
85+
"format": "iri"
86+
}
87+
},
88+
"required": ["@id"]
89+
}
90+
91+
return object_property_spec
92+
93+
def _resolve_array_property(self, property):
94+
array_property_spec = {"type": property["type"]}
95+
if "items" in property and property["items"]:
96+
if "type" in property["items"] and property["items"]["type"]:
97+
if property["items"]["type"] == "string":
98+
array_property_spec["items"] = self._resolve_string_property(property["items"])
99+
elif property["items"]["type"] in ["number", "float", "integer"]:
100+
array_property_spec["items"] = self._resolve_number_property(property["items"])
101+
else:
102+
array_property_spec["items"] = {"type": "UNKNOWN_TYPE"}
103+
else:
104+
array_property_spec["items"] = self._resolve_object_property(property)
105+
if "minItems" in property and property["minItems"]:
106+
array_property_spec["minItems"] = property["minItems"]
107+
if "maxItems" in property and property["maxItems"]:
108+
array_property_spec["maxItems"] = property["maxItems"]
109+
if "uniqueItems" in property and property["uniqueItems"]:
110+
array_property_spec["uniqueItems"] = property["uniqueItems"]
111+
112+
return array_property_spec
113+
114+
def _translate_property_specifications(self, property) -> Dict:
115+
self._translated_prop_spec = {}
116+
117+
prop_description = property["description"] if "description" in property else None
118+
self._translated_prop_spec["description"] = prop_description
119+
120+
self._translated_prop_spec["name"] = property["name"]
121+
122+
prop_type = property["type"] if "type" in property else "object"
123+
124+
if prop_type == "string":
125+
self._translated_prop_spec.update(self._resolve_string_property(property))
126+
elif prop_type in ["number", "float", "integer"]:
127+
self._translated_prop_spec.update(self._resolve_number_property(property))
128+
elif prop_type == "object":
129+
self._translated_prop_spec.update(self._resolve_object_property(property))
130+
elif prop_type == "array":
131+
self._translated_prop_spec.update(self._resolve_array_property(property))
132+
else:
133+
self._translated_prop_spec["type"] = "UNKNOWN_TYPE"
134+
135+
return self._translated_prop_spec
136+
137+
def translate(self):
138+
# set required properties
139+
required_prop = self._schema_payload["required"] if "required" in self._schema_payload else []
140+
required_prop.extend(["@id", "@type"])
141+
# set description
142+
description = self._schema_payload['description'] if "description" in self._schema_payload else None
143+
144+
self._translated_schema = {
145+
"$id": f"{self._schema_payload['_type']}?format=json-schema",
146+
"$schema": "http://json-schema.org/draft-07/schema#",
147+
"description": description,
148+
"properties": {
149+
"@id": {
150+
"description": "Metadata node identifier.",
151+
"type": "string"
152+
},
153+
"@type": {
154+
"const": self._schema_payload['_type'],
155+
"description": "Metadata schema identifier.",
156+
"type": "string"
157+
}
158+
},
159+
"required": sorted(required_prop),
160+
"title": self._schema_payload['name'],
161+
"type": "object"
162+
}
163+
164+
if "properties" in self._schema_payload and self._schema_payload["properties"]:
165+
for prop_name, prop_spec in self._schema_payload["properties"].items():
166+
self._translated_schema["properties"][prop_name] = self._translate_property_specifications(prop_spec)
167+
17168
def build(self):
18169
target_file = os.path.join("target", "schemas", f"{self._target_file_without_extension()}.schema.json")
19170
os.makedirs(os.path.dirname(target_file), exist_ok=True)
20171

172+
self.translate()
173+
21174
with open(target_file, "w") as target_file:
22-
target_file.write(json.dumps(self._schema_payload, indent=2))
175+
target_file.write(json.dumps(self._translated_schema, indent=2, sort_keys=True))

0 commit comments

Comments
 (0)