rdf-construct 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. rdf_construct/__init__.py +12 -0
  2. rdf_construct/__main__.py +0 -0
  3. rdf_construct/cli.py +3429 -0
  4. rdf_construct/core/__init__.py +33 -0
  5. rdf_construct/core/config.py +116 -0
  6. rdf_construct/core/ordering.py +219 -0
  7. rdf_construct/core/predicate_order.py +212 -0
  8. rdf_construct/core/profile.py +157 -0
  9. rdf_construct/core/selector.py +64 -0
  10. rdf_construct/core/serialiser.py +232 -0
  11. rdf_construct/core/utils.py +89 -0
  12. rdf_construct/cq/__init__.py +77 -0
  13. rdf_construct/cq/expectations.py +365 -0
  14. rdf_construct/cq/formatters/__init__.py +45 -0
  15. rdf_construct/cq/formatters/json.py +104 -0
  16. rdf_construct/cq/formatters/junit.py +104 -0
  17. rdf_construct/cq/formatters/text.py +146 -0
  18. rdf_construct/cq/loader.py +300 -0
  19. rdf_construct/cq/runner.py +321 -0
  20. rdf_construct/diff/__init__.py +59 -0
  21. rdf_construct/diff/change_types.py +214 -0
  22. rdf_construct/diff/comparator.py +338 -0
  23. rdf_construct/diff/filters.py +133 -0
  24. rdf_construct/diff/formatters/__init__.py +71 -0
  25. rdf_construct/diff/formatters/json.py +192 -0
  26. rdf_construct/diff/formatters/markdown.py +210 -0
  27. rdf_construct/diff/formatters/text.py +195 -0
  28. rdf_construct/docs/__init__.py +60 -0
  29. rdf_construct/docs/config.py +238 -0
  30. rdf_construct/docs/extractors.py +603 -0
  31. rdf_construct/docs/generator.py +360 -0
  32. rdf_construct/docs/renderers/__init__.py +7 -0
  33. rdf_construct/docs/renderers/html.py +803 -0
  34. rdf_construct/docs/renderers/json.py +390 -0
  35. rdf_construct/docs/renderers/markdown.py +628 -0
  36. rdf_construct/docs/search.py +278 -0
  37. rdf_construct/docs/templates/html/base.html.jinja +44 -0
  38. rdf_construct/docs/templates/html/class.html.jinja +152 -0
  39. rdf_construct/docs/templates/html/hierarchy.html.jinja +28 -0
  40. rdf_construct/docs/templates/html/index.html.jinja +110 -0
  41. rdf_construct/docs/templates/html/instance.html.jinja +90 -0
  42. rdf_construct/docs/templates/html/namespaces.html.jinja +37 -0
  43. rdf_construct/docs/templates/html/property.html.jinja +124 -0
  44. rdf_construct/docs/templates/html/single_page.html.jinja +169 -0
  45. rdf_construct/lint/__init__.py +75 -0
  46. rdf_construct/lint/config.py +214 -0
  47. rdf_construct/lint/engine.py +396 -0
  48. rdf_construct/lint/formatters.py +327 -0
  49. rdf_construct/lint/rules.py +692 -0
  50. rdf_construct/localise/__init__.py +114 -0
  51. rdf_construct/localise/config.py +508 -0
  52. rdf_construct/localise/extractor.py +427 -0
  53. rdf_construct/localise/formatters/__init__.py +36 -0
  54. rdf_construct/localise/formatters/markdown.py +229 -0
  55. rdf_construct/localise/formatters/text.py +224 -0
  56. rdf_construct/localise/merger.py +346 -0
  57. rdf_construct/localise/reporter.py +356 -0
  58. rdf_construct/main.py +6 -0
  59. rdf_construct/merge/__init__.py +165 -0
  60. rdf_construct/merge/config.py +354 -0
  61. rdf_construct/merge/conflicts.py +281 -0
  62. rdf_construct/merge/formatters.py +426 -0
  63. rdf_construct/merge/merger.py +425 -0
  64. rdf_construct/merge/migrator.py +339 -0
  65. rdf_construct/merge/rules.py +377 -0
  66. rdf_construct/merge/splitter.py +1102 -0
  67. rdf_construct/puml2rdf/__init__.py +103 -0
  68. rdf_construct/puml2rdf/config.py +230 -0
  69. rdf_construct/puml2rdf/converter.py +420 -0
  70. rdf_construct/puml2rdf/merger.py +200 -0
  71. rdf_construct/puml2rdf/model.py +202 -0
  72. rdf_construct/puml2rdf/parser.py +565 -0
  73. rdf_construct/puml2rdf/validators.py +451 -0
  74. rdf_construct/refactor/__init__.py +72 -0
  75. rdf_construct/refactor/config.py +362 -0
  76. rdf_construct/refactor/deprecator.py +328 -0
  77. rdf_construct/refactor/formatters/__init__.py +8 -0
  78. rdf_construct/refactor/formatters/text.py +311 -0
  79. rdf_construct/refactor/renamer.py +294 -0
  80. rdf_construct/shacl/__init__.py +56 -0
  81. rdf_construct/shacl/config.py +166 -0
  82. rdf_construct/shacl/converters.py +520 -0
  83. rdf_construct/shacl/generator.py +364 -0
  84. rdf_construct/shacl/namespaces.py +93 -0
  85. rdf_construct/stats/__init__.py +29 -0
  86. rdf_construct/stats/collector.py +178 -0
  87. rdf_construct/stats/comparator.py +298 -0
  88. rdf_construct/stats/formatters/__init__.py +83 -0
  89. rdf_construct/stats/formatters/json.py +38 -0
  90. rdf_construct/stats/formatters/markdown.py +153 -0
  91. rdf_construct/stats/formatters/text.py +186 -0
  92. rdf_construct/stats/metrics/__init__.py +26 -0
  93. rdf_construct/stats/metrics/basic.py +147 -0
  94. rdf_construct/stats/metrics/complexity.py +137 -0
  95. rdf_construct/stats/metrics/connectivity.py +130 -0
  96. rdf_construct/stats/metrics/documentation.py +128 -0
  97. rdf_construct/stats/metrics/hierarchy.py +207 -0
  98. rdf_construct/stats/metrics/properties.py +88 -0
  99. rdf_construct/uml/__init__.py +22 -0
  100. rdf_construct/uml/context.py +194 -0
  101. rdf_construct/uml/mapper.py +371 -0
  102. rdf_construct/uml/odm_renderer.py +789 -0
  103. rdf_construct/uml/renderer.py +684 -0
  104. rdf_construct/uml/uml_layout.py +393 -0
  105. rdf_construct/uml/uml_style.py +613 -0
  106. rdf_construct-0.3.0.dist-info/METADATA +496 -0
  107. rdf_construct-0.3.0.dist-info/RECORD +110 -0
  108. rdf_construct-0.3.0.dist-info/WHEEL +4 -0
  109. rdf_construct-0.3.0.dist-info/entry_points.txt +3 -0
  110. rdf_construct-0.3.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,390 @@
