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.
Files changed (88) hide show
  1. rdf_construct/__init__.py +12 -0
  2. rdf_construct/__main__.py +0 -0
  3. rdf_construct/cli.py +1762 -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/main.py +6 -0
  51. rdf_construct/puml2rdf/__init__.py +103 -0
  52. rdf_construct/puml2rdf/config.py +230 -0
  53. rdf_construct/puml2rdf/converter.py +420 -0
  54. rdf_construct/puml2rdf/merger.py +200 -0
  55. rdf_construct/puml2rdf/model.py +202 -0
  56. rdf_construct/puml2rdf/parser.py +565 -0
  57. rdf_construct/puml2rdf/validators.py +451 -0
  58. rdf_construct/shacl/__init__.py +56 -0
  59. rdf_construct/shacl/config.py +166 -0
  60. rdf_construct/shacl/converters.py +520 -0
  61. rdf_construct/shacl/generator.py +364 -0
  62. rdf_construct/shacl/namespaces.py +93 -0
  63. rdf_construct/stats/__init__.py +29 -0
  64. rdf_construct/stats/collector.py +178 -0
  65. rdf_construct/stats/comparator.py +298 -0
  66. rdf_construct/stats/formatters/__init__.py +83 -0
  67. rdf_construct/stats/formatters/json.py +38 -0
  68. rdf_construct/stats/formatters/markdown.py +153 -0
  69. rdf_construct/stats/formatters/text.py +186 -0
  70. rdf_construct/stats/metrics/__init__.py +26 -0
  71. rdf_construct/stats/metrics/basic.py +147 -0
  72. rdf_construct/stats/metrics/complexity.py +137 -0
  73. rdf_construct/stats/metrics/connectivity.py +130 -0
  74. rdf_construct/stats/metrics/documentation.py +128 -0
  75. rdf_construct/stats/metrics/hierarchy.py +207 -0
  76. rdf_construct/stats/metrics/properties.py +88 -0
  77. rdf_construct/uml/__init__.py +22 -0
  78. rdf_construct/uml/context.py +194 -0
  79. rdf_construct/uml/mapper.py +371 -0
  80. rdf_construct/uml/odm_renderer.py +789 -0
  81. rdf_construct/uml/renderer.py +684 -0
  82. rdf_construct/uml/uml_layout.py +393 -0
  83. rdf_construct/uml/uml_style.py +613 -0
  84. rdf_construct-0.2.0.dist-info/METADATA +431 -0
  85. rdf_construct-0.2.0.dist-info/RECORD +88 -0
  86. rdf_construct-0.2.0.dist-info/WHEEL +4 -0
  87. rdf_construct-0.2.0.dist-info/entry_points.txt +3 -0
  88. rdf_construct-0.2.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,360 @@
