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.
- rdf_construct/__init__.py +1 -1
- rdf_construct/cli.py +1794 -0
- rdf_construct/describe/__init__.py +93 -0
- rdf_construct/describe/analyzer.py +176 -0
- rdf_construct/describe/documentation.py +146 -0
- rdf_construct/describe/formatters/__init__.py +47 -0
- rdf_construct/describe/formatters/json.py +65 -0
- rdf_construct/describe/formatters/markdown.py +275 -0
- rdf_construct/describe/formatters/text.py +315 -0
- rdf_construct/describe/hierarchy.py +232 -0
- rdf_construct/describe/imports.py +213 -0
- rdf_construct/describe/metadata.py +187 -0
- rdf_construct/describe/metrics.py +145 -0
- rdf_construct/describe/models.py +552 -0
- rdf_construct/describe/namespaces.py +180 -0
- rdf_construct/describe/profiles.py +415 -0
- rdf_construct/localise/__init__.py +114 -0
- rdf_construct/localise/config.py +508 -0
- rdf_construct/localise/extractor.py +427 -0
- rdf_construct/localise/formatters/__init__.py +36 -0
- rdf_construct/localise/formatters/markdown.py +229 -0
- rdf_construct/localise/formatters/text.py +224 -0
- rdf_construct/localise/merger.py +346 -0
- rdf_construct/localise/reporter.py +356 -0
- rdf_construct/merge/__init__.py +165 -0
- rdf_construct/merge/config.py +354 -0
- rdf_construct/merge/conflicts.py +281 -0
- rdf_construct/merge/formatters.py +426 -0
- rdf_construct/merge/merger.py +425 -0
- rdf_construct/merge/migrator.py +339 -0
- rdf_construct/merge/rules.py +377 -0
- rdf_construct/merge/splitter.py +1102 -0
- rdf_construct/refactor/__init__.py +72 -0
- rdf_construct/refactor/config.py +362 -0
- rdf_construct/refactor/deprecator.py +328 -0
- rdf_construct/refactor/formatters/__init__.py +8 -0
- rdf_construct/refactor/formatters/text.py +311 -0
- rdf_construct/refactor/renamer.py +294 -0
- {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/METADATA +91 -6
- {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/RECORD +43 -7
- {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/WHEEL +0 -0
- {rdf_construct-0.2.1.dist-info → rdf_construct-0.4.0.dist-info}/entry_points.txt +0 -0
- {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
|
+
'''
|