dapla-toolbelt-metadata 0.9.7__py3-none-any.whl → 0.9.9__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.

Potentially problematic release.


This version of dapla-toolbelt-metadata might be problematic. Click here for more details.

@@ -13,9 +13,6 @@ from dotenv import load_dotenv
13
13
  from dapla_metadata._shared.enums import DaplaEnvironment
14
14
  from dapla_metadata._shared.enums import DaplaRegion
15
15
  from dapla_metadata._shared.enums import DaplaService
16
- from dapla_metadata.datasets.utility.constants import (
17
- DATADOC_STATISTICAL_SUBJECT_SOURCE_URL,
18
- )
19
16
 
20
17
  logger = logging.getLogger(__name__)
21
18
 
@@ -28,6 +25,12 @@ DAPLA_SERVICE = "DAPLA_SERVICE"
28
25
  DAPLA_GROUP_CONTEXT = "DAPLA_GROUP_CONTEXT"
29
26
  OIDC_TOKEN = "OIDC_TOKEN" # noqa: S105
30
27
 
28
+
29
+ DATADOC_STATISTICAL_SUBJECT_SOURCE_URL_DEFAULT = (
30
+ "https://www.ssb.no/xp/_/service/mimir/subjectStructurStatistics"
31
+ )
32
+
33
+
31
34
  env_loaded = False
32
35
 
33
36
 
@@ -73,7 +76,7 @@ def get_statistical_subject_source_url() -> str | None:
73
76
  """Get the URL to the statistical subject source."""
74
77
  return (
75
78
  get_config_item("DATADOC_STATISTICAL_SUBJECT_SOURCE_URL")
76
- or DATADOC_STATISTICAL_SUBJECT_SOURCE_URL
79
+ or DATADOC_STATISTICAL_SUBJECT_SOURCE_URL_DEFAULT
77
80
  )
78
81
 
79
82
 
@@ -78,7 +78,7 @@ class DatasetConsistencyStatus:
78
78
 
79
79
  def check_dataset_consistency(
80
80
  new_dataset_path: Path | CloudPath,
81
- existing_dataset_path: Path,
81
+ existing_dataset_path: Path | CloudPath,
82
82
  ) -> list[DatasetConsistencyStatus]:
83
83
  """Run consistency tests.
84
84
 
@@ -6,7 +6,6 @@ import copy
6
6
  import json
7
7
  import logging
8
8
  from concurrent.futures import ThreadPoolExecutor
9
- from pathlib import Path
10
9
  from typing import TYPE_CHECKING
11
10
  from typing import cast
12
11
 
@@ -175,7 +174,7 @@ class Datadoc:
175
174
  ):
176
175
  self.dataset_consistency_status = check_dataset_consistency(
177
176
  self.dataset_path,
178
- Path(self.metadata_document),
177
+ self.metadata_document,
179
178
  )
180
179
  self.dataset_consistency_status.extend(
181
180
  check_variables_consistency(
@@ -94,10 +94,6 @@ DATASET_FIELDS_FROM_EXISTING_METADATA = [
94
94
 
95
95
  METADATA_DOCUMENT_FILE_SUFFIX = "__DOC.json"
96
96
 
97
- DATADOC_STATISTICAL_SUBJECT_SOURCE_URL = (
98
- "https://www.ssb.no/xp/_/service/mimir/subjectStructurStatistics"
99
- )
100
-
101
97
  PAPIS_STABLE_IDENTIFIER_TYPE = "FREG_SNR"
102
98
  PAPIS_ENCRYPTION_KEY_REFERENCE = "papis-common-key-1"
103
99
  DAEAD_ENCRYPTION_KEY_REFERENCE = "ssb-common-key-1"
@@ -6,9 +6,12 @@ from collections.abc import Iterable
6
6
  from dataclasses import dataclass
7
7
  from enum import Enum
8
8
  from enum import auto
9
+ from typing import Literal
9
10
 
10
11
  from pydantic import AnyUrl
11
12
 
13
+ from dapla_metadata._shared.config import get_dapla_environment
14
+ from dapla_metadata._shared.enums import DaplaEnvironment
12
15
  from dapla_metadata.datasets.utility.utils import VariableListType
13
16
 
14
17
  logger = logging.getLogger(__name__)
@@ -44,6 +47,9 @@ class ReferenceUrlTypes(Enum):
44
47
  FRONTEND = auto()
45
48
 
46
49
 
50
+ UrlVisibility = Literal["public", "internal"]
51
+
52
+
47
53
  @dataclass
48
54
  class UrnConverter:
49
55
  """Converts URLs to URNs and vice versa.
