rdf-construct 0.2.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.
- rdf_construct/__init__.py +12 -0
- rdf_construct/__main__.py +0 -0
- rdf_construct/cli.py +1762 -0
- rdf_construct/core/__init__.py +33 -0
- rdf_construct/core/config.py +116 -0
- rdf_construct/core/ordering.py +219 -0
- rdf_construct/core/predicate_order.py +212 -0
- rdf_construct/core/profile.py +157 -0
- rdf_construct/core/selector.py +64 -0
- rdf_construct/core/serialiser.py +232 -0
- rdf_construct/core/utils.py +89 -0
- rdf_construct/cq/__init__.py +77 -0
- rdf_construct/cq/expectations.py +365 -0
- rdf_construct/cq/formatters/__init__.py +45 -0
- rdf_construct/cq/formatters/json.py +104 -0
- rdf_construct/cq/formatters/junit.py +104 -0
- rdf_construct/cq/formatters/text.py +146 -0
- rdf_construct/cq/loader.py +300 -0
- rdf_construct/cq/runner.py +321 -0
- rdf_construct/diff/__init__.py +59 -0
- rdf_construct/diff/change_types.py +214 -0
- rdf_construct/diff/comparator.py +338 -0
- rdf_construct/diff/filters.py +133 -0
- rdf_construct/diff/formatters/__init__.py +71 -0
- rdf_construct/diff/formatters/json.py +192 -0
- rdf_construct/diff/formatters/markdown.py +210 -0
- rdf_construct/diff/formatters/text.py +195 -0
- rdf_construct/docs/__init__.py +60 -0
- rdf_construct/docs/config.py +238 -0
- rdf_construct/docs/extractors.py +603 -0
- rdf_construct/docs/generator.py +360 -0
- rdf_construct/docs/renderers/__init__.py +7 -0
- rdf_construct/docs/renderers/html.py +803 -0
- rdf_construct/docs/renderers/json.py +390 -0
- rdf_construct/docs/renderers/markdown.py +628 -0
- rdf_construct/docs/search.py +278 -0
- rdf_construct/docs/templates/html/base.html.jinja +44 -0
- rdf_construct/docs/templates/html/class.html.jinja +152 -0
- rdf_construct/docs/templates/html/hierarchy.html.jinja +28 -0
- rdf_construct/docs/templates/html/index.html.jinja +110 -0
- rdf_construct/docs/templates/html/instance.html.jinja +90 -0
- rdf_construct/docs/templates/html/namespaces.html.jinja +37 -0
- rdf_construct/docs/templates/html/property.html.jinja +124 -0
- rdf_construct/docs/templates/html/single_page.html.jinja +169 -0
- rdf_construct/lint/__init__.py +75 -0
- rdf_construct/lint/config.py +214 -0
- rdf_construct/lint/engine.py +396 -0
- rdf_construct/lint/formatters.py +327 -0
- rdf_construct/lint/rules.py +692 -0
- rdf_construct/main.py +6 -0
- rdf_construct/puml2rdf/__init__.py +103 -0
- rdf_construct/puml2rdf/config.py +230 -0
- rdf_construct/puml2rdf/converter.py +420 -0
- rdf_construct/puml2rdf/merger.py +200 -0
- rdf_construct/puml2rdf/model.py +202 -0
- rdf_construct/puml2rdf/parser.py +565 -0
- rdf_construct/puml2rdf/validators.py +451 -0
- rdf_construct/shacl/__init__.py +56 -0
- rdf_construct/shacl/config.py +166 -0
- rdf_construct/shacl/converters.py +520 -0
- rdf_construct/shacl/generator.py +364 -0
- rdf_construct/shacl/namespaces.py +93 -0
- rdf_construct/stats/__init__.py +29 -0
- rdf_construct/stats/collector.py +178 -0
- rdf_construct/stats/comparator.py +298 -0
- rdf_construct/stats/formatters/__init__.py +83 -0
- rdf_construct/stats/formatters/json.py +38 -0
- rdf_construct/stats/formatters/markdown.py +153 -0
- rdf_construct/stats/formatters/text.py +186 -0
- rdf_construct/stats/metrics/__init__.py +26 -0
- rdf_construct/stats/metrics/basic.py +147 -0
- rdf_construct/stats/metrics/complexity.py +137 -0
- rdf_construct/stats/metrics/connectivity.py +130 -0
- rdf_construct/stats/metrics/documentation.py +128 -0
- rdf_construct/stats/metrics/hierarchy.py +207 -0
- rdf_construct/stats/metrics/properties.py +88 -0
- rdf_construct/uml/__init__.py +22 -0
- rdf_construct/uml/context.py +194 -0
- rdf_construct/uml/mapper.py +371 -0
- rdf_construct/uml/odm_renderer.py +789 -0
- rdf_construct/uml/renderer.py +684 -0
- rdf_construct/uml/uml_layout.py +393 -0
- rdf_construct/uml/uml_style.py +613 -0
- rdf_construct-0.2.0.dist-info/METADATA +431 -0
- rdf_construct-0.2.0.dist-info/RECORD +88 -0
- rdf_construct-0.2.0.dist-info/WHEEL +4 -0
- rdf_construct-0.2.0.dist-info/entry_points.txt +3 -0
- rdf_construct-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
"""Markdown documentation renderer."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..config import DocsConfig
|
|
10
|
+
from ..extractors import ClassInfo, ExtractedEntities, InstanceInfo, PropertyInfo
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MarkdownRenderer:
|
|
14
|
+
"""Renders ontology documentation as Markdown files.
|
|
15
|
+
|
|
16
|
+
Generates GitHub/GitLab-compatible Markdown with optional
|
|
17
|
+
Jekyll/Hugo frontmatter.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, config: "DocsConfig") -> None:
|
|
21
|
+
"""Initialise the Markdown renderer.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
config: Documentation configuration.
|
|
25
|
+
"""
|
|
26
|
+
self.config = config
|
|
27
|
+
|
|
28
|
+
def _get_output_path(self, filename: str, subdir: str | None = None) -> Path:
|
|
29
|
+
"""Get the full output path for a file.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
filename: Name of the file.
|
|
33
|
+
subdir: Optional subdirectory.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Full output path.
|
|
37
|
+
"""
|
|
38
|
+
if subdir:
|
|
39
|
+
path = self.config.output_dir / subdir / filename
|
|
40
|
+
else:
|
|
41
|
+
path = self.config.output_dir / filename
|
|
42
|
+
|
|
43
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
return path
|
|
45
|
+
|
|
46
|
+
def _write_file(self, path: Path, content: str) -> Path:
|
|
47
|
+
"""Write content to a file.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
path: Output path.
|
|
51
|
+
content: Content to write.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Path to the written file.
|
|
55
|
+
"""
|
|
56
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
path.write_text(content, encoding="utf-8")
|
|
58
|
+
return path
|
|
59
|
+
|
|
60
|
+
def _entity_link(self, qname: str, entity_type: str, label: str | None = None) -> str:
|
|
61
|
+
"""Generate a markdown link to an entity.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
qname: Entity qualified name.
|
|
65
|
+
entity_type: Type of entity.
|
|
66
|
+
label: Optional display label.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Markdown link string.
|
|
70
|
+
"""
|
|
71
|
+
from ..config import entity_to_path
|
|
72
|
+
|
|
73
|
+
display = label or qname
|
|
74
|
+
path = entity_to_path(qname, entity_type, self.config, extension=".md")
|
|
75
|
+
# Make path relative from root
|
|
76
|
+
return f"[{display}]({path})"
|
|
77
|
+
|
|
78
|
+
def _frontmatter(self, **kwargs: Any) -> str:
|
|
79
|
+
"""Generate YAML frontmatter.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
**kwargs: Frontmatter fields.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Frontmatter string.
|
|
86
|
+
"""
|
|
87
|
+
lines = ["---"]
|
|
88
|
+
for key, value in kwargs.items():
|
|
89
|
+
if isinstance(value, list):
|
|
90
|
+
lines.append(f"{key}:")
|
|
91
|
+
for item in value:
|
|
92
|
+
lines.append(f" - {item}")
|
|
93
|
+
else:
|
|
94
|
+
lines.append(f"{key}: {value}")
|
|
95
|
+
lines.append("---")
|
|
96
|
+
lines.append("")
|
|
97
|
+
return "\n".join(lines)
|
|
98
|
+
|
|
99
|
+
def render_index(self, entities: "ExtractedEntities") -> Path:
|
|
100
|
+
"""Render the main index page.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
entities: All extracted entities.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Path to the rendered file.
|
|
107
|
+
"""
|
|
108
|
+
lines = []
|
|
109
|
+
|
|
110
|
+
# Frontmatter
|
|
111
|
+
lines.append(self._frontmatter(
|
|
112
|
+
title=entities.ontology.title or "Ontology Documentation",
|
|
113
|
+
layout="default",
|
|
114
|
+
))
|
|
115
|
+
|
|
116
|
+
# Header
|
|
117
|
+
lines.append(f"# {entities.ontology.title or 'Ontology Documentation'}")
|
|
118
|
+
lines.append("")
|
|
119
|
+
|
|
120
|
+
if entities.ontology.description:
|
|
121
|
+
lines.append(entities.ontology.description)
|
|
122
|
+
lines.append("")
|
|
123
|
+
|
|
124
|
+
# Statistics
|
|
125
|
+
lines.append("## Overview")
|
|
126
|
+
lines.append("")
|
|
127
|
+
lines.append(f"- **Classes:** {len(entities.classes)}")
|
|
128
|
+
lines.append(f"- **Object Properties:** {len(entities.object_properties)}")
|
|
129
|
+
lines.append(f"- **Datatype Properties:** {len(entities.datatype_properties)}")
|
|
130
|
+
lines.append(f"- **Annotation Properties:** {len(entities.annotation_properties)}")
|
|
131
|
+
if entities.instances:
|
|
132
|
+
lines.append(f"- **Instances:** {len(entities.instances)}")
|
|
133
|
+
lines.append("")
|
|
134
|
+
|
|
135
|
+
# Navigation
|
|
136
|
+
lines.append("## Quick Links")
|
|
137
|
+
lines.append("")
|
|
138
|
+
lines.append("- [Class Hierarchy](hierarchy.md)")
|
|
139
|
+
lines.append("- [Namespaces](namespaces.md)")
|
|
140
|
+
lines.append("")
|
|
141
|
+
|
|
142
|
+
# Classes section
|
|
143
|
+
if entities.classes:
|
|
144
|
+
lines.append("## Classes")
|
|
145
|
+
lines.append("")
|
|
146
|
+
for c in entities.classes:
|
|
147
|
+
link = self._entity_link(c.qname, "class", c.label or c.qname)
|
|
148
|
+
if c.definition:
|
|
149
|
+
# Truncate long definitions
|
|
150
|
+
desc = c.definition[:100] + "..." if len(c.definition) > 100 else c.definition
|
|
151
|
+
lines.append(f"- {link} — {desc}")
|
|
152
|
+
else:
|
|
153
|
+
lines.append(f"- {link}")
|
|
154
|
+
lines.append("")
|
|
155
|
+
|
|
156
|
+
# Properties section
|
|
157
|
+
if entities.object_properties:
|
|
158
|
+
lines.append("## Object Properties")
|
|
159
|
+
lines.append("")
|
|
160
|
+
for p in entities.object_properties:
|
|
161
|
+
link = self._entity_link(p.qname, "object_property", p.label or p.qname)
|
|
162
|
+
lines.append(f"- {link}")
|
|
163
|
+
lines.append("")
|
|
164
|
+
|
|
165
|
+
if entities.datatype_properties:
|
|
166
|
+
lines.append("## Datatype Properties")
|
|
167
|
+
lines.append("")
|
|
168
|
+
for p in entities.datatype_properties:
|
|
169
|
+
link = self._entity_link(p.qname, "datatype_property", p.label or p.qname)
|
|
170
|
+
lines.append(f"- {link}")
|
|
171
|
+
lines.append("")
|
|
172
|
+
|
|
173
|
+
content = "\n".join(lines)
|
|
174
|
+
return self._write_file(self._get_output_path("index.md"), content)
|
|
175
|
+
|
|
176
|
+
def render_hierarchy(self, entities: "ExtractedEntities") -> Path:
|
|
177
|
+
"""Render the class hierarchy page.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
entities: All extracted entities.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Path to the rendered file.
|
|
184
|
+
"""
|
|
185
|
+
lines = []
|
|
186
|
+
|
|
187
|
+
lines.append(self._frontmatter(title="Class Hierarchy"))
|
|
188
|
+
lines.append("# Class Hierarchy")
|
|
189
|
+
lines.append("")
|
|
190
|
+
|
|
191
|
+
# Build and render tree
|
|
192
|
+
hierarchy = self._build_hierarchy_tree(entities.classes)
|
|
193
|
+
|
|
194
|
+
def render_tree(nodes: list[dict[str, Any]], indent: int = 0) -> None:
|
|
195
|
+
prefix = " " * indent
|
|
196
|
+
for node in nodes:
|
|
197
|
+
c = node["class"]
|
|
198
|
+
link = self._entity_link(c.qname, "class", c.label or c.qname)
|
|
199
|
+
lines.append(f"{prefix}- {link}")
|
|
200
|
+
if node["children"]:
|
|
201
|
+
render_tree(node["children"], indent + 1)
|
|
202
|
+
|
|
203
|
+
render_tree(hierarchy)
|
|
204
|
+
lines.append("")
|
|
205
|
+
|
|
206
|
+
content = "\n".join(lines)
|
|
207
|
+
return self._write_file(self._get_output_path("hierarchy.md"), content)
|
|
208
|
+
|
|
209
|
+
def _build_hierarchy_tree(
|
|
210
|
+
self,
|
|
211
|
+
classes: list["ClassInfo"],
|
|
212
|
+
) -> list[dict[str, Any]]:
|
|
213
|
+
"""Build a tree structure for the class hierarchy.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
classes: List of all classes.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Nested list structure representing the hierarchy.
|
|
220
|
+
"""
|
|
221
|
+
class_by_uri = {str(c.uri): c for c in classes}
|
|
222
|
+
internal_uris = set(class_by_uri.keys())
|
|
223
|
+
root_classes = []
|
|
224
|
+
|
|
225
|
+
for c in classes:
|
|
226
|
+
has_internal_parent = any(
|
|
227
|
+
str(parent) in internal_uris for parent in c.superclasses
|
|
228
|
+
)
|
|
229
|
+
if not has_internal_parent:
|
|
230
|
+
root_classes.append(c)
|
|
231
|
+
|
|
232
|
+
def build_node(class_info: "ClassInfo") -> dict[str, Any]:
|
|
233
|
+
children = []
|
|
234
|
+
for child_uri in class_info.subclasses:
|
|
235
|
+
child_key = str(child_uri)
|
|
236
|
+
if child_key in class_by_uri:
|
|
237
|
+
children.append(build_node(class_by_uri[child_key]))
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
"class": class_info,
|
|
241
|
+
"children": sorted(children, key=lambda n: n["class"].qname),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return sorted(
|
|
245
|
+
[build_node(c) for c in root_classes],
|
|
246
|
+
key=lambda n: n["class"].qname,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def render_class(
|
|
250
|
+
self,
|
|
251
|
+
class_info: "ClassInfo",
|
|
252
|
+
entities: "ExtractedEntities",
|
|
253
|
+
) -> Path:
|
|
254
|
+
"""Render a class documentation page.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
class_info: Class to render.
|
|
258
|
+
entities: All extracted entities.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Path to the rendered file.
|
|
262
|
+
"""
|
|
263
|
+
lines = []
|
|
264
|
+
|
|
265
|
+
lines.append(self._frontmatter(
|
|
266
|
+
title=class_info.label or class_info.qname,
|
|
267
|
+
type="class",
|
|
268
|
+
))
|
|
269
|
+
|
|
270
|
+
lines.append(f"# {class_info.label or class_info.qname}")
|
|
271
|
+
lines.append("")
|
|
272
|
+
lines.append(f"**URI:** `{class_info.uri}`")
|
|
273
|
+
lines.append("")
|
|
274
|
+
|
|
275
|
+
if class_info.definition:
|
|
276
|
+
lines.append(class_info.definition)
|
|
277
|
+
lines.append("")
|
|
278
|
+
|
|
279
|
+
# Superclasses
|
|
280
|
+
if class_info.superclasses:
|
|
281
|
+
lines.append("## Superclasses")
|
|
282
|
+
lines.append("")
|
|
283
|
+
for uri in class_info.superclasses:
|
|
284
|
+
# Try to make a link if we have this class
|
|
285
|
+
qname = self._uri_to_display(uri, entities)
|
|
286
|
+
lines.append(f"- {qname}")
|
|
287
|
+
lines.append("")
|
|
288
|
+
|
|
289
|
+
# Subclasses
|
|
290
|
+
if class_info.subclasses:
|
|
291
|
+
lines.append("## Subclasses")
|
|
292
|
+
lines.append("")
|
|
293
|
+
for uri in class_info.subclasses:
|
|
294
|
+
qname = self._uri_to_display(uri, entities)
|
|
295
|
+
lines.append(f"- {qname}")
|
|
296
|
+
lines.append("")
|
|
297
|
+
|
|
298
|
+
# Domain of (properties where this is domain)
|
|
299
|
+
if class_info.domain_of:
|
|
300
|
+
lines.append("## Properties")
|
|
301
|
+
lines.append("")
|
|
302
|
+
for p in class_info.domain_of:
|
|
303
|
+
link = self._entity_link(p.qname, f"{p.property_type}_property")
|
|
304
|
+
lines.append(f"- {link}")
|
|
305
|
+
lines.append("")
|
|
306
|
+
|
|
307
|
+
# Range of (properties where this is range)
|
|
308
|
+
if class_info.range_of:
|
|
309
|
+
lines.append("## Used as Range")
|
|
310
|
+
lines.append("")
|
|
311
|
+
for p in class_info.range_of:
|
|
312
|
+
link = self._entity_link(p.qname, f"{p.property_type}_property")
|
|
313
|
+
lines.append(f"- {link}")
|
|
314
|
+
lines.append("")
|
|
315
|
+
|
|
316
|
+
# Instances
|
|
317
|
+
if class_info.instances:
|
|
318
|
+
lines.append("## Instances")
|
|
319
|
+
lines.append("")
|
|
320
|
+
for uri in class_info.instances:
|
|
321
|
+
qname = self._uri_to_display(uri, entities, "instance")
|
|
322
|
+
lines.append(f"- {qname}")
|
|
323
|
+
lines.append("")
|
|
324
|
+
|
|
325
|
+
# Annotations
|
|
326
|
+
if class_info.annotations:
|
|
327
|
+
lines.append("## Annotations")
|
|
328
|
+
lines.append("")
|
|
329
|
+
for name, values in class_info.annotations.items():
|
|
330
|
+
for value in values:
|
|
331
|
+
lines.append(f"- **{name}:** {value}")
|
|
332
|
+
lines.append("")
|
|
333
|
+
|
|
334
|
+
content = "\n".join(lines)
|
|
335
|
+
from ..config import entity_to_path
|
|
336
|
+
rel_path = entity_to_path(class_info.qname, "class", self.config, extension=".md")
|
|
337
|
+
return self._write_file(self.config.output_dir / rel_path, content)
|
|
338
|
+
|
|
339
|
+
def _uri_to_display(
|
|
340
|
+
self,
|
|
341
|
+
uri: Any,
|
|
342
|
+
entities: "ExtractedEntities",
|
|
343
|
+
default_type: str = "class",
|
|
344
|
+
) -> str:
|
|
345
|
+
"""Convert a URI to a display string, linking if possible.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
uri: URI to convert.
|
|
349
|
+
entities: All entities for lookups.
|
|
350
|
+
default_type: Entity type if not found.
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
Display string with link if available.
|
|
354
|
+
"""
|
|
355
|
+
uri_str = str(uri)
|
|
356
|
+
|
|
357
|
+
# Check if it's a known class
|
|
358
|
+
for c in entities.classes:
|
|
359
|
+
if str(c.uri) == uri_str:
|
|
360
|
+
return self._entity_link(c.qname, "class", c.label or c.qname)
|
|
361
|
+
|
|
362
|
+
# Check if it's a known instance
|
|
363
|
+
for i in entities.instances:
|
|
364
|
+
if str(i.uri) == uri_str:
|
|
365
|
+
return self._entity_link(i.qname, "instance", i.label or i.qname)
|
|
366
|
+
|
|
367
|
+
# Fall back to extracting local name
|
|
368
|
+
if "#" in uri_str:
|
|
369
|
+
return f"`{uri_str.split('#')[-1]}`"
|
|
370
|
+
elif "/" in uri_str:
|
|
371
|
+
return f"`{uri_str.split('/')[-1]}`"
|
|
372
|
+
return f"`{uri_str}`"
|
|
373
|
+
|
|
374
|
+
def render_property(
|
|
375
|
+
self,
|
|
376
|
+
prop_info: "PropertyInfo",
|
|
377
|
+
entities: "ExtractedEntities",
|
|
378
|
+
) -> Path:
|
|
379
|
+
"""Render a property documentation page.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
prop_info: Property to render.
|
|
383
|
+
entities: All extracted entities.
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
Path to the rendered file.
|
|
387
|
+
"""
|
|
388
|
+
lines = []
|
|
389
|
+
|
|
390
|
+
type_label = prop_info.property_type.replace("_", " ").title()
|
|
391
|
+
lines.append(self._frontmatter(
|
|
392
|
+
title=prop_info.label or prop_info.qname,
|
|
393
|
+
type=prop_info.property_type,
|
|
394
|
+
))
|
|
395
|
+
|
|
396
|
+
lines.append(f"# {prop_info.label or prop_info.qname}")
|
|
397
|
+
lines.append("")
|
|
398
|
+
lines.append(f"**Type:** {type_label} Property")
|
|
399
|
+
lines.append("")
|
|
400
|
+
lines.append(f"**URI:** `{prop_info.uri}`")
|
|
401
|
+
lines.append("")
|
|
402
|
+
|
|
403
|
+
if prop_info.definition:
|
|
404
|
+
lines.append(prop_info.definition)
|
|
405
|
+
lines.append("")
|
|
406
|
+
|
|
407
|
+
# Domain
|
|
408
|
+
if prop_info.domain:
|
|
409
|
+
lines.append("## Domain")
|
|
410
|
+
lines.append("")
|
|
411
|
+
for uri in prop_info.domain:
|
|
412
|
+
display = self._uri_to_display(uri, entities)
|
|
413
|
+
lines.append(f"- {display}")
|
|
414
|
+
lines.append("")
|
|
415
|
+
|
|
416
|
+
# Range
|
|
417
|
+
if prop_info.range:
|
|
418
|
+
lines.append("## Range")
|
|
419
|
+
lines.append("")
|
|
420
|
+
for uri in prop_info.range:
|
|
421
|
+
display = self._uri_to_display(uri, entities)
|
|
422
|
+
lines.append(f"- {display}")
|
|
423
|
+
lines.append("")
|
|
424
|
+
|
|
425
|
+
# Characteristics
|
|
426
|
+
characteristics = []
|
|
427
|
+
if prop_info.is_functional:
|
|
428
|
+
characteristics.append("Functional")
|
|
429
|
+
if prop_info.is_inverse_functional:
|
|
430
|
+
characteristics.append("Inverse Functional")
|
|
431
|
+
if prop_info.inverse_of:
|
|
432
|
+
inv_display = self._uri_to_display(prop_info.inverse_of, entities)
|
|
433
|
+
characteristics.append(f"Inverse of {inv_display}")
|
|
434
|
+
|
|
435
|
+
if characteristics:
|
|
436
|
+
lines.append("## Characteristics")
|
|
437
|
+
lines.append("")
|
|
438
|
+
for char in characteristics:
|
|
439
|
+
lines.append(f"- {char}")
|
|
440
|
+
lines.append("")
|
|
441
|
+
|
|
442
|
+
# Super/subproperties
|
|
443
|
+
if prop_info.superproperties:
|
|
444
|
+
lines.append("## Superproperties")
|
|
445
|
+
lines.append("")
|
|
446
|
+
for uri in prop_info.superproperties:
|
|
447
|
+
lines.append(f"- `{uri}`")
|
|
448
|
+
lines.append("")
|
|
449
|
+
|
|
450
|
+
if prop_info.subproperties:
|
|
451
|
+
lines.append("## Subproperties")
|
|
452
|
+
lines.append("")
|
|
453
|
+
for uri in prop_info.subproperties:
|
|
454
|
+
lines.append(f"- `{uri}`")
|
|
455
|
+
lines.append("")
|
|
456
|
+
|
|
457
|
+
content = "\n".join(lines)
|
|
458
|
+
entity_type = f"{prop_info.property_type}_property"
|
|
459
|
+
from ..config import entity_to_path
|
|
460
|
+
rel_path = entity_to_path(prop_info.qname, entity_type, self.config, extension=".md")
|
|
461
|
+
return self._write_file(self.config.output_dir / rel_path, content)
|
|
462
|
+
|
|
463
|
+
def render_instance(
|
|
464
|
+
self,
|
|
465
|
+
instance_info: "InstanceInfo",
|
|
466
|
+
entities: "ExtractedEntities",
|
|
467
|
+
) -> Path:
|
|
468
|
+
"""Render an instance documentation page.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
instance_info: Instance to render.
|
|
472
|
+
entities: All extracted entities.
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
Path to the rendered file.
|
|
476
|
+
"""
|
|
477
|
+
lines = []
|
|
478
|
+
|
|
479
|
+
lines.append(self._frontmatter(
|
|
480
|
+
title=instance_info.label or instance_info.qname,
|
|
481
|
+
type="instance",
|
|
482
|
+
))
|
|
483
|
+
|
|
484
|
+
lines.append(f"# {instance_info.label or instance_info.qname}")
|
|
485
|
+
lines.append("")
|
|
486
|
+
lines.append(f"**URI:** `{instance_info.uri}`")
|
|
487
|
+
lines.append("")
|
|
488
|
+
|
|
489
|
+
if instance_info.definition:
|
|
490
|
+
lines.append(instance_info.definition)
|
|
491
|
+
lines.append("")
|
|
492
|
+
|
|
493
|
+
# Types
|
|
494
|
+
if instance_info.types:
|
|
495
|
+
lines.append("## Types")
|
|
496
|
+
lines.append("")
|
|
497
|
+
for uri in instance_info.types:
|
|
498
|
+
display = self._uri_to_display(uri, entities)
|
|
499
|
+
lines.append(f"- {display}")
|
|
500
|
+
lines.append("")
|
|
501
|
+
|
|
502
|
+
# Properties
|
|
503
|
+
if instance_info.properties:
|
|
504
|
+
lines.append("## Properties")
|
|
505
|
+
lines.append("")
|
|
506
|
+
for pred, values in instance_info.properties.items():
|
|
507
|
+
pred_name = str(pred).split("#")[-1] if "#" in str(pred) else str(pred).split("/")[-1]
|
|
508
|
+
for value in values:
|
|
509
|
+
if isinstance(value, str):
|
|
510
|
+
lines.append(f"- **{pred_name}:** {value}")
|
|
511
|
+
else:
|
|
512
|
+
display = self._uri_to_display(value, entities)
|
|
513
|
+
lines.append(f"- **{pred_name}:** {display}")
|
|
514
|
+
lines.append("")
|
|
515
|
+
|
|
516
|
+
content = "\n".join(lines)
|
|
517
|
+
from ..config import entity_to_path
|
|
518
|
+
rel_path = entity_to_path(instance_info.qname, "instance", self.config, extension=".md")
|
|
519
|
+
return self._write_file(self.config.output_dir / rel_path, content)
|
|
520
|
+
|
|
521
|
+
def render_namespaces(self, entities: "ExtractedEntities") -> Path:
|
|
522
|
+
"""Render the namespace reference page.
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
entities: All extracted entities.
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
Path to the rendered file.
|
|
529
|
+
"""
|
|
530
|
+
lines = []
|
|
531
|
+
|
|
532
|
+
lines.append(self._frontmatter(title="Namespaces"))
|
|
533
|
+
lines.append("# Namespaces")
|
|
534
|
+
lines.append("")
|
|
535
|
+
|
|
536
|
+
if entities.ontology.namespaces:
|
|
537
|
+
lines.append("| Prefix | Namespace |")
|
|
538
|
+
lines.append("|--------|-----------|")
|
|
539
|
+
for prefix, namespace in sorted(entities.ontology.namespaces.items()):
|
|
540
|
+
lines.append(f"| `{prefix}` | `{namespace}` |")
|
|
541
|
+
lines.append("")
|
|
542
|
+
|
|
543
|
+
content = "\n".join(lines)
|
|
544
|
+
return self._write_file(self._get_output_path("namespaces.md"), content)
|
|
545
|
+
|
|
546
|
+
def render_single_page(self, entities: "ExtractedEntities") -> Path:
|
|
547
|
+
"""Render all documentation as a single page.
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
entities: All extracted entities.
|
|
551
|
+
|
|
552
|
+
Returns:
|
|
553
|
+
Path to the rendered file.
|
|
554
|
+
"""
|
|
555
|
+
lines = []
|
|
556
|
+
|
|
557
|
+
lines.append(self._frontmatter(
|
|
558
|
+
title=entities.ontology.title or "Ontology Documentation",
|
|
559
|
+
))
|
|
560
|
+
|
|
561
|
+
# Header
|
|
562
|
+
lines.append(f"# {entities.ontology.title or 'Ontology Documentation'}")
|
|
563
|
+
lines.append("")
|
|
564
|
+
|
|
565
|
+
if entities.ontology.description:
|
|
566
|
+
lines.append(entities.ontology.description)
|
|
567
|
+
lines.append("")
|
|
568
|
+
|
|
569
|
+
# TOC
|
|
570
|
+
lines.append("## Table of Contents")
|
|
571
|
+
lines.append("")
|
|
572
|
+
lines.append("- [Classes](#classes)")
|
|
573
|
+
lines.append("- [Object Properties](#object-properties)")
|
|
574
|
+
lines.append("- [Datatype Properties](#datatype-properties)")
|
|
575
|
+
lines.append("- [Namespaces](#namespaces)")
|
|
576
|
+
lines.append("")
|
|
577
|
+
|
|
578
|
+
# Classes
|
|
579
|
+
lines.append("## Classes")
|
|
580
|
+
lines.append("")
|
|
581
|
+
for c in entities.classes:
|
|
582
|
+
lines.append(f"### {c.label or c.qname}")
|
|
583
|
+
lines.append("")
|
|
584
|
+
lines.append(f"**URI:** `{c.uri}`")
|
|
585
|
+
lines.append("")
|
|
586
|
+
if c.definition:
|
|
587
|
+
lines.append(c.definition)
|
|
588
|
+
lines.append("")
|
|
589
|
+
|
|
590
|
+
# Properties
|
|
591
|
+
lines.append("## Object Properties")
|
|
592
|
+
lines.append("")
|
|
593
|
+
for p in entities.object_properties:
|
|
594
|
+
lines.append(f"### {p.label or p.qname}")
|
|
595
|
+
lines.append("")
|
|
596
|
+
lines.append(f"**URI:** `{p.uri}`")
|
|
597
|
+
lines.append("")
|
|
598
|
+
if p.definition:
|
|
599
|
+
lines.append(p.definition)
|
|
600
|
+
lines.append("")
|
|
601
|
+
|
|
602
|
+
lines.append("## Datatype Properties")
|
|
603
|
+
lines.append("")
|
|
604
|
+
for p in entities.datatype_properties:
|
|
605
|
+
lines.append(f"### {p.label or p.qname}")
|
|
606
|
+
lines.append("")
|
|
607
|
+
lines.append(f"**URI:** `{p.uri}`")
|
|
608
|
+
lines.append("")
|
|
609
|
+
if p.definition:
|
|
610
|
+
lines.append(p.definition)
|
|
611
|
+
lines.append("")
|
|
612
|
+
|
|
613
|
+
# Namespaces
|
|
614
|
+
lines.append("## Namespaces")
|
|
615
|
+
lines.append("")
|
|
616
|
+
if entities.ontology.namespaces:
|
|
617
|
+
lines.append("| Prefix | Namespace |")
|
|
618
|
+
lines.append("|--------|-----------|")
|
|
619
|
+
for prefix, namespace in sorted(entities.ontology.namespaces.items()):
|
|
620
|
+
lines.append(f"| `{prefix}` | `{namespace}` |")
|
|
621
|
+
lines.append("")
|
|
622
|
+
|
|
623
|
+
content = "\n".join(lines)
|
|
624
|
+
return self._write_file(self._get_output_path("index.md"), content)
|
|
625
|
+
|
|
626
|
+
def copy_assets(self) -> None:
|
|
627
|
+
"""Copy static assets. No assets needed for Markdown."""
|
|
628
|
+
pass
|