sdcreader 4.3.1__tar.gz → 4.4.0__tar.gz
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.
- {sdcreader-4.3.1/src/sdcreader.egg-info → sdcreader-4.4.0}/PKG-INFO +5 -3
- {sdcreader-4.3.1 → sdcreader-4.4.0}/README.md +4 -2
- {sdcreader-4.3.1 → sdcreader-4.4.0}/pyproject.toml +1 -1
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader/__init__.py +1 -1
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader/mcp_server.py +2 -1
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader/record.py +24 -8
- {sdcreader-4.3.1 → sdcreader-4.4.0/src/sdcreader.egg-info}/PKG-INFO +5 -3
- {sdcreader-4.3.1 → sdcreader-4.4.0}/tests/test_record.py +23 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/LICENSE +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/NOTICE +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/setup.cfg +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader/mcp.py +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader/model.py +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader/package.py +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader.egg-info/SOURCES.txt +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader.egg-info/dependency_links.txt +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader.egg-info/entry_points.txt +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader.egg-info/requires.txt +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/src/sdcreader.egg-info/top_level.txt +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/tests/test_mcp_scaffold.py +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/tests/test_mcp_server.py +0 -0
- {sdcreader-4.3.1 → sdcreader-4.4.0}/tests/test_reader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sdcreader
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.4.0
|
|
4
4
|
Summary: Read a published Semantic Data Charter model's package for projection into other standards (CDIF, DCAT-US, DCAT-AP, HealthDCAT-AP); MCP server included.
|
|
5
5
|
Author: Axius SDC, Inc.
|
|
6
6
|
License: Apache-2.0
|
|
@@ -97,8 +97,10 @@ that defines it. `source` is the record's bytes, its text or a path; a record of
|
|
|
97
97
|
|
|
98
98
|
- **`Record`**: `attributes` (`instance_id`, `instance_version`, `creation_timestamp`, `current-state`, ...),
|
|
99
99
|
`values` in document order, and `exceptional`, the values that carry an Exceptional Value.
|
|
100
|
-
- **`RecordValue`**: the `component`, its `path` of clusters (root first),
|
|
101
|
-
|
|
100
|
+
- **`RecordValue`**: the `component`, its `path` of clusters (root first), `instances` parallel to the path (which
|
|
101
|
+
occurrence each cluster is among the same cluster's repeats under the same parent; `instance_key` pairs them with the
|
|
102
|
+
clusters' identifiers, one key per cluster instance), an `occurrence` index for a component repeated within its
|
|
103
|
+
cluster instance, its `position`, its `elements` as the record holds them (a quantity's units nested:
|
|
102
104
|
`{"xdquantity-value": "1", "xdquantity-units": {"label": "Trade Units", "xdstring-value": "EA"}}`), and its
|
|
103
105
|
`context` (`act`, `vtb`, `vte`, `tr`, `modified`, `latitude`, `longitude`).
|
|
104
106
|
- **`ExceptionalValue`**: the ISO 21090 null flavor (`code`, `name`) and its `origin`: `source` when it stands in
|
|
@@ -72,8 +72,10 @@ that defines it. `source` is the record's bytes, its text or a path; a record of
|
|
|
72
72
|
|
|
73
73
|
- **`Record`**: `attributes` (`instance_id`, `instance_version`, `creation_timestamp`, `current-state`, ...),
|
|
74
74
|
`values` in document order, and `exceptional`, the values that carry an Exceptional Value.
|
|
75
|
-
- **`RecordValue`**: the `component`, its `path` of clusters (root first),
|
|
76
|
-
|
|
75
|
+
- **`RecordValue`**: the `component`, its `path` of clusters (root first), `instances` parallel to the path (which
|
|
76
|
+
occurrence each cluster is among the same cluster's repeats under the same parent; `instance_key` pairs them with the
|
|
77
|
+
clusters' identifiers, one key per cluster instance), an `occurrence` index for a component repeated within its
|
|
78
|
+
cluster instance, its `position`, its `elements` as the record holds them (a quantity's units nested:
|
|
77
79
|
`{"xdquantity-value": "1", "xdquantity-units": {"label": "Trade Units", "xdstring-value": "EA"}}`), and its
|
|
78
80
|
`context` (`act`, `vtb`, `vte`, `tr`, `modified`, `latitude`, `longitude`).
|
|
79
81
|
- **`ExceptionalValue`**: the ISO 21090 null flavor (`code`, `name`) and its `origin`: `source` when it stands in
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sdcreader"
|
|
7
|
-
version = "4.
|
|
7
|
+
version = "4.4.0"
|
|
8
8
|
description = "Read a published Semantic Data Charter model's package for projection into other standards (CDIF, DCAT-US, DCAT-AP, HealthDCAT-AP); MCP server included."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -16,7 +16,7 @@ from .model import (CLOSE, DC_DEFAULTS, EXACT, HAS_UNIT, IDENTIFIER, LEAF_TYPES,
|
|
|
16
16
|
from .package import DEFAULT_HOST, ModelPackage, PackageError, fetch_package, load_package
|
|
17
17
|
from .record import CONTEXT_ELEMENTS, EXCEPTIONAL_VALUES, RECORD_ATTRIBUTES, VALUE_ELEMENTS, ExceptionalValue, Record, RecordError, RecordValue, read_record
|
|
18
18
|
|
|
19
|
-
__version__ = "4.
|
|
19
|
+
__version__ = "4.4.0"
|
|
20
20
|
__all__ = ["ModelPackage", "PackageError", "DEFAULT_HOST", "load_package", "fetch_package", "read_model", "read_header", "enumeration_iri",
|
|
21
21
|
"Model", "Component", "Leaf", "Code", "LEAF_TYPES", "NUMERIC_TYPES", "QUANTIFIED_TYPES", "DC_DEFAULTS", "IDENTIFIER", "SOURCE",
|
|
22
22
|
"PUBLISHER", "EXACT", "CLOSE", "SEE_ALSO", "HAS_UNIT", "read_record", "Record", "RecordValue", "RecordError", "ExceptionalValue",
|
|
@@ -311,7 +311,8 @@ def _handle_read_record(args: dict[str, Any]) -> Any:
|
|
|
311
311
|
values = [
|
|
312
312
|
{
|
|
313
313
|
"position": v.position, "ct_id": v.component.ct_id, "label": v.component.label, "sdc_type": v.component.sdc_type,
|
|
314
|
-
"path": [p.label for p in v.path], "
|
|
314
|
+
"path": [p.label for p in v.path], "instances": list(v.instances), "occurrence": v.occurrence,
|
|
315
|
+
"elements": v.elements, "context": v.context,
|
|
315
316
|
"exceptional": ({"code": v.exceptional.code, "name": v.exceptional.name, "origin": v.exceptional.origin}
|
|
316
317
|
if v.exceptional else None),
|
|
317
318
|
}
|
|
@@ -71,12 +71,21 @@ class ExceptionalValue:
|
|
|
71
71
|
class RecordValue:
|
|
72
72
|
component: Component
|
|
73
73
|
path: list[Component] # the clusters above it, root first
|
|
74
|
-
occurrence: int # 0 for the first time this component appears
|
|
74
|
+
occurrence: int # 0 for the first time this component appears in its cluster instance, 1 for the next
|
|
75
75
|
position: int # document order among the record's values, 0-based
|
|
76
76
|
elements: dict # the value elements as the record holds them: name -> text, or name -> dict for a
|
|
77
77
|
# nested element (a quantity's units: {"label": "Trade Units", "xdstring-value": "EA"})
|
|
78
78
|
context: dict[str, str] # act, vtb, vte, tr, modified, latitude, longitude, where present
|
|
79
79
|
exceptional: ExceptionalValue | None = None
|
|
80
|
+
instances: tuple[int, ...] = () # parallel to `path`: which occurrence each cluster is among the same cluster's
|
|
81
|
+
# repeats under the same parent instance (4.4.0). Two lines of one Line cluster
|
|
82
|
+
# in one Lines cluster are (..., 0) and (..., 1).
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def instance_key(self) -> tuple[tuple[str, int], ...]:
|
|
86
|
+
"""The value's cluster instance, root first: (ct_id, occurrence) for every cluster above it. Values with equal
|
|
87
|
+
keys belong to the same cluster instance; a mapper writes one target element per key."""
|
|
88
|
+
return tuple(zip((c.ct_id for c in self.path), self.instances))
|
|
80
89
|
|
|
81
90
|
@property
|
|
82
91
|
def rejected(self) -> dict:
|
|
@@ -147,9 +156,11 @@ def read_record(source: bytes | str | Path, model: Model) -> Record:
|
|
|
147
156
|
attributes[n] = _text(el)
|
|
148
157
|
|
|
149
158
|
record = Record(model=model, attributes=attributes)
|
|
150
|
-
seen: dict[tuple, int] = {}
|
|
159
|
+
seen: dict[tuple, int] = {} # leaf occurrences, per cluster instance
|
|
160
|
+
clusters: dict[tuple, int] = {} # cluster occurrences, per parent cluster instance
|
|
151
161
|
|
|
152
|
-
def walk(el, path: list[Component]):
|
|
162
|
+
def walk(el, path: list[Component], instances: tuple[int, ...]):
|
|
163
|
+
parent = tuple(zip((c.ct_id for c in path), instances))
|
|
153
164
|
for child in el:
|
|
154
165
|
if not _is_sdc4(child):
|
|
155
166
|
continue
|
|
@@ -158,16 +169,21 @@ def read_record(source: bytes | str | Path, model: Model) -> Record:
|
|
|
158
169
|
continue
|
|
159
170
|
comp = model.components.get(n[3:])
|
|
160
171
|
if comp is None: # an adapter: pass through to the component inside
|
|
161
|
-
walk(child, path)
|
|
172
|
+
walk(child, path, instances)
|
|
162
173
|
elif comp.sdc_type == "Cluster" or comp.sdc_type not in LEAF_TYPES:
|
|
163
|
-
|
|
174
|
+
key = (parent, comp.ct_id) # a cluster, or a structural component (audit, party, ...) holding values
|
|
175
|
+
k = clusters.get(key, 0)
|
|
176
|
+
clusters[key] = k + 1
|
|
177
|
+
walk(child, path + [comp], instances + (k,))
|
|
164
178
|
else:
|
|
165
|
-
key = (comp.ct_id,
|
|
179
|
+
key = (comp.ct_id, parent)
|
|
166
180
|
occurrence = seen.get(key, 0)
|
|
167
181
|
seen[key] = occurrence + 1
|
|
168
|
-
|
|
182
|
+
v = _value(child, comp, path, occurrence, len(record.values))
|
|
183
|
+
v.instances = instances
|
|
184
|
+
record.values.append(v)
|
|
169
185
|
|
|
170
|
-
walk(root, [])
|
|
186
|
+
walk(root, [], ())
|
|
171
187
|
return record
|
|
172
188
|
|
|
173
189
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sdcreader
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.4.0
|
|
4
4
|
Summary: Read a published Semantic Data Charter model's package for projection into other standards (CDIF, DCAT-US, DCAT-AP, HealthDCAT-AP); MCP server included.
|
|
5
5
|
Author: Axius SDC, Inc.
|
|
6
6
|
License: Apache-2.0
|
|
@@ -97,8 +97,10 @@ that defines it. `source` is the record's bytes, its text or a path; a record of
|
|
|
97
97
|
|
|
98
98
|
- **`Record`**: `attributes` (`instance_id`, `instance_version`, `creation_timestamp`, `current-state`, ...),
|
|
99
99
|
`values` in document order, and `exceptional`, the values that carry an Exceptional Value.
|
|
100
|
-
- **`RecordValue`**: the `component`, its `path` of clusters (root first),
|
|
101
|
-
|
|
100
|
+
- **`RecordValue`**: the `component`, its `path` of clusters (root first), `instances` parallel to the path (which
|
|
101
|
+
occurrence each cluster is among the same cluster's repeats under the same parent; `instance_key` pairs them with the
|
|
102
|
+
clusters' identifiers, one key per cluster instance), an `occurrence` index for a component repeated within its
|
|
103
|
+
cluster instance, its `position`, its `elements` as the record holds them (a quantity's units nested:
|
|
102
104
|
`{"xdquantity-value": "1", "xdquantity-units": {"label": "Trade Units", "xdstring-value": "EA"}}`), and its
|
|
103
105
|
`context` (`act`, `vtb`, `vte`, `tr`, `modified`, `latitude`, `longitude`).
|
|
104
106
|
- **`ExceptionalValue`**: the ISO 21090 null flavor (`code`, `name`) and its `origin`: `source` when it stands in
|
|
@@ -145,3 +145,26 @@ def test_recovery_is_recognized_for_every_value_element(model, sdc_type, value_e
|
|
|
145
145
|
comp = Component(ct_id="x", sdc_type=sdc_type, label="L", description="", data_type="", constraints={}, links={})
|
|
146
146
|
v = _value(el, comp, [], 0, 0)
|
|
147
147
|
assert v.exceptional.origin == "recovery" and v.rejected == {value_el: "bad"}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_cluster_instances_number_repeats_under_the_same_parent(model):
|
|
151
|
+
"""4.4.0: a cluster repeated under the same parent instance is numbered, and every value in it carries the number,
|
|
152
|
+
so a mapper can write one target element per instance."""
|
|
153
|
+
tree = etree.parse(str(RECORD))
|
|
154
|
+
_, comp = _first_string(tree)
|
|
155
|
+
cluster = comp.getparent().getparent() # the cluster holding the first value (through its adapter)
|
|
156
|
+
twin = etree.fromstring(etree.tostring(cluster))
|
|
157
|
+
cluster.addnext(twin)
|
|
158
|
+
r = read_record(etree.tostring(tree), model)
|
|
159
|
+
ct = comp.tag.split("ms-", 1)[1]
|
|
160
|
+
first, second = [v for v in r.values if v.component.ct_id == ct][:2]
|
|
161
|
+
assert first.path == second.path
|
|
162
|
+
assert first.instances[-1] == 0 and second.instances[-1] == 1
|
|
163
|
+
assert first.instance_key != second.instance_key
|
|
164
|
+
assert first.occurrence == second.occurrence == 0, "occurrence counts within a cluster instance"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_every_value_has_an_instance_number_per_cluster_in_its_path(model):
|
|
168
|
+
r = read_record(RECORD, model)
|
|
169
|
+
assert all(len(v.instances) == len(v.path) for v in r.values)
|
|
170
|
+
assert all(v.instance_key[0] == (model.root.ct_id, 0) for v in r.values)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|