@@ -70,11 +76,81 @@ class UrnConverter:
70
76
  def _build_pattern(self, url_base: str) -> re.Pattern[str]:
71
77
  return re.compile(f"^{url_base}/{self.id_pattern}")
72
78
 
73
- def build_urn(self, identifier: str) -> str:
79
+ def get_urn(self, identifier: str) -> str:
74
80
  """Build a URN for the given identifier."""
75
81
  return f"{self.urn_base}:{identifier}"
76
82
 
77
- def convert_to_urn(self, url: str | AnyUrl) -> AnyUrl | None:
83
+ def get_url(
84
+ self,
85
+ identifier: str,
86
+ url_type: ReferenceUrlTypes,
87
+ visibility: Literal["public", "internal"] = "public",
88
+ ) -> str | None:
89
+ """Build concrete URL to reference a resource.
90
+
91
+ There are typically multiple URLs used to refer to one resource, this method attempts to support known variations.
92
+
93
+ Args:
94
+ identifier (str): The identifier of the resource the URL refers to.
95
+ url_type (ReferenceUrlTypes): The representation type of the URL
96
+ visibility (UrlVisibility, optional): Whether the URL should be that which is publicly available or not. Defaults to "public".
97
+
98
+ Returns:
99
+ str | None: The concrete URL. None if we cannot satisfy the supplied requirements.
100
+ """
101
+ candidates = [base[-1] for base in self.url_bases if base[0] == url_type]
102
+
103
+ def matches_visibility(url: str, visibility: UrlVisibility):
104
+ return (".intern." in url) is (visibility == "internal")
105
+
106
+ def matches_environment(url: str):
107
+ current_environment = get_dapla_environment()
108
+ if current_environment == DaplaEnvironment.TEST:
109
+ return ".test." in url
110
+ return ".test." not in url
111
+
112
+ if url := next(
113
+ (
114
+ url
115
+ for url in candidates
116
+ if matches_visibility(url, visibility) and matches_environment(url)
117
+ ),
118
+ None,
119
+ ):
120
+ return url + "/" + identifier
121
+ return None
122
+
123
+ def get_id(self, urn_or_url: str | AnyUrl) -> str | None:
124
+ """Get an identifier from a URN or URL.
125
+
126
+ Args:
127
+ urn_or_url (str | AnyUrl): The URN or URL refering to a particular resource
128
+
129
+ Returns:
130
+ str | None: The identifier for the resource, or None if it cannot be extracted.
131
+ """
132
+ if str(urn_or_url).startswith(self.urn_base):
133
+ return str(urn_or_url).removeprefix(self.urn_base + ":")
134
+ return self._extract_id_from_url(urn_or_url)
135
+
136
+ def is_id(self, value: str) -> bool:
137
+ """Check if the value is an identifier for this URN type.
138
+
139
+ Args:
140
+ value (str): The value to check.
141
+ """
142
+ if not isinstance(value, str):
143
+ # Mypy thinks it's impossible to reach this branch, but there are no guarantees in Python.
144
+ return False # type: ignore [unreachable]
145
+ pattern = re.compile(f"^{self.id_pattern}$")
146
+ return bool(pattern.match(value))
147
+
148
+ def _extract_id_from_url(self, url: str | AnyUrl) -> str | None:
149
+ patterns = (self._build_pattern(url[-1]) for url in self.url_bases)
150
+ matches = (self._extract_id(str(url), p) for p in patterns)
151
+ return next((m for m in matches if m), None)
152
+
153
+ def convert_url_to_urn(self, url: str | AnyUrl) -> AnyUrl | None:
78
154
  """Convert a URL to a generalized URN for that same resource.
79
155
 
80
156
  Args:
@@ -86,11 +162,8 @@ class UrnConverter:
86
162
  if str(url).startswith(self.urn_base):
87
163
  # In this case the value is already in the expected format and nothing needs to be done.
88
164
  return AnyUrl(url)
89
- patterns = (self._build_pattern(url[-1]) for url in self.url_bases)
90
- matches = (self._extract_id(str(url), p) for p in patterns)
91
- identifier = next((m for m in matches if m), None)
92
- if identifier:
93
- return AnyUrl(self.build_urn(identifier))
165
+ if identifier := self._extract_id_from_url(url):
166
+ return AnyUrl(self.get_urn(identifier))
94
167
 
95
168
  return None
96
169
 
@@ -149,7 +222,7 @@ def convert_uris_to_urns(
149
222
  for v in variables:
150
223
  field = getattr(v, field_name, None)
151
224
  if field:
152
- if urn := next((c.convert_to_urn(field) for c in converters), None):
225
+ if urn := next((c.convert_url_to_urn(field) for c in converters), None):
153
226
  setattr(v, field_name, urn)
154
227
  else:
155
228
  logger.error(
@@ -33,7 +33,11 @@ class VardokId(VardokIdResponse):
33
33
  model: VardokIdResponse,
34
34
  ) -> "VardokId":
35
35
  """Create a VariableDefinition instance from a CompleteResponse."""
36
- return VardokId.model_construct(**model.model_dump())
36
+ self = VardokId.from_dict(model.model_dump())
37
+ if not self:
38
+ msg = f"Could not construct a VardokId instance from {model}"
39
+ raise ValueError(msg)
40
+ return self
37
41
 
38
42
  def __str__(self) -> str:
39
43
  """Format as indented YAML."""
@@ -32,7 +32,11 @@ class VardokVardefIdPair(VardokVardefIdPairResponse):
32
32
  model: VardokVardefIdPairResponse,
33
33
  ) -> "VardokVardefIdPair":
34
34
  """Create a VardokVardefIdPair instance from a VardokVardefIdPairResponse."""
35
- return VardokVardefIdPair.model_construct(**model.model_dump())
35
+ self = VardokVardefIdPair.from_dict(model.model_dump())
36
+ if not self:
37
+ msg = f"Could not construct a VardokVardefIdPair instance from {model}"
38
+ raise ValueError(msg)
39
+ return self
36
40
 
37
41
  def __str__(self) -> str:
38
42
  """Format as indented YAML."""
@@ -82,7 +82,11 @@ class VariableDefinition(CompleteResponse):
82
82
  model: CompleteResponse,
83
83
  ) -> "VariableDefinition":
84
84
  """Create a VariableDefinition instance from a CompleteResponse."""
85
- return VariableDefinition.model_construct(**model.model_dump())
85
+ self = VariableDefinition.from_dict(model.model_dump())
86
+ if not self:
87
+ msg = f"Could not construct a VariableDefinition instance from {model}"
88
+ raise ValueError(msg)
89
+ return self
86
90
 
87
91
  @vardef_exception_handler
88
92
  def list_validity_periods(self) -> list["VariableDefinition"]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dapla-toolbelt-metadata
3
- Version: 0.9.7
3
+ Version: 0.9.9
4
4
  Summary: Dapla Toolbelt Metadata
5
5
  Project-URL: homepage, https://github.com/statisticsnorway/dapla-toolbelt-metadata
6
6
  Project-URL: repository, https://github.com/statisticsnorway/dapla-toolbelt-metadata
@@ -1,14 +1,14 @@
1
1
  dapla_metadata/__init__.py,sha256=37yh9XWYQoLIVIS_fDdwNN8OXzbYY-6kMYwvjQrLMJQ,428
2
2
  dapla_metadata/_shared/__init__.py,sha256=qUFgnVhBVlPRQP0ePmY76c8FvWRrJ-9c5GvzibwERnQ,103
3
- dapla_metadata/_shared/config.py,sha256=QqXcmP66AfXF8wi6FMsa7et7kH2k4EJPOF4IELKuQig,3213
3
+ dapla_metadata/_shared/config.py,sha256=xRAXDULS85lAi5XyAirh1acDbf7_0xPhElssXvQQk3Y,3245
4
4
  dapla_metadata/_shared/enums.py,sha256=WHkH1d8xw41gOly6au_izZB1_-6XTcKu5rhBWUImjp8,509
5
5
  dapla_metadata/_shared/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  dapla_metadata/dapla/__init__.py,sha256=tkapF-YwmruPPrKvN3pEoCZqb7xvJx_ogBM8XyGMuJI,130
7
7
  dapla_metadata/dapla/user_info.py,sha256=bENez-ICt9ySR8orYebO68Q3_2LkIW9QTL58DTctmEQ,4833
8
8
  dapla_metadata/datasets/__init__.py,sha256=an-REJgi7N8-S1SCz-MYO_8as6fMe03WvhjRP_hWWkg,293
9
- dapla_metadata/datasets/_merge.py,sha256=Tk5wQz6xZGr8veUAHZb42O8HARU8ObBJ_E4afvVWdlo,12993
9
+ dapla_metadata/datasets/_merge.py,sha256=hoNBqiPi1an2mF9ZFnl4K-E7eg9xhlzG3986iPwHAj0,13005
10
10
  dapla_metadata/datasets/code_list.py,sha256=JtCE-5Q8grAKvkn0KKjzeGhO-96O7yGsastbuoakreg,9057
11
- dapla_metadata/datasets/core.py,sha256=OMMCnKADTZGTqenu8_F4lwjQ1sVg4JSgqv5CNBv8eGk,20902
11
+ dapla_metadata/datasets/core.py,sha256=zofQ4MwCPCdStPXXjO9GUwpBVEmqGbzabmef-jmGvyw,20871
12
12
  dapla_metadata/datasets/dapla_dataset_path_info.py,sha256=WPeV_mwKk2B9sXd14SaP-kTb1bOQ_8W2KtrqOG7sJIY,26867
13
13
  dapla_metadata/datasets/dataset_parser.py,sha256=3dtRXNy1C8SfG8zTYWdY26nV4l-dG25IC_0J5t2bYwI,8285
14
14
  dapla_metadata/datasets/model_validation.py,sha256=6qqq1ueTWRWBPTwEGJD49Pv7ksMEaq0iDtuOXelaw-s,7223
@@ -21,9 +21,9 @@ dapla_metadata/datasets/compatibility/model_backwards_compatibility.py,sha256=W5
21
21
  dapla_metadata/datasets/external_sources/__init__.py,sha256=qvIdXwqyEmXNUCB94ZtZXRzifdW4hiXASFFPtC70f6E,83
22
22
  dapla_metadata/datasets/external_sources/external_sources.py,sha256=9eIcOIUbaodNX1w9Tj2wl4U4wUmr5kF1R0i01fKUzGs,2974
23
23
  dapla_metadata/datasets/utility/__init__.py,sha256=pp6tUcgUbo8iq9OPtFKQrTbLuI3uY7NHptwWSTpasOU,33
24
- dapla_metadata/datasets/utility/constants.py,sha256=YKsn6GfNIkwLoBp0yq209o0TbsEhsA_jGaZLVR984JU,2933
24
+ dapla_metadata/datasets/utility/constants.py,sha256=4ixDvz5nErQwXa3BEtaGZb2AFUUUldJtNZV46SKwUBc,2817
25
25
  dapla_metadata/datasets/utility/enums.py,sha256=i6dcxWya5k4LjLdGGIM_H37rRndizug3peaAgoE5UdM,652
26
- dapla_metadata/datasets/utility/urn.py,sha256=Y_4wYwWWaFDffIN3uXjCodi-uUUQ7zkX1qEFSwGlVqs,5317
26
+ dapla_metadata/datasets/utility/urn.py,sha256=1NtM9OkTUe4_T5Iy8cR-ofwuR0gUlrtA7oQqEmgWKOc,8141
27
27
  dapla_metadata/datasets/utility/utils.py,sha256=q76UJI8W4j2aHSq1jz_AfYnJmLfygEflgUrQpqQEPnY,20157
28
28
  dapla_metadata/standards/__init__.py,sha256=n8jnMrudLuScSdfQ4UMJorc-Ptg3Y1-ilT8zAaQnM70,179
29
29
  dapla_metadata/standards/name_validator.py,sha256=6-DQE_EKVd6UjL--EXpFcZDQtusVbSFaWaUY-CfOV2c,9184
@@ -33,9 +33,9 @@ dapla_metadata/standards/utils/constants.py,sha256=mhWNFnS6NMsRl0c_deIdzY7_bD_wK
33
33
  dapla_metadata/variable_definitions/__init__.py,sha256=z48vevGb8UuQ8mwkqCtBGoyM-Ts53vUcKo7Ag5rE_Wc,482
34
34
  dapla_metadata/variable_definitions/exceptions.py,sha256=ImB81bne-h45kX9lE5hIh80QAWkOPS52uzcOftuoouM,10118
35
35
  dapla_metadata/variable_definitions/vardef.py,sha256=WUpiKfvgFGPhMdjYSFSmdlXQKAolmRgW4-t-EocddQs,13934
36
- dapla_metadata/variable_definitions/vardok_id.py,sha256=8T23BUHyVQr5hovTVc2E4HVY7f7e_jdi3YL1qzMQgFw,1268
37
- dapla_metadata/variable_definitions/vardok_vardef_id_pair.py,sha256=8MDdd2-9L30MXkoQrk7NDcueaoxdeYie-TJhgoskTzk,1389
38
- dapla_metadata/variable_definitions/variable_definition.py,sha256=tsTo_BObHgppSNf6lGbt_Vh88PypPF4pwA-vWDgFy9A,14869
36
+ dapla_metadata/variable_definitions/vardok_id.py,sha256=hHX3muzz-cJD7fPJbt5QPwIWlUbuoxWxXXz-sn_s92k,1409
37
+ dapla_metadata/variable_definitions/vardok_vardef_id_pair.py,sha256=E4UEddg6VjcL3wdf2DbIu0v6071m7M6ll2MifegPwNA,1540
38
+ dapla_metadata/variable_definitions/variable_definition.py,sha256=CFy5i53zFp6rCoWlALe5HLCx-QaOg5fj3YeUnfWYQ6I,15020
39
39
  dapla_metadata/variable_definitions/_generated/.openapi-generator-ignore,sha256=x9lryVB5wtVEuKQ5GcZ94b10RgtkVXbtvWXOArO1XsM,169
40
40
  dapla_metadata/variable_definitions/_generated/README.md,sha256=Y4et1oAhZTCr7a-CZfLbIpyYnhKzpygNg-gj7qJ09Eg,7650
41
41
  dapla_metadata/variable_definitions/_generated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -91,7 +91,7 @@ dapla_metadata/variable_definitions/_utils/constants.py,sha256=zr5FNVCEz6TM9PVEr
91
91
  dapla_metadata/variable_definitions/_utils/files.py,sha256=JbPgPNQ7iA38juMqGEdcg5OjZZUwCb6NQtPL0AEspD0,10933
92
92
  dapla_metadata/variable_definitions/_utils/template_files.py,sha256=7fcc7yEHOl5JUZ698kqj4IiikXPHBi3SrAVOk4wqQtw,3308
93
93
  dapla_metadata/variable_definitions/_utils/variable_definition_files.py,sha256=sGhcSpckR9NtYGNh2oVkiCd5SI3bbJEBhc1PA2uShs0,4701
94
- dapla_toolbelt_metadata-0.9.7.dist-info/METADATA,sha256=yb6SMXPh6nMfUPfOMyicbrcConcCoWgEtcgsUsl2jJ0,4854
95
- dapla_toolbelt_metadata-0.9.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
96
- dapla_toolbelt_metadata-0.9.7.dist-info/licenses/LICENSE,sha256=np3IfD5m0ZUofn_kVzDZqliozuiO6wrktw3LRPjyEiI,1073
97
- dapla_toolbelt_metadata-0.9.7.dist-info/RECORD,,
94
+ dapla_toolbelt_metadata-0.9.9.dist-info/METADATA,sha256=nCdEjXMl-6rGQNmpxCxEFrNBGk4KfT28xqyGSEK9BTc,4854
95
+ dapla_toolbelt_metadata-0.9.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
96
+ dapla_toolbelt_metadata-0.9.9.dist-info/licenses/LICENSE,sha256=np3IfD5m0ZUofn_kVzDZqliozuiO6wrktw3LRPjyEiI,1073
97
+ dapla_toolbelt_metadata-0.9.9.dist-info/RECORD,,