1
+ """JSON documentation renderer for structured data output."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ if TYPE_CHECKING:
10
+ from ..config import DocsConfig
11
+ from ..extractors import ClassInfo, ExtractedEntities, InstanceInfo, PropertyInfo
12
+
13
+
14
+ class JSONRenderer:
15
+ """Renders ontology documentation as structured JSON files.
16
+
17
+ Produces machine-readable JSON that can be consumed by custom
18
+ renderers, APIs, or documentation systems.
19
+ """
20
+
21
+ def __init__(self, config: "DocsConfig") -> None:
22
+ """Initialise the JSON renderer.
23
+
24
+ Args:
25
+ config: Documentation configuration.
26
+ """
27
+ self.config = config
28
+
29
+ def _get_output_path(self, filename: str, subdir: str | None = None) -> Path:
30
+ """Get the full output path for a file.
31
+
32
+ Args:
33
+ filename: Name of the file.
34
+ subdir: Optional subdirectory.
35
+
36
+ Returns:
37
+ Full output path.
38
+ """
39
+ if subdir:
40
+ path = self.config.output_dir / subdir / filename
41
+ else:
42
+ path = self.config.output_dir / filename
43
+
44
+ path.parent.mkdir(parents=True, exist_ok=True)
45
+ return path
46
+
47
+ def _write_json(self, path: Path, data: Any) -> Path:
48
+ """Write JSON data to a file.
49
+
50
+ Args:
51
+ path: Output path.
52
+ data: Data to serialise.
53
+
54
+ Returns:
55
+ Path to the written file.
56
+ """
57
+ path.parent.mkdir(parents=True, exist_ok=True)
58
+ with open(path, "w", encoding="utf-8") as f:
59
+ json.dump(data, f, indent=2, default=str)
60
+ return path
61
+
62
+ def _class_to_dict(self, class_info: "ClassInfo") -> dict[str, Any]:
63
+ """Convert a ClassInfo to a dictionary.
64
+
65
+ Args:
66
+ class_info: Class to convert.
67
+
68
+ Returns:
69
+ Dictionary representation.
70
+ """
71
+ return {
72
+ "uri": str(class_info.uri),
73
+ "qname": class_info.qname,
74
+ "label": class_info.label,
75
+ "definition": class_info.definition,
76
+ "superclasses": [str(uri) for uri in class_info.superclasses],
77
+ "subclasses": [str(uri) for uri in class_info.subclasses],
78
+ "domain_of": [self._property_to_dict(p) for p in class_info.domain_of],
79
+ "range_of": [self._property_to_dict(p) for p in class_info.range_of],
80
+ "instances": [str(uri) for uri in class_info.instances],
81
+ "disjoint_with": [str(uri) for uri in class_info.disjoint_with],
82
+ "equivalent_to": [str(uri) for uri in class_info.equivalent_to],
83
+ "annotations": class_info.annotations,
84
+ }
85
+
86
+ def _property_to_dict(self, prop_info: "PropertyInfo") -> dict[str, Any]:
87
+ """Convert a PropertyInfo to a dictionary.
88
+
89
+ Args:
90
+ prop_info: Property to convert.
91
+
92
+ Returns:
93
+ Dictionary representation.
94
+ """
95
+ return {
96
+ "uri": str(prop_info.uri),
97
+ "qname": prop_info.qname,
98
+ "label": prop_info.label,
99
+ "definition": prop_info.definition,
100
+ "property_type": prop_info.property_type,
101
+ "domain": [str(uri) for uri in prop_info.domain],
102
+ "range": [str(uri) for uri in prop_info.range],
103
+ "superproperties": [str(uri) for uri in prop_info.superproperties],
104
+ "subproperties": [str(uri) for uri in prop_info.subproperties],
105
+ "is_functional": prop_info.is_functional,
106
+ "is_inverse_functional": prop_info.is_inverse_functional,
107
+ "inverse_of": str(prop_info.inverse_of) if prop_info.inverse_of else None,
108
+ "annotations": prop_info.annotations,
109
+ }
110
+
111
+ def _instance_to_dict(self, instance_info: "InstanceInfo") -> dict[str, Any]:
112
+ """Convert an InstanceInfo to a dictionary.
113
+
114
+ Args:
115
+ instance_info: Instance to convert.
116
+
117
+ Returns:
118
+ Dictionary representation.
119
+ """
120
+ # Convert properties to serialisable format
121
+ properties: dict[str, list[str]] = {}
122
+ for pred, values in instance_info.properties.items():
123
+ pred_str = str(pred)
124
+ properties[pred_str] = [str(v) for v in values]
125
+
126
+ return {
127
+ "uri": str(instance_info.uri),
128
+ "qname": instance_info.qname,
129
+ "label": instance_info.label,
130
+ "definition": instance_info.definition,
131
+ "types": [str(uri) for uri in instance_info.types],
132
+ "properties": properties,
133
+ "annotations": instance_info.annotations,
134
+ }
135
+
136
+ def _ontology_to_dict(self, entities: "ExtractedEntities") -> dict[str, Any]:
137
+ """Convert ontology info to a dictionary.
138
+
139
+ Args:
140
+ entities: All extracted entities.
141
+
142
+ Returns:
143
+ Dictionary representation.
144
+ """
145
+ onto = entities.ontology
146
+ return {
147
+ "uri": str(onto.uri) if onto.uri else None,
148
+ "title": onto.title,
149
+ "description": onto.description,
150
+ "version": onto.version,
151
+ "creators": onto.creators,
152
+ "contributors": onto.contributors,
153
+ "imports": [str(uri) for uri in onto.imports],
154
+ "namespaces": onto.namespaces,
155
+ "annotations": onto.annotations,
156
+ }
157
+
158
+ def render_index(self, entities: "ExtractedEntities") -> Path:
159
+ """Render the main index as JSON.
160
+
161
+ Args:
162
+ entities: All extracted entities.
163
+
164
+ Returns:
165
+ Path to the rendered file.
166
+ """
167
+ data = {
168
+ "ontology": self._ontology_to_dict(entities),
169
+ "statistics": {
170
+ "classes": len(entities.classes),
171
+ "object_properties": len(entities.object_properties),
172
+ "datatype_properties": len(entities.datatype_properties),
173
+ "annotation_properties": len(entities.annotation_properties),
174
+ "instances": len(entities.instances),
175
+ },
176
+ "classes": [
177
+ {
178
+ "uri": str(c.uri),
179
+ "qname": c.qname,
180
+ "label": c.label,
181
+ }
182
+ for c in entities.classes
183
+ ],
184
+ "object_properties": [
185
+ {
186
+ "uri": str(p.uri),
187
+ "qname": p.qname,
188
+ "label": p.label,
189
+ }
190
+ for p in entities.object_properties
191
+ ],
192
+ "datatype_properties": [
193
+ {
194
+ "uri": str(p.uri),
195
+ "qname": p.qname,
196
+ "label": p.label,
197
+ }
198
+ for p in entities.datatype_properties
199
+ ],
200
+ "annotation_properties": [
201
+ {
202
+ "uri": str(p.uri),
203
+ "qname": p.qname,
204
+ "label": p.label,
205
+ }
206
+ for p in entities.annotation_properties
207
+ ],
208
+ "instances": [
209
+ {
210
+ "uri": str(i.uri),
211
+ "qname": i.qname,
212
+ "label": i.label,
213
+ }
214
+ for i in entities.instances
215
+ ],
216
+ }
217
+
218
+ return self._write_json(self._get_output_path("index.json"), data)
219
+
220
+ def render_hierarchy(self, entities: "ExtractedEntities") -> Path:
221
+ """Render the class hierarchy as JSON.
222
+
223
+ Args:
224
+ entities: All extracted entities.
225
+
226
+ Returns:
227
+ Path to the rendered file.
228
+ """
229
+ hierarchy = self._build_hierarchy_tree(entities.classes)
230
+
231
+ def tree_to_json(nodes: list[dict[str, Any]]) -> list[dict[str, Any]]:
232
+ return [
233
+ {
234
+ "uri": str(node["class"].uri),
235
+ "qname": node["class"].qname,
236
+ "label": node["class"].label,
237
+ "children": tree_to_json(node["children"]),
238
+ }
239
+ for node in nodes
240
+ ]
241
+
242
+ data = {"hierarchy": tree_to_json(hierarchy)}
243
+ return self._write_json(self._get_output_path("hierarchy.json"), data)
244
+
245
+ def _build_hierarchy_tree(
246
+ self,
247
+ classes: list["ClassInfo"],
248
+ ) -> list[dict[str, Any]]:
249
+ """Build a tree structure for the class hierarchy.
250
+
251
+ Args:
252
+ classes: List of all classes.
253
+
254
+ Returns:
255
+ Nested list structure representing the hierarchy.
256
+ """
257
+ class_by_uri = {str(c.uri): c for c in classes}
258
+ internal_uris = set(class_by_uri.keys())
259
+ root_classes = []
260
+
261
+ for c in classes:
262
+ has_internal_parent = any(
263
+ str(parent) in internal_uris for parent in c.superclasses
264
+ )
265
+ if not has_internal_parent:
266
+ root_classes.append(c)
267
+
268
+ def build_node(class_info: "ClassInfo") -> dict[str, Any]:
269
+ children = []
270
+ for child_uri in class_info.subclasses:
271
+ child_key = str(child_uri)
272
+ if child_key in class_by_uri:
273
+ children.append(build_node(class_by_uri[child_key]))
274
+
275
+ return {
276
+ "class": class_info,
277
+ "children": sorted(children, key=lambda n: n["class"].qname),
278
+ }
279
+
280
+ return sorted(
281
+ [build_node(c) for c in root_classes],
282
+ key=lambda n: n["class"].qname,
283
+ )
284
+
285
+ def render_class(
286
+ self,
287
+ class_info: "ClassInfo",
288
+ entities: "ExtractedEntities",
289
+ ) -> Path:
290
+ """Render a class as JSON.
291
+
292
+ Args:
293
+ class_info: Class to render.
294
+ entities: All extracted entities.
295
+
296
+ Returns:
297
+ Path to the rendered file.
298
+ """
299
+ data = self._class_to_dict(class_info)
300
+
301
+ from ..config import entity_to_path
302
+ rel_path = entity_to_path(class_info.qname, "class", self.config, extension=".json")
303
+ return self._write_json(self.config.output_dir / rel_path, data)
304
+
305
+ def render_property(
306
+ self,
307
+ prop_info: "PropertyInfo",
308
+ entities: "ExtractedEntities",
309
+ ) -> Path:
310
+ """Render a property as JSON.
311
+
312
+ Args:
313
+ prop_info: Property to render.
314
+ entities: All extracted entities.
315
+
316
+ Returns:
317
+ Path to the rendered file.
318
+ """
319
+ data = self._property_to_dict(prop_info)
320
+
321
+ entity_type = f"{prop_info.property_type}_property"
322
+ from ..config import entity_to_path
323
+ rel_path = entity_to_path(prop_info.qname, entity_type, self.config, extension=".json")
324
+ return self._write_json(self.config.output_dir / rel_path, data)
325
+
326
+ def render_instance(
327
+ self,
328
+ instance_info: "InstanceInfo",
329
+ entities: "ExtractedEntities",
330
+ ) -> Path:
331
+ """Render an instance as JSON.
332
+
333
+ Args:
334
+ instance_info: Instance to render.
335
+ entities: All extracted entities.
336
+
337
+ Returns:
338
+ Path to the rendered file.
339
+ """
340
+ data = self._instance_to_dict(instance_info)
341
+
342
+ from ..config import entity_to_path
343
+ rel_path = entity_to_path(instance_info.qname, "instance", self.config, extension=".json")
344
+ return self._write_json(self.config.output_dir / rel_path, data)
345
+
346
+ def render_namespaces(self, entities: "ExtractedEntities") -> Path:
347
+ """Render namespaces as JSON.
348
+
349
+ Args:
350
+ entities: All extracted entities.
351
+
352
+ Returns:
353
+ Path to the rendered file.
354
+ """
355
+ data = {
356
+ "namespaces": entities.ontology.namespaces,
357
+ }
358
+ return self._write_json(self._get_output_path("namespaces.json"), data)
359
+
360
+ def render_single_page(self, entities: "ExtractedEntities") -> Path:
361
+ """Render all documentation as a single JSON file.
362
+
363
+ Args:
364
+ entities: All extracted entities.
365
+
366
+ Returns:
367
+ Path to the rendered file.
368
+ """
369
+ data = {
370
+ "ontology": self._ontology_to_dict(entities),
371
+ "classes": [self._class_to_dict(c) for c in entities.classes],
372
+ "object_properties": [
373
+ self._property_to_dict(p) for p in entities.object_properties
374
+ ],
375
+ "datatype_properties": [
376
+ self._property_to_dict(p) for p in entities.datatype_properties
377
+ ],
378
+ "annotation_properties": [
379
+ self._property_to_dict(p) for p in entities.annotation_properties
380
+ ],
381
+ "instances": [
382
+ self._instance_to_dict(i) for i in entities.instances
383
+ ],
384
+ }
385
+
386
+ return self._write_json(self._get_output_path("ontology.json"), data)
387
+
388
+ def copy_assets(self) -> None:
389
+ """Copy static assets. No assets needed for JSON."""
390
+ pass