1
+ """Main orchestrator for documentation generation.
2
+
3
+ This module coordinates entity extraction, template rendering, and output
4
+ file creation for generating ontology documentation.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+ from typing import TYPE_CHECKING
11
+
12
+ from rdflib import Graph
13
+
14
+ from rdf_construct.docs.config import DocsConfig, load_docs_config
15
+ from rdf_construct.docs.extractors import ExtractedEntities, extract_all
16
+ from rdf_construct.docs.search import generate_search_index, write_search_index
17
+
18
+ if TYPE_CHECKING:
19
+ pass
20
+
21
+
22
+ class DocsGenerator:
23
+ """Main documentation generator class.
24
+
25
+ Coordinates extraction, rendering, and file output for generating
26
+ comprehensive ontology documentation.
27
+ """
28
+
29
+ def __init__(self, config: DocsConfig | None = None) -> None:
30
+ """Initialise the documentation generator.
31
+
32
+ Args:
33
+ config: Documentation configuration. Uses defaults if not provided.
34
+ """
35
+ self.config = config or DocsConfig()
36
+ self._renderer: "BaseRenderer | None" = None
37
+
38
+ @property
39
+ def renderer(self) -> "BaseRenderer":
40
+ """Get the appropriate renderer for the configured format.
41
+
42
+ Returns:
43
+ Renderer instance for the configured output format.
44
+ """
45
+ if self._renderer is None:
46
+ self._renderer = self._create_renderer()
47
+ return self._renderer
48
+
49
+ def _create_renderer(self) -> "BaseRenderer":
50
+ """Create the appropriate renderer based on configuration.
51
+
52
+ Returns:
53
+ Renderer instance.
54
+
55
+ Raises:
56
+ ValueError: If the output format is not supported.
57
+ """
58
+ from .renderers import HTMLRenderer, JSONRenderer, MarkdownRenderer
59
+
60
+ format_lower = self.config.format.lower()
61
+
62
+ if format_lower == "html":
63
+ return HTMLRenderer(self.config)
64
+ elif format_lower in ("markdown", "md"):
65
+ return MarkdownRenderer(self.config)
66
+ elif format_lower == "json":
67
+ return JSONRenderer(self.config)
68
+ else:
69
+ raise ValueError(f"Unsupported output format: {self.config.format}")
70
+
71
+ def generate(self, graph: Graph) -> GenerationResult:
72
+ """Generate documentation from an RDF graph.
73
+
74
+ Args:
75
+ graph: RDF graph to generate documentation from.
76
+
77
+ Returns:
78
+ GenerationResult with details of generated files.
79
+ """
80
+ # Extract all entities
81
+ entities = extract_all(graph)
82
+
83
+ # Apply configuration overrides
84
+ if self.config.title:
85
+ entities.ontology.title = self.config.title
86
+ if self.config.description:
87
+ entities.ontology.description = self.config.description
88
+
89
+ # Create output directory
90
+ self.config.output_dir.mkdir(parents=True, exist_ok=True)
91
+
92
+ result = GenerationResult(output_dir=self.config.output_dir)
93
+
94
+ # Generate main pages
95
+ if self.config.single_page:
96
+ output_path = self.renderer.render_single_page(entities)
97
+ result.files_created.append(output_path)
98
+ else:
99
+ # Index page
100
+ index_path = self.renderer.render_index(entities)
101
+ result.files_created.append(index_path)
102
+
103
+ # Hierarchy page
104
+ if self.config.include_hierarchy:
105
+ hierarchy_path = self.renderer.render_hierarchy(entities)
106
+ result.files_created.append(hierarchy_path)
107
+
108
+ # Individual entity pages
109
+ if self.config.include_classes:
110
+ for class_info in entities.classes:
111
+ class_path = self.renderer.render_class(class_info, entities)
112
+ result.files_created.append(class_path)
113
+ result.classes_count += 1
114
+
115
+ if self.config.include_object_properties:
116
+ for prop_info in entities.object_properties:
117
+ prop_path = self.renderer.render_property(prop_info, entities)
118
+ result.files_created.append(prop_path)
119
+ result.properties_count += 1
120
+
121
+ if self.config.include_datatype_properties:
122
+ for prop_info in entities.datatype_properties:
123
+ prop_path = self.renderer.render_property(prop_info, entities)
124
+ result.files_created.append(prop_path)
125
+ result.properties_count += 1
126
+
127
+ if self.config.include_annotation_properties:
128
+ for prop_info in entities.annotation_properties:
129
+ prop_path = self.renderer.render_property(prop_info, entities)
130
+ result.files_created.append(prop_path)
131
+ result.properties_count += 1
132
+
133
+ if self.config.include_instances:
134
+ for instance_info in entities.instances:
135
+ instance_path = self.renderer.render_instance(instance_info, entities)
136
+ result.files_created.append(instance_path)
137
+ result.instances_count += 1
138
+
139
+ # Namespace page
140
+ namespace_path = self.renderer.render_namespaces(entities)
141
+ result.files_created.append(namespace_path)
142
+
143
+ # Generate search index
144
+ if self.config.include_search and self.config.format == "html":
145
+ search_entries = generate_search_index(entities, self.config)
146
+ search_path = write_search_index(search_entries, self.config.output_dir)
147
+ result.files_created.append(search_path)
148
+
149
+ # Copy assets
150
+ self.renderer.copy_assets()
151
+
152
+ return result
153
+
154
+ def generate_from_file(self, source: Path) -> GenerationResult:
155
+ """Generate documentation from an RDF file.
156
+
157
+ Args:
158
+ source: Path to RDF source file.
159
+
160
+ Returns:
161
+ GenerationResult with details of generated files.
162
+
163
+ Raises:
164
+ FileNotFoundError: If the source file doesn't exist.
165
+ """
166
+ if not source.exists():
167
+ raise FileNotFoundError(f"Source file not found: {source}")
168
+
169
+ graph = Graph()
170
+
171
+ # Determine format from extension
172
+ suffix = source.suffix.lower()
173
+ format_map = {
174
+ ".ttl": "turtle",
175
+ ".turtle": "turtle",
176
+ ".rdf": "xml",
177
+ ".xml": "xml",
178
+ ".owl": "xml",
179
+ ".nt": "nt",
180
+ ".ntriples": "nt",
181
+ ".n3": "n3",
182
+ ".jsonld": "json-ld",
183
+ ".json": "json-ld",
184
+ }
185
+ rdf_format = format_map.get(suffix, "turtle")
186
+
187
+ graph.parse(str(source), format=rdf_format)
188
+ return self.generate(graph)
189
+
190
+
191
+ class GenerationResult:
192
+ """Result of a documentation generation run."""
193
+
194
+ def __init__(self, output_dir: Path) -> None:
195
+ """Initialise the result.
196
+
197
+ Args:
198
+ output_dir: Output directory for generated files.
199
+ """
200
+ self.output_dir = output_dir
201
+ self.files_created: list[Path] = []
202
+ self.classes_count = 0
203
+ self.properties_count = 0
204
+ self.instances_count = 0
205
+
206
+ @property
207
+ def total_pages(self) -> int:
208
+ """Get the total number of pages generated."""
209
+ return len(self.files_created)
210
+
211
+ def __str__(self) -> str:
212
+ """Get a summary string."""
213
+ return (
214
+ f"Generated {self.total_pages} files to {self.output_dir}/\n"
215
+ f" Classes: {self.classes_count}\n"
216
+ f" Properties: {self.properties_count}\n"
217
+ f" Instances: {self.instances_count}"
218
+ )
219
+
220
+
221
+ # Public interface from this module
222
+ class BaseRenderer:
223
+ """Base class for documentation renderers.
224
+
225
+ Subclasses implement format-specific rendering logic.
226
+ """
227
+
228
+ def __init__(self, config: DocsConfig) -> None:
229
+ """Initialise the renderer.
230
+
231
+ Args:
232
+ config: Documentation configuration.
233
+ """
234
+ self.config = config
235
+
236
+ def render_index(self, entities: ExtractedEntities) -> Path:
237
+ """Render the main index page.
238
+
239
+ Args:
240
+ entities: All extracted entities.
241
+
242
+ Returns:
243
+ Path to the rendered file.
244
+ """
245
+ raise NotImplementedError
246
+
247
+ def render_hierarchy(self, entities: ExtractedEntities) -> Path:
248
+ """Render the class hierarchy page.
249
+
250
+ Args:
251
+ entities: All extracted entities.
252
+
253
+ Returns:
254
+ Path to the rendered file.
255
+ """
256
+ raise NotImplementedError
257
+
258
+ def render_class(
259
+ self,
260
+ class_info: "ClassInfo",
261
+ entities: ExtractedEntities,
262
+ ) -> Path:
263
+ """Render a class documentation page.
264
+
265
+ Args:
266
+ class_info: Class to render.
267
+ entities: All extracted entities (for cross-references).
268
+
269
+ Returns:
270
+ Path to the rendered file.
271
+ """
272
+ raise NotImplementedError
273
+
274
+ def render_property(
275
+ self,
276
+ prop_info: "PropertyInfo",
277
+ entities: ExtractedEntities,
278
+ ) -> Path:
279
+ """Render a property documentation page.
280
+
281
+ Args:
282
+ prop_info: Property to render.
283
+ entities: All extracted entities.
284
+
285
+ Returns:
286
+ Path to the rendered file.
287
+ """
288
+ raise NotImplementedError
289
+
290
+ def render_instance(
291
+ self,
292
+ instance_info: "InstanceInfo",
293
+ entities: ExtractedEntities,
294
+ ) -> Path:
295
+ """Render an instance documentation page.
296
+
297
+ Args:
298
+ instance_info: Instance to render.
299
+ entities: All extracted entities.
300
+
301
+ Returns:
302
+ Path to the rendered file.
303
+ """
304
+ raise NotImplementedError
305
+
306
+ def render_namespaces(self, entities: ExtractedEntities) -> Path:
307
+ """Render the namespace reference page.
308
+
309
+ Args:
310
+ entities: All extracted entities.
311
+
312
+ Returns:
313
+ Path to the rendered file.
314
+ """
315
+ raise NotImplementedError
316
+
317
+ def render_single_page(self, entities: ExtractedEntities) -> Path:
318
+ """Render all documentation as a single page.
319
+
320
+ Args:
321
+ entities: All extracted entities.
322
+
323
+ Returns:
324
+ Path to the rendered file.
325
+ """
326
+ raise NotImplementedError
327
+
328
+ def copy_assets(self) -> None:
329
+ """Copy static assets (CSS, JS) to the output directory."""
330
+ pass # Default: no assets to copy
331
+
332
+
333
+ def generate_docs(
334
+ source: Path,
335
+ output_dir: Path | None = None,
336
+ config_path: Path | None = None,
337
+ output_format: str = "html",
338
+ ) -> GenerationResult:
339
+ """Generate documentation from an RDF file.
340
+
341
+ Convenience function for simple documentation generation.
342
+
343
+ Args:
344
+ source: Path to RDF source file.
345
+ output_dir: Output directory (overrides config).
346
+ config_path: Path to configuration file.
347
+ output_format: Output format (html, markdown, json).
348
+
349
+ Returns:
350
+ GenerationResult with details of generated files.
351
+ """
352
+ config = load_docs_config(config_path)
353
+
354
+ if output_dir:
355
+ config.output_dir = output_dir
356
+ if output_format:
357
+ config.format = output_format
358
+
359
+ generator = DocsGenerator(config)
360
+ return generator.generate_from_file(source)
@@ -0,0 +1,7 @@
1
+ """Documentation renderers for different output formats."""
2
+
3
+ from .html import HTMLRenderer
4
+ from .markdown import MarkdownRenderer
5
+ from .json import JSONRenderer
6
+
7
+ __all__ = ["HTMLRenderer", "MarkdownRenderer", "JSONRenderer"]