rdf-construct 0.2.1__py3-none-any.whl → 0.4.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 (43) hide show
  1. rdf_construct/__init__.py +1 -1
  2. rdf_construct/cli.py +1794 -0
  3. rdf_construct/describe/__init__.py +93 -0
  4. rdf_construct/describe/analyzer.py +176 -0
  5. rdf_construct/describe/documentation.py +146 -0
  6. rdf_construct/describe/formatters/__init__.py +47 -0
  7. rdf_construct/describe/formatters/json.py +65 -0
  8. rdf_construct/describe/formatters/markdown.py +275 -0
  9. rdf_construct/describe/formatters/text.py +315 -0
  10. rdf_construct/describe/hierarchy.py +232 -0
  11. rdf_construct/describe/imports.py +213 -0
  12. rdf_construct/describe/metadata.py +187 -0
  13. rdf_construct/describe/metrics.py +145 -0
  14. rdf_construct/describe/models.py +552 -0
  15. rdf_construct/describe/namespaces.py +180 -0
  16. rdf_construct/describe/profiles.py +415 -0
  17. rdf_construct/localise/__init__.py +114 -0
  18. rdf_construct/localise/config.py +508 -0
  19. rdf_construct/localise/extractor.py +427 -0
  20. rdf_construct/localise/formatters/__init__.py +36 -0
  21. rdf_construct/localise/formatters/markdown.py +229 -0
  22. rdf_construct/localise/formatters/text.py +224 -0
  23. rdf_construct/localise/merger.py +346 -0
  24. rdf_construct/localise/reporter.py +356 -0
  25. rdf_construct/merge/__init__.py +165 -0
  26. rdf_construct/merge/config.py +354 -0
  27. rdf_construct/merge/conflicts.py +281 -0
  28. rdf_construct/merge/formatters.py +426 -0
  29. rdf_construct/merge/merger.py +425 -0
  30. rdf_construct/merge/migrator.py +339 -0
  31. rdf_construct/merge/rules.py +377 -0
  32. rdf_construct/merge/splitter.py +1102 -0
  33. rdf_construct/refactor/__init__.py +72 -0
  34. rdf_construct/refactor/config.py +362 -0
  35. rdf_construct/refactor/deprecator.py +328 -0
  36. rdf_construct/refactor/formatters/__init__.py +8 -0
  37. rdf_construct/refactor/formatters/text.py +311 -0
  38. rdf_construct/refactor/renamer.py +294 -0
  39. {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/METADATA +91 -6
  40. {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/RECORD +43 -7
  41. {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/WHEEL +0 -0
  42. {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/entry_points.txt +0 -0
  43. {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,72 @@
1
+ """Refactor module for URI renaming and deprecation.
2
+
3
+ This module provides tools for common ontology maintenance tasks:
4
+ - Renaming URIs (single entities or bulk namespace changes)
5
+ - Deprecating entities with proper OWL annotations
6
+ - Data migration for instance graphs
7
+
8
+ Example usage:
9
+ from rdf_construct.refactor import OntologyRenamer, RenameConfig
10
+
11
+ renamer = OntologyRenamer()
12
+ config = RenameConfig(entities={
13
+ "http://example.org/Buiding": "http://example.org/Building"
14
+ })
15
+ result = renamer.rename(graph, config)
16
+ """
17
+
18
+ from rdf_construct.refactor.config import (
19
+ RenameConfig,
20
+ RenameMapping,
21
+ DeprecationSpec,
22
+ DeprecationConfig,
23
+ RefactorConfig,
24
+ DataMigrationSpec,
25
+ load_refactor_config,
26
+ create_default_rename_config,
27
+ create_default_deprecation_config,
28
+ )
29
+ from rdf_construct.refactor.renamer import (
30
+ OntologyRenamer,
31
+ RenameResult,
32
+ RenameStats,
33
+ rename_file,
34
+ rename_files,
35
+ )
36
+ from rdf_construct.refactor.deprecator import (
37
+ OntologyDeprecator,
38
+ DeprecationResult,
39
+ DeprecationStats,
40
+ EntityDeprecationInfo,
41
+ deprecate_file,
42
+ generate_deprecation_message,
43
+ )
44
+ from rdf_construct.refactor.formatters import TextFormatter
45
+
46
+ __all__ = [
47
+ # Config
48
+ "RenameConfig",
49
+ "RenameMapping",
50
+ "DeprecationSpec",
51
+ "DeprecationConfig",
52
+ "RefactorConfig",
53
+ "DataMigrationSpec",
54
+ "load_refactor_config",
55
+ "create_default_rename_config",
56
+ "create_default_deprecation_config",
57
+ # Renamer
58
+ "OntologyRenamer",
59
+ "RenameResult",
60
+ "RenameStats",
61
+ "rename_file",
62
+ "rename_files",
63
+ # Deprecator
64
+ "OntologyDeprecator",
65
+ "DeprecationResult",
66
+ "DeprecationStats",
67
+ "EntityDeprecationInfo",
68
+ "deprecate_file",
69
+ "generate_deprecation_message",
70
+ # Formatters
71
+ "TextFormatter",
72
+ ]
@@ -0,0 +1,362 @@
1
+ """Configuration dataclasses for the refactor command.
2
+
3
+ Defines configuration structures for:
4
+ - URI renaming (single and bulk namespace)
5
+ - Deprecation specifications
6
+ - Data migration settings
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum, auto
11
+ from pathlib import Path
12
+ from typing import Any, Literal
13
+
14
+ import yaml
15
+ from rdflib import Graph, URIRef
16
+
17
+
18
+ @dataclass
19
+ class RenameMapping:
20
+ """A single URI rename mapping.
21
+
22
+ Attributes:
23
+ from_uri: Source URI to rename.
24
+ to_uri: Target URI to rename to.
25
+ source: How this mapping was determined.
26
+ """
27
+
28
+ from_uri: URIRef
29
+ to_uri: URIRef
30
+ source: Literal["explicit", "namespace"]
31
+
32
+
33
+ @dataclass
34
+ class RenameConfig:
35
+ """Configuration for URI renaming operations.
36
+
37
+ Supports both explicit entity renames and bulk namespace remapping.
38
+ Namespace rules are applied first, then explicit entity renames,
39
+ allowing fine-grained overrides after namespace changes.
40
+
41
+ Attributes:
42
+ namespaces: Old namespace -> new namespace mappings.
43
+ entities: Explicit old URI -> new URI mappings.
44
+ """
45
+
46
+ namespaces: dict[str, str] = field(default_factory=dict)
47
+ entities: dict[str, str] = field(default_factory=dict)
48
+
49
+ def build_mappings(self, graph: Graph) -> list[RenameMapping]:
50
+ """Expand namespace rules to concrete URI mappings.
51
+
52
+ Scans the graph for all URIs and creates RenameMapping entries
53
+ for those matching namespace patterns. Explicit entity mappings
54
+ override any namespace-derived mappings.
55
+
56
+ Args:
57
+ graph: RDF graph to scan for URIs.
58
+
59
+ Returns:
60
+ List of RenameMapping objects.
61
+ """
62
+ mappings: dict[URIRef, RenameMapping] = {}
63
+
64
+ # Phase 1: Apply namespace mappings
65
+ if self.namespaces:
66
+ # Collect all URIs from the graph
67
+ all_uris: set[URIRef] = set()
68
+ for s, p, o in graph:
69
+ if isinstance(s, URIRef):
70
+ all_uris.add(s)
71
+ if isinstance(p, URIRef):
72
+ all_uris.add(p)
73
+ if isinstance(o, URIRef):
74
+ all_uris.add(o)
75
+
76
+ # Apply namespace mappings
77
+ for uri in all_uris:
78
+ uri_str = str(uri)
79
+ for old_ns, new_ns in self.namespaces.items():
80
+ if uri_str.startswith(old_ns):
81
+ new_uri_str = uri_str.replace(old_ns, new_ns, 1)
82
+ mappings[uri] = RenameMapping(
83
+ from_uri=uri,
84
+ to_uri=URIRef(new_uri_str),
85
+ source="namespace",
86
+ )
87
+ break
88
+
89
+ # Phase 2: Apply explicit entity mappings (override namespace)
90
+ for old_uri_str, new_uri_str in self.entities.items():
91
+ old_uri = URIRef(old_uri_str)
92
+ mappings[old_uri] = RenameMapping(
93
+ from_uri=old_uri,
94
+ to_uri=URIRef(new_uri_str),
95
+ source="explicit",
96
+ )
97
+
98
+ return list(mappings.values())
99
+
100
+ @classmethod
101
+ def from_dict(cls, data: dict[str, Any]) -> "RenameConfig":
102
+ """Create from dictionary.
103
+
104
+ Args:
105
+ data: Dictionary with rename configuration.
106
+
107
+ Returns:
108
+ RenameConfig instance.
109
+ """
110
+ return cls(
111
+ namespaces=data.get("namespaces", {}),
112
+ entities=data.get("entities", {}),
113
+ )
114
+
115
+
116
+ @dataclass
117
+ class DeprecationSpec:
118
+ """Specification for deprecating a single entity.
119
+
120
+ Attributes:
121
+ entity: URI of entity to deprecate.
122
+ replaced_by: Optional URI of replacement entity.
123
+ message: Deprecation message for rdfs:comment.
124
+ version: Optional version when deprecated.
125
+ """
126
+
127
+ entity: str
128
+ replaced_by: str | None = None
129
+ message: str | None = None
130
+ version: str | None = None
131
+
132
+ @classmethod
133
+ def from_dict(cls, data: dict[str, Any]) -> "DeprecationSpec":
134
+ """Create from dictionary.
135
+
136
+ Args:
137
+ data: Dictionary with deprecation specification.
138
+
139
+ Returns:
140
+ DeprecationSpec instance.
141
+ """
142
+ return cls(
143
+ entity=data["entity"],
144
+ replaced_by=data.get("replaced_by"),
145
+ message=data.get("message"),
146
+ version=data.get("version"),
147
+ )
148
+
149
+
150
+ @dataclass
151
+ class DeprecationConfig:
152
+ """Configuration for bulk deprecation operations.
153
+
154
+ Attributes:
155
+ deprecations: List of deprecation specifications.
156
+ """
157
+
158
+ deprecations: list[DeprecationSpec] = field(default_factory=list)
159
+
160
+ @classmethod
161
+ def from_dict(cls, data: dict[str, Any]) -> "DeprecationConfig":
162
+ """Create from dictionary.
163
+
164
+ Args:
165
+ data: Dictionary with deprecation configuration.
166
+
167
+ Returns:
168
+ DeprecationConfig instance.
169
+ """
170
+ specs = [DeprecationSpec.from_dict(d) for d in data.get("deprecations", [])]
171
+ return cls(deprecations=specs)
172
+
173
+
174
+ @dataclass
175
+ class DataMigrationSpec:
176
+ """Specification for data graph migration.
177
+
178
+ Attributes:
179
+ sources: Paths to data files to migrate.
180
+ output_dir: Directory for migrated outputs.
181
+ output: Single output file (for merging all data).
182
+ """
183
+
184
+ sources: list[str] = field(default_factory=list)
185
+ output_dir: str | None = None
186
+ output: str | None = None
187
+
188
+
189
+ @dataclass
190
+ class RefactorConfig:
191
+ """Complete configuration for a refactor operation.
192
+
193
+ Can contain either rename or deprecation (or both) configurations.
194
+
195
+ Attributes:
196
+ rename: Rename configuration (namespaces and entities).
197
+ deprecations: List of deprecation specifications.
198
+ migrate_data: Optional data migration configuration.
199
+ source_files: Source ontology files to process.
200
+ output: Output file path (for single file).
201
+ output_dir: Output directory (for multiple files).
202
+ dry_run: If True, report what would happen without writing.
203
+ """
204
+
205
+ rename: RenameConfig | None = None
206
+ deprecations: list[DeprecationSpec] = field(default_factory=list)
207
+ migrate_data: DataMigrationSpec | None = None
208
+ source_files: list[Path] = field(default_factory=list)
209
+ output: Path | None = None
210
+ output_dir: Path | None = None
211
+ dry_run: bool = False
212
+
213
+ @classmethod
214
+ def from_yaml(cls, path: Path) -> "RefactorConfig":
215
+ """Load configuration from a YAML file.
216
+
217
+ Args:
218
+ path: Path to YAML configuration file.
219
+
220
+ Returns:
221
+ RefactorConfig instance.
222
+
223
+ Raises:
224
+ FileNotFoundError: If config file doesn't exist.
225
+ ValueError: If config is invalid.
226
+ """
227
+ if not path.exists():
228
+ raise FileNotFoundError(f"Config file not found: {path}")
229
+
230
+ with open(path) as f:
231
+ data = yaml.safe_load(f)
232
+
233
+ return cls.from_dict(data)
234
+
235
+ @classmethod
236
+ def from_dict(cls, data: dict[str, Any]) -> "RefactorConfig":
237
+ """Create from dictionary.
238
+
239
+ Args:
240
+ data: Dictionary with configuration.
241
+
242
+ Returns:
243
+ RefactorConfig instance.
244
+ """
245
+ # Parse rename config
246
+ rename = None
247
+ if "rename" in data:
248
+ rename = RenameConfig.from_dict(data["rename"])
249
+
250
+ # Parse deprecations
251
+ deprecations = []
252
+ if "deprecations" in data:
253
+ deprecations = [DeprecationSpec.from_dict(d) for d in data["deprecations"]]
254
+
255
+ # Parse data migration
256
+ migrate_data = None
257
+ if "migrate_data" in data:
258
+ mig = data["migrate_data"]
259
+ migrate_data = DataMigrationSpec(
260
+ sources=mig.get("sources", []),
261
+ output_dir=mig.get("output_dir"),
262
+ output=mig.get("output"),
263
+ )
264
+
265
+ # Parse source files
266
+ sources = [Path(p) for p in data.get("source_files", [])]
267
+
268
+ # Parse output
269
+ output = Path(data["output"]) if data.get("output") else None
270
+ output_dir = Path(data["output_dir"]) if data.get("output_dir") else None
271
+
272
+ return cls(
273
+ rename=rename,
274
+ deprecations=deprecations,
275
+ migrate_data=migrate_data,
276
+ source_files=sources,
277
+ output=output,
278
+ output_dir=output_dir,
279
+ dry_run=data.get("dry_run", False),
280
+ )
281
+
282
+
283
+ def load_refactor_config(path: Path) -> RefactorConfig:
284
+ """Load refactor configuration from a YAML file.
285
+
286
+ Args:
287
+ path: Path to configuration file.
288
+
289
+ Returns:
290
+ RefactorConfig instance.
291
+ """
292
+ return RefactorConfig.from_yaml(path)
293
+
294
+
295
+ def create_default_rename_config() -> str:
296
+ """Generate default rename configuration as YAML string.
297
+
298
+ Returns:
299
+ YAML configuration template.
300
+ """
301
+ return '''# rdf-construct refactor rename configuration
302
+ # See REFACTOR_GUIDE.md for full documentation
303
+
304
+ # Source files to process
305
+ source_files:
306
+ - ontology.ttl
307
+
308
+ # Output file
309
+ output: renamed.ttl
310
+
311
+ # Rename configuration
312
+ rename:
313
+ # Namespace mappings (applied first)
314
+ namespaces:
315
+ "http://old.example.org/v1#": "http://example.org/v2#"
316
+ # "http://temp.local/": "http://example.org/v2#"
317
+
318
+ # Individual entity renames (applied after namespace)
319
+ entities:
320
+ # Fix typos
321
+ # "http://example.org/v2#Buiding": "http://example.org/v2#Building"
322
+ # "http://example.org/v2#hasAddres": "http://example.org/v2#hasAddress"
323
+
324
+ # Optional data migration
325
+ # migrate_data:
326
+ # sources:
327
+ # - data/*.ttl
328
+ # output_dir: data/migrated/
329
+ '''
330
+
331
+
332
+ def create_default_deprecation_config() -> str:
333
+ """Generate default deprecation configuration as YAML string.
334
+
335
+ Returns:
336
+ YAML configuration template.
337
+ """
338
+ return '''# rdf-construct refactor deprecation configuration
339
+ # See REFACTOR_GUIDE.md for full documentation
340
+
341
+ # Source files to process
342
+ source_files:
343
+ - ontology.ttl
344
+
345
+ # Output file
346
+ output: deprecated.ttl
347
+
348
+ # Deprecation specifications
349
+ deprecations:
350
+ - entity: "http://example.org/ont#LegacyPerson"
351
+ replaced_by: "http://example.org/ont#Agent"
352
+ message: "Deprecated in v2.0. Use Agent for both people and organisations."
353
+ version: "2.0.0"
354
+
355
+ - entity: "http://example.org/ont#hasAddress"
356
+ replaced_by: "http://example.org/ont#locatedAt"
357
+ message: "Renamed for consistency with location vocabulary."
358
+
359
+ - entity: "http://example.org/ont#TemporaryClass"
360
+ # No replacement - just deprecated
361
+ message: "Experimental class removed. No replacement available."
362
+ '''