statcatlib 0.8.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.
@@ -0,0 +1,141 @@
1
+ """SKOS domain types for statcatlib.
2
+
3
+ ConceptScheme and Concept are first-class artefacts, on a par with Dataset
4
+ and Catalog. They model DCAT's use of SKOS for themes, types, subjects, and
5
+ other controlled vocabulary references.
6
+
7
+ Identity model
8
+ --------------
9
+ - ``ConceptScheme`` is globally identified by URI.
10
+ - ``Concept`` is identified by ``notation`` within its scheme (the "code").
11
+ It does not carry a ``uri`` field — the URI is implicit from the scheme and
12
+ reconstructed at serialization or recovered from the storage PK at load time.
13
+ This mirrors how sdmxlib models Code (local ``id``) vs. Codelist (global URN).
14
+
15
+ Ref types
16
+ ---------
17
+ Cross-boundary citations use typed ref objects rather than bare strings:
18
+
19
+ - ``ConceptSchemeRef`` — carried on ``Catalog.theme_taxonomy``
20
+ - ``ConceptRef`` — carried on ``Dataset.themes``, ``.subject``, ``.dataset_type``
21
+
22
+ Non-identity fields on refs (``label``, ``notation``, ``scheme_uri``) are
23
+ ``eq=False, hash=False``: two refs with the same URI compare equal regardless
24
+ of what display metadata they carry. This ensures refs parsed from different
25
+ sources (DCAT-AP XML, JSON-LD, store read-back) match cleanly.
26
+
27
+ XKOS extensibility
28
+ ------------------
29
+ The ``broader``/``narrower``/``related`` relation names in storage use a
30
+ VARCHAR column rather than an enum, so future XKOS types (``xkos:generalizes``,
31
+ ``xkos:isPartOf``, etc.) can be added without schema changes.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import attrs
37
+ from sdmxlib import InternationalString
38
+
39
+ __all__ = [
40
+ "Concept",
41
+ "ConceptRef",
42
+ "ConceptScheme",
43
+ "ConceptSchemeRef",
44
+ "is_concept_in_scheme",
45
+ ]
46
+
47
+
48
+ @attrs.frozen
49
+ class ConceptSchemeRef:
50
+ """Typed reference to a ConceptScheme.
51
+
52
+ Carried on ``Catalog.theme_taxonomy`` (DCAT's ``dcat:themeTaxonomy``).
53
+ URI is identity; label is denormalized display convenience only.
54
+ """
55
+
56
+ uri: str
57
+ label: InternationalString = attrs.field(factory=InternationalString, eq=False, hash=False)
58
+
59
+
60
+ @attrs.frozen
61
+ class ConceptRef:
62
+ """Typed reference to a Concept.
63
+
64
+ Carried on ``Dataset.themes``, ``.subject``, and ``.dataset_type``
65
+ (DCAT's ``dcat:theme``, ``dcterms:subject``, ``dcterms:type``).
66
+
67
+ URI is identity. ``scheme_uri``, ``notation``, and ``label`` are hints
68
+ that speed up display and within-scheme lookups but do not affect equality.
69
+
70
+ ``label`` is populated at read time via a LEFT JOIN with the concept table;
71
+ it is an empty dict when the concept is not materialized in the store
72
+ (permissive refs — datasets may cite concepts from external vocabularies
73
+ that are never locally ingested).
74
+ """
75
+
76
+ uri: str
77
+ scheme_uri: str | None = attrs.field(default=None, eq=False, hash=False)
78
+ notation: str | None = attrs.field(default=None, eq=False, hash=False)
79
+ label: InternationalString = attrs.field(factory=InternationalString, eq=False, hash=False)
80
+
81
+
82
+ @attrs.frozen
83
+ class Concept:
84
+ """A SKOS Concept.
85
+
86
+ Identity is ``notation`` within its parent scheme — the "code" (e.g. "PUMF",
87
+ "HEAL", "ON"). ``broader``, ``narrower``, and ``related`` are tuples of
88
+ notations in the same scheme, not URIs. Cross-scheme relations use
89
+ ``ConceptRef`` or (in future) an XKOS Correspondence artefact.
90
+ """
91
+
92
+ notation: str
93
+ label: InternationalString = attrs.field(factory=InternationalString)
94
+ alt_label: InternationalString = attrs.field(factory=InternationalString)
95
+ definition: InternationalString | None = None
96
+ broader: tuple[str, ...] = () # notations within same scheme
97
+ narrower: tuple[str, ...] = ()
98
+ related: tuple[str, ...] = ()
99
+
100
+
101
+ @attrs.frozen
102
+ class ConceptScheme:
103
+ """A SKOS ConceptScheme — a controlled vocabulary.
104
+
105
+ Globally identified by URI. Owns its Concepts: a Concept has no identity
106
+ outside its scheme (``skos:inScheme`` is a required property).
107
+
108
+ ``concepts`` maps notation → Concept for Pythonic within-scheme access::
109
+
110
+ scheme.concepts["PUMF"] # direct lookup by notation
111
+
112
+ ``top_concepts`` lists notations of the top-level hierarchy entries
113
+ (``skos:hasTopConcept``).
114
+
115
+ Construction with embedded concepts enables cascade persistence::
116
+
117
+ store.put_concept_scheme(ConceptScheme(uri=..., label=..., concepts={...}))
118
+
119
+ ``put_concept_scheme()`` writes the scheme row and cascades to write each
120
+ Concept row. This is the correct container convenience: the ownership
121
+ relation is genuine.
122
+ """
123
+
124
+ uri: str
125
+ label: InternationalString
126
+ definition: InternationalString | None = None
127
+ concepts: dict[str, Concept] = attrs.field(factory=dict) # notation → Concept
128
+ top_concepts: tuple[str, ...] = () # notations
129
+
130
+
131
+ def is_concept_in_scheme(scheme: ConceptScheme, notation: str) -> bool:
132
+ """Return True if *notation* is a known concept in *scheme*.
133
+
134
+ Pure-Python — no DuckDB or any other storage involved.
135
+ Useful for validation pipelines that operate on in-memory domain objects.
136
+
137
+ Example::
138
+
139
+ valid = is_concept_in_scheme(product_type_scheme, dataset.dataset_type.notation)
140
+ """
141
+ return notation in scheme.concepts
@@ -0,0 +1,19 @@
1
+ """Spatial types for the statcatlib domain model."""
2
+
3
+ from attrs import define
4
+ from sdmxlib import InternationalString
5
+
6
+ __all__ = ["SpatialCoverage"]
7
+
8
+
9
+ @define
10
+ class SpatialCoverage:
11
+ """A geographic area covered by the dataset.
12
+
13
+ Maps to dct:spatial → dct:Location in DCAT-AP.
14
+ Can be a named region (with URI) or a bounding box.
15
+ """
16
+
17
+ label: InternationalString | None = None
18
+ uri: str | None = None
19
+ bbox: tuple[float, float, float, float] | None = None
@@ -0,0 +1,33 @@
1
+ """Temporal types for the statcatlib domain model."""
2
+
3
+ from datetime import datetime
4
+
5
+ from attrs import define
6
+ from sdmxlib import InternationalString
7
+
8
+ __all__ = ["Frequency", "TemporalCoverage"]
9
+
10
+
11
+ @define
12
+ class TemporalCoverage:
13
+ """A time interval that the dataset covers.
14
+
15
+ Maps to dct:temporal → dct:PeriodOfTime in DCAT-AP.
16
+ """
17
+
18
+ start: datetime | None = None
19
+ end: datetime | None = None
20
+ label: InternationalString | None = None
21
+
22
+
23
+ @define
24
+ class Frequency:
25
+ """Update frequency of a dataset.
26
+
27
+ Maps to dct:accrualPeriodicity in DCAT-AP. The id should use
28
+ the Dublin Core frequency vocabulary or SDMX CL_FREQ values
29
+ where possible (e.g. "ANNUAL", "QUARTERLY", "MONTHLY").
30
+ """
31
+
32
+ id: str
33
+ label: InternationalString | None = None
@@ -0,0 +1,5 @@
1
+ """Shared supporting types for the statcatlib domain model."""
2
+
3
+ from sdmxlib import InternationalString
4
+
5
+ __all__ = ["InternationalString"]
statcatlib/py.typed ADDED
File without changes