dapla-toolbelt-metadata 0.4.2__py3-none-any.whl → 0.5.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.

Potentially problematic release.


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

Files changed (81) hide show
  1. dapla_metadata/__init__.py +7 -0
  2. dapla_metadata/dapla/__init__.py +1 -0
  3. dapla_metadata/{_shared → dapla}/user_info.py +55 -8
  4. dapla_metadata/datasets/code_list.py +1 -1
  5. dapla_metadata/datasets/core.py +1 -1
  6. dapla_metadata/datasets/dataset_parser.py +1 -1
  7. dapla_metadata/datasets/model_backwards_compatibility.py +6 -6
  8. dapla_metadata/datasets/model_validation.py +2 -2
  9. dapla_metadata/datasets/utility/constants.py +1 -0
  10. dapla_metadata/datasets/utility/enums.py +1 -1
  11. dapla_metadata/datasets/utility/utils.py +7 -11
  12. dapla_metadata/variable_definitions/__init__.py +5 -3
  13. dapla_metadata/variable_definitions/{generated → _generated}/.openapi-generator/FILES +0 -5
  14. dapla_metadata/variable_definitions/_generated/.openapi-generator/VERSION +1 -0
  15. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/__init__.py +0 -5
  16. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/__init__.py +0 -1
  17. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/data_migration_api.py +2 -2
  18. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/draft_variable_definitions_api.py +14 -14
  19. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/patches_api.py +15 -15
  20. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/validity_periods_api.py +8 -281
  21. dapla_metadata/variable_definitions/{generated/vardef_client/api/public_api.py → _generated/vardef_client/api/variable_definitions_api.py} +73 -358
  22. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/__init__.py +2 -6
  23. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/complete_response.py +8 -32
  24. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/contact.py +2 -2
  25. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/draft.py +8 -23
  26. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/language_string_type.py +7 -6
  27. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/owner.py +2 -2
  28. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/patch.py +16 -61
  29. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/problem.py +2 -2
  30. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/update_draft.py +22 -55
  31. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/validity_period.py +14 -48
  32. dapla_metadata/variable_definitions/_generated/vardef_client/models/variable_status.py +33 -0
  33. dapla_metadata/variable_definitions/_utils/__init__.py +1 -0
  34. dapla_metadata/variable_definitions/{_client.py → _utils/_client.py} +5 -3
  35. dapla_metadata/variable_definitions/{config.py → _utils/config.py} +25 -1
  36. dapla_metadata/variable_definitions/_utils/constants.py +41 -0
  37. dapla_metadata/variable_definitions/_utils/descriptions.py +86 -0
  38. dapla_metadata/variable_definitions/_utils/files.py +273 -0
  39. dapla_metadata/variable_definitions/_utils/template_files.py +112 -0
  40. dapla_metadata/variable_definitions/_utils/variable_definition_files.py +93 -0
  41. dapla_metadata/variable_definitions/exceptions.py +141 -11
  42. dapla_metadata/variable_definitions/resources/vardef_model_descriptions_nb.yaml +63 -0
  43. dapla_metadata/variable_definitions/vardef.py +131 -10
  44. dapla_metadata/variable_definitions/variable_definition.py +241 -43
  45. {dapla_toolbelt_metadata-0.4.2.dist-info → dapla_toolbelt_metadata-0.5.0.dist-info}/METADATA +5 -7
  46. dapla_toolbelt_metadata-0.5.0.dist-info/RECORD +84 -0
  47. {dapla_toolbelt_metadata-0.4.2.dist-info → dapla_toolbelt_metadata-0.5.0.dist-info}/WHEEL +1 -1
  48. dapla_metadata/variable_definitions/generated/.openapi-generator/VERSION +0 -1
  49. dapla_metadata/variable_definitions/generated/vardef_client/api/variable_definitions_api.py +0 -1205
  50. dapla_metadata/variable_definitions/generated/vardef_client/models/klass_reference.py +0 -99
  51. dapla_metadata/variable_definitions/generated/vardef_client/models/rendered_contact.py +0 -92
  52. dapla_metadata/variable_definitions/generated/vardef_client/models/rendered_variable_definition.py +0 -235
  53. dapla_metadata/variable_definitions/generated/vardef_client/models/supported_languages.py +0 -33
  54. dapla_metadata/variable_definitions/generated/vardef_client/models/variable_status.py +0 -33
  55. dapla_toolbelt_metadata-0.4.2.dist-info/RECORD +0 -81
  56. /dapla_metadata/variable_definitions/{generated → _generated}/.openapi-generator-ignore +0 -0
  57. /dapla_metadata/variable_definitions/{generated → _generated}/README.md +0 -0
  58. /dapla_metadata/variable_definitions/{generated → _generated}/__init__.py +0 -0
  59. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api_client.py +0 -0
  60. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api_response.py +0 -0
  61. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/configuration.py +0 -0
  62. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/CompleteResponse.md +0 -0
  63. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/Contact.md +0 -0
  64. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/DataMigrationApi.md +0 -0
  65. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/Draft.md +0 -0
  66. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/DraftVariableDefinitionsApi.md +0 -0
  67. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/LanguageStringType.md +0 -0
  68. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/Owner.md +0 -0
  69. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/Patch.md +0 -0
  70. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/PatchesApi.md +0 -0
  71. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/PublicApi.md +0 -0
  72. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/SupportedLanguages.md +0 -0
  73. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/UpdateDraft.md +0 -0
  74. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/ValidityPeriod.md +0 -0
  75. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/ValidityPeriodsApi.md +0 -0
  76. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/VariableDefinitionsApi.md +0 -0
  77. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/VariableStatus.md +0 -0
  78. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/exceptions.py +0 -0
  79. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/py.typed +0 -0
  80. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/rest.py +0 -0
  81. {dapla_toolbelt_metadata-0.4.2.dist-info → dapla_toolbelt_metadata-0.5.0.dist-info}/LICENSE +0 -0
@@ -0,0 +1,63 @@
1
+ # --- Variabel definisjoner ---
2
+ # ref: https://statistics-norway.atlassian.net/wiki/spaces/MPD/pages/3009839199/VarDef+-+Krav+til+dokumentasjon+av+variabler
3
+ name: |
4
+ Variabelens navn. Dette skal ikke være en mer “teknisk” forkortelse, men et navn som er forståelig for mennesker, f.eks. “Lønnsinntekter”.
5
+ short_name: |
6
+ Dette er variabelens kortnavn, som kan være en mer “teknisk” forkortelse, f.eks. wlonn (kortnavnet til Lønnsinntekter). Kortnavnet til en variabel i Vardef skal være unikt.
7
+ Kravet til kortnavnet er at det kan inneholde a-z (kun små bokstaver), 0-9 og _ (understrek). Minimumslengden på kortnavnet er 2 tegn. Bokstavene “æ”, “ø” og “å” kan ikke brukes. Disse anbefales erstattet med hhv. “ae”, “oe” og “aa"
8
+ definition: |
9
+ En definisjon skal beskrive hva variabelen betyr og være så kort og presis som mulig. Mer utfyllende opplysninger kan legges i Merknad-feltet.
10
+ classification_reference: |
11
+ Lenke (URI) til kodeverk (klassifikasjon eller kodeliste) i KLASS som beskriver verdiene variabelen kan anta. F.eks. vil variabelen “Sivilstand” ha kodeverks-URI Standard for [sivilstand](https://www.ssb.no/klass/klassifikasjoner/19).
12
+ unit_types: |
13
+ Enhetstype(r) - enheten(e) som beskrives av denne variabelen. Variabelen “sivilstand” vil f.eks. ha enhetstypen person, mens f.eks. “Produsentpris for tjenester” vil ha både foretak og bedrift som enhetstyper siden variabelen kan beskrive begge.
14
+ ref: https://www.ssb.no/klass/klassifikasjoner/702.
15
+ subject_fields: |
16
+ Statistikkområde(r) som variabelen brukes innenfor, hentet fra [Kodeliste for statistikkområder i Statistikkbanken](https://www.ssb.no/klass/klassifikasjoner/618).
17
+ F.eks. tilhører variabelen “Sivilstand” statistikkområdet “Befolkning”.
18
+ contains_special_categories_of_personal_data: |
19
+ Viser om variabelen inneholder spesielt sensitive personopplysninger [Lov om behandling av personopplysninger(personopplysningsloven)-KAPITTEL || Prinsipper - Lovdata](https://lovdata.no/dokument/NL/lov/2018-06-15-38/KAPITTEL_gdpr-2#gdpr/a9)
20
+ - opplysninger om etnisk opprinnelse
21
+ - opplysninger om politisk oppfatning
22
+ - opplysninger om religion
23
+ - opplysninger om filosofisk overbevisning
24
+ - opplysninger om fagforeningsmedlemskap
25
+ - genetiske opplysninger
26
+ - biometriske opplysninger med det formål å entydig identifisere noen
27
+ - helseopplysninger
28
+ - opplysninger om seksuelle forhold
29
+ - opplysninger om seksuell legning
30
+ measurement_type: |
31
+ Måletype som en kvantitativ variabelen tilhører, f.eks. valuta, areal osv. Disse ligger i kodeverket [SSB måletyper/måleenheter](https://www.ssb.no/klass/klassifikasjoner/303/koder)
32
+ valid_from: |
33
+ Datoen variabeldefinisjonen er gyldig f.o.m.
34
+ valid_until: |
35
+ Datoen variabeldefinisjonens var gyldig t.o.m. Settes hvis definisjonen skal erstattet av en ny definisjon (med en ny gyldighetsperiode), eller variabelen ikke lenger skal brukes.
36
+ external_reference_uri: |
37
+ En peker (URI) til ekstern definisjon/dokumentasjon, f.eks. ei webside som er relevant for variabelen.
38
+ comment: |
39
+ Her kan en sette inn eventuelle tilleggsopplysninger som ikke hører hjemme i selve definisjonen. Variabelen “Landbakgrunn” har f.eks. merknaden “Fra og med 1.1.2003 ble definisjon endret til også å trekke inn besteforeldrenes fødeland”.
40
+ related_variable_definition_uris: |
41
+ Her kan en legge inn URI(er) til andre variabler som er relevante. Eksempelvis er variabelen “Inntekt etter skatt” en beregnet variabel der “Yrkesinntekter” og “Kapitalinntekter” inngår i beregningen. En kan da legge inn deres URI-er i dette feltet.
42
+ contact: |
43
+ Her dokumenterer en navn og epost for person eller gruppe som kan svare på spørsmål.
44
+ variable_status: |
45
+ Livssyklus for variabelen. Denne har tre kategorier: Utkast, Publisert internt og Publisert eksternt.
46
+ id: |
47
+ Unik SSB identifikator for variabeldefinisjonen. Denne blir maskingenerert.
48
+ Variabeldefinisjoner med ulike gyldighetsperioder har samme ID (og samme kortnavn).
49
+ patch_id: |
50
+ Løpenummer som identifiserer en patch, endring, for en variabeldefinisjon.
51
+ owner: |
52
+ Eier av variabelen dvs. ansvarlig Dapla-team (statistikk-team) og informasjon om tilgangsstyringsgrupper. Team-tilhørighet settes automatisk til det samme som teamtilhørigheten til den som oppretter variabelen.
53
+ Eksempel:
54
+ team: ledstil
55
+ groups: [developers]
56
+ created_at: |
57
+ Datoen variabelen ble opprettet. Denne er maskingenerert.
58
+ created_by: |
59
+ Personen som har opprettet variabelen (initialer). Dette er maskingenerert.
60
+ last_updated_at: |
61
+ Dato da variabelen sist ble oppdatert. Denne er maskingenerert.
62
+ last_updated_by: |
63
+ Personen (initialer) som sist utførte en endring i variabelen. Denne er maskingenerert.
@@ -1,22 +1,38 @@
1
+ import logging
1
2
  from datetime import date
3
+ from os import PathLike
4
+ from pathlib import Path
2
5
 
3
- from dapla_metadata.variable_definitions import config
4
- from dapla_metadata.variable_definitions._client import VardefClient
5
- from dapla_metadata.variable_definitions.exceptions import vardef_exception_handler
6
- from dapla_metadata.variable_definitions.generated.vardef_client.api.data_migration_api import (
6
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.data_migration_api import (
7
7
  DataMigrationApi,
8
8
  )
9
- from dapla_metadata.variable_definitions.generated.vardef_client.api.draft_variable_definitions_api import (
9
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.draft_variable_definitions_api import (
10
10
  DraftVariableDefinitionsApi,
11
11
  )
12
- from dapla_metadata.variable_definitions.generated.vardef_client.api.variable_definitions_api import (
12
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.variable_definitions_api import (
13
13
  VariableDefinitionsApi,
14
14
  )
15
- from dapla_metadata.variable_definitions.generated.vardef_client.models.draft import (
15
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.draft import (
16
16
  Draft,
17
17
  )
18
+ from dapla_metadata.variable_definitions._utils import config
19
+ from dapla_metadata.variable_definitions._utils._client import VardefClient
20
+ from dapla_metadata.variable_definitions._utils.template_files import (
21
+ _find_latest_template_file,
22
+ )
23
+ from dapla_metadata.variable_definitions._utils.template_files import (
24
+ create_template_yaml,
25
+ )
26
+ from dapla_metadata.variable_definitions._utils.variable_definition_files import (
27
+ _read_file_to_model,
28
+ )
29
+ from dapla_metadata.variable_definitions.exceptions import VariableNotFoundError
30
+ from dapla_metadata.variable_definitions.exceptions import vardef_exception_handler
31
+ from dapla_metadata.variable_definitions.exceptions import vardef_file_error_handler
18
32
  from dapla_metadata.variable_definitions.variable_definition import VariableDefinition
19
33
 
34
+ logger = logging.getLogger(__name__)
35
+
20
36
 
21
37
  class Vardef:
22
38
  """Create, maintain and read Variable Definitions.
@@ -82,7 +98,7 @@ class Vardef:
82
98
  @vardef_exception_handler
83
99
  def create_draft(cls, draft: Draft) -> VariableDefinition:
84
100
  """Create a Draft Variable Definition."""
85
- return VariableDefinition.from_model(
101
+ new_variable = VariableDefinition.from_model(
86
102
  DraftVariableDefinitionsApi(
87
103
  VardefClient.get_client(),
88
104
  ).create_variable_definition(
@@ -91,6 +107,39 @@ class Vardef:
91
107
  ),
92
108
  )
93
109
 
110
+ logger.info(
111
+ "Successfully created variable definition '%s' with ID '%s'",
112
+ new_variable.short_name,
113
+ new_variable.id,
114
+ )
115
+ return new_variable
116
+
117
+ @classmethod
118
+ @vardef_file_error_handler
119
+ def create_draft_from_file(
120
+ cls,
121
+ file_path: PathLike[str] | None = None,
122
+ ) -> VariableDefinition:
123
+ """Create a Draft Variable Definition from a stored yaml file.
124
+
125
+ By default the latest template file in the default directory is chosen, this may be overridden by providing a value for the optional `file_path` parameter.
126
+
127
+ Args:
128
+ file_path (PathLike[str], optional): Supply a file path to override the automatic one. Defaults to None.
129
+
130
+ Raises:
131
+ FileNotFoundError: When a file can't be found.
132
+
133
+ Returns:
134
+ VariableDefinition: The created draft variable definition.
135
+ """
136
+ return cls.create_draft(
137
+ _read_file_to_model(
138
+ file_path or _find_latest_template_file(),
139
+ Draft,
140
+ ),
141
+ )
142
+
94
143
  @classmethod
95
144
  @vardef_exception_handler
96
145
  def migrate_from_vardok(cls, vardok_id: str) -> VariableDefinition:
@@ -106,7 +155,7 @@ class Vardef:
106
155
  Returns:
107
156
  VariableDefinition: The migrated Variable Definition in Vardef.
108
157
  """
109
- return VariableDefinition.from_model(
158
+ migrated_variable = VariableDefinition.from_model(
110
159
  DataMigrationApi(
111
160
  VardefClient.get_client(),
112
161
  ).create_variable_definition_from_var_dok(
@@ -115,6 +164,13 @@ class Vardef:
115
164
  ),
116
165
  )
117
166
 
167
+ logger.info(
168
+ "Successfully migrated variable definition '%s' with ID '%s'",
169
+ migrated_variable.short_name,
170
+ migrated_variable.id,
171
+ )
172
+ return migrated_variable
173
+
118
174
  @classmethod
119
175
  @vardef_exception_handler
120
176
  def list_variable_definitions(
@@ -146,7 +202,7 @@ class Vardef:
146
202
 
147
203
  @classmethod
148
204
  @vardef_exception_handler
149
- def get_variable_definition(
205
+ def get_variable_definition_by_id(
150
206
  cls,
151
207
  variable_definition_id: str,
152
208
  date_of_validity: date | None = None,
@@ -171,3 +227,68 @@ class Vardef:
171
227
  date_of_validity=date_of_validity,
172
228
  ),
173
229
  )
230
+
231
+ @classmethod
232
+ @vardef_exception_handler
233
+ def get_variable_definition_by_shortname(
234
+ cls,
235
+ short_name: str,
236
+ date_of_validity: date | None = None,
237
+ ) -> VariableDefinition:
238
+ """Retrieve a Variable Definition by ID or short name.
239
+
240
+ Args:
241
+ short_name (str): The short name of the Variable Definition.
242
+ date_of_validity (date | None, optional): Filter by validity date. Defaults to None.
243
+
244
+ Returns:
245
+ VariableDefinition: The retrieved Variable Definition.
246
+
247
+ Raises:
248
+ VariableNotFoundError: If no matching Variable Definition is found.
249
+ ValueError: If multiple variables with the same shortname is found.
250
+ """
251
+ client = VardefClient.get_client()
252
+ api = VariableDefinitionsApi(client)
253
+
254
+ variable_definitions = api.list_variable_definitions(
255
+ short_name=short_name,
256
+ date_of_validity=date_of_validity,
257
+ )
258
+
259
+ if not variable_definitions:
260
+ msg = f"Variable with short name {short_name} not found"
261
+ raise VariableNotFoundError(msg)
262
+ if len(variable_definitions) > 1:
263
+ msg = f"Lookup by short name {short_name} found multiple variables which should not be possible."
264
+ raise VariableNotFoundError(msg)
265
+
266
+ return VariableDefinition.from_model(variable_definitions[0])
267
+
268
+ @classmethod
269
+ @vardef_file_error_handler
270
+ def write_template_to_file(cls, custom_file_path: str | None = None) -> Path:
271
+ """Write template with default values to a yaml file."""
272
+ file_path = create_template_yaml(
273
+ custom_directory=Path(custom_file_path) if custom_file_path else None,
274
+ )
275
+ logger.info(
276
+ f"Created editable variable definition template file at {file_path}", # noqa: G004
277
+ )
278
+ return file_path
279
+
280
+ @classmethod
281
+ @vardef_exception_handler
282
+ def does_short_name_exist(
283
+ cls,
284
+ short_name: str,
285
+ ) -> bool:
286
+ """Return True if the short name exists in Vardef, otherwise False."""
287
+ variable_definitions = Vardef.list_variable_definitions()
288
+ for variable in variable_definitions:
289
+ if short_name.strip() == variable.short_name:
290
+ logger.info(
291
+ f"Found duplicate short name {short_name}", # noqa: G004
292
+ )
293
+ return True
294
+ return False
@@ -1,47 +1,51 @@
1
+ import logging
1
2
  from datetime import date
3
+ from os import PathLike
4
+ from pathlib import Path
2
5
 
3
- from dapla_metadata.variable_definitions import config
4
- from dapla_metadata.variable_definitions._client import VardefClient
5
- from dapla_metadata.variable_definitions.exceptions import vardef_exception_handler
6
- from dapla_metadata.variable_definitions.generated.vardef_client.api.draft_variable_definitions_api import (
6
+ import yaml
7
+ from pydantic import ConfigDict
8
+ from pydantic import PrivateAttr
9
+
10
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.draft_variable_definitions_api import (
7
11
  DraftVariableDefinitionsApi,
8
12
  )
9
- from dapla_metadata.variable_definitions.generated.vardef_client.api.patches_api import (
13
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.patches_api import (
10
14
  PatchesApi,
11
15
  )
12
- from dapla_metadata.variable_definitions.generated.vardef_client.api.validity_periods_api import (
16
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.validity_periods_api import (
13
17
  ValidityPeriodsApi,
14
18
  )
15
- from dapla_metadata.variable_definitions.generated.vardef_client.models.complete_response import (
19
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.complete_response import (
16
20
  CompleteResponse,
17
21
  )
18
- from dapla_metadata.variable_definitions.generated.vardef_client.models.patch import (
22
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.patch import (
19
23
  Patch,
20
24
  )
21
- from dapla_metadata.variable_definitions.generated.vardef_client.models.update_draft import (
25
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.update_draft import (
22
26
  UpdateDraft,
23
27
  )
24
- from dapla_metadata.variable_definitions.generated.vardef_client.models.validity_period import (
28
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.validity_period import (
25
29
  ValidityPeriod,
26
30
  )
31
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.variable_status import (
32
+ VariableStatus,
33
+ )
34
+ from dapla_metadata.variable_definitions._utils import config
35
+ from dapla_metadata.variable_definitions._utils._client import VardefClient
36
+ from dapla_metadata.variable_definitions._utils.variable_definition_files import (
37
+ _read_file_to_model,
38
+ )
39
+ from dapla_metadata.variable_definitions._utils.variable_definition_files import (
40
+ create_variable_yaml,
41
+ )
42
+ from dapla_metadata.variable_definitions.exceptions import vardef_exception_handler
43
+ from dapla_metadata.variable_definitions.exceptions import vardef_file_error_handler
27
44
 
45
+ logger = logging.getLogger(__name__)
28
46
 
29
- class CompletePatchOutput(CompleteResponse):
30
- """Complete response For internal users who need all details while maintaining variable definitions."""
31
-
32
- @staticmethod
33
- def from_model(
34
- model: CompleteResponse,
35
- ) -> "CompletePatchOutput":
36
- """Create a CompletePatchOutput instance from a CompletePatchOutput."""
37
- return CompletePatchOutput.model_construct(**model.model_dump())
38
-
39
- def __str__(self) -> str:
40
- """Format as indented JSON."""
41
- return self.model_dump_json(indent=2, warnings=False)
42
47
 
43
-
44
- class VariableDefinition(CompletePatchOutput):
48
+ class VariableDefinition(CompleteResponse):
45
49
  """A Variable Definition.
46
50
 
47
51
  - Provides access to the fields of the specific Variable Definition.
@@ -49,21 +53,33 @@ class VariableDefinition(CompletePatchOutput):
49
53
  - Provides methods allowing maintenance of this Variable Definition.
50
54
 
51
55
  Args:
52
- CompletePatchOutput: The Pydantic model superclass, representing a Variable Definition.
56
+ CompleteResponse: The Pydantic model superclass, representing a Variable Definition.
53
57
  """
54
58
 
59
+ _file_path: Path | None = PrivateAttr(None)
60
+
61
+ model_config = ConfigDict(use_enum_values=True, str_strip_whitespace=True)
62
+
63
+ def get_file_path(self) -> Path | None:
64
+ """Get the file path where the variable definition has been written to for editing."""
65
+ return self._file_path
66
+
67
+ def set_file_path(self, file_path: Path | None) -> None:
68
+ """Set the file path where the variable definition has been written to for editing."""
69
+ self._file_path = file_path
70
+
55
71
  @staticmethod
56
72
  def from_model(
57
73
  model: CompleteResponse,
58
74
  ) -> "VariableDefinition":
59
- """Create a VariableDefinition instance from a CompletePatchOutput or CompletePatchOutput."""
75
+ """Create a VariableDefinition instance from a CompleteResponse."""
60
76
  return VariableDefinition.model_construct(**model.model_dump())
61
77
 
62
78
  @vardef_exception_handler
63
- def list_validity_periods(self) -> list[CompletePatchOutput]:
79
+ def list_validity_periods(self) -> list["VariableDefinition"]:
64
80
  """List all Validity Periods for this Variable Definition."""
65
81
  return [
66
- CompletePatchOutput.from_model(validity_period)
82
+ VariableDefinition.from_model(validity_period)
67
83
  for validity_period in ValidityPeriodsApi(
68
84
  VardefClient.get_client(),
69
85
  ).list_validity_periods(
@@ -72,10 +88,10 @@ class VariableDefinition(CompletePatchOutput):
72
88
  ]
73
89
 
74
90
  @vardef_exception_handler
75
- def list_patches(self) -> list[CompletePatchOutput]:
91
+ def list_patches(self) -> list["VariableDefinition"]:
76
92
  """List all Patches for this Variable Definition."""
77
93
  return [
78
- CompletePatchOutput.from_model(patch)
94
+ VariableDefinition.from_model(patch)
79
95
  for patch in PatchesApi(VardefClient.get_client()).list_patches(
80
96
  variable_definition_id=self.id,
81
97
  )
@@ -85,7 +101,7 @@ class VariableDefinition(CompletePatchOutput):
85
101
  def update_draft(
86
102
  self,
87
103
  update_draft: UpdateDraft,
88
- ) -> CompletePatchOutput:
104
+ ) -> "VariableDefinition":
89
105
  """Update this Variable Definition.
90
106
 
91
107
  - Variable definition must have status 'DRAFT'.
@@ -95,9 +111,9 @@ class VariableDefinition(CompletePatchOutput):
95
111
  update_draft: The input with updated values.
96
112
 
97
113
  Returns:
98
- CompletePatchOutput: Updated Variable definition with all details.
114
+ VariableDefinition: Updated Variable definition with all details.
99
115
  """
100
- return CompletePatchOutput.from_model(
116
+ updated = VariableDefinition.from_model(
101
117
  DraftVariableDefinitionsApi(
102
118
  VardefClient.get_client(),
103
119
  ).update_variable_definition_by_id(
@@ -106,6 +122,38 @@ class VariableDefinition(CompletePatchOutput):
106
122
  update_draft=update_draft,
107
123
  ),
108
124
  )
125
+ logger.info(
126
+ "Successfully updated variable definition '%s' with ID '%s'",
127
+ updated.short_name,
128
+ updated.id,
129
+ )
130
+ return updated
131
+
132
+ @vardef_file_error_handler
133
+ def update_draft_from_file(
134
+ self,
135
+ file_path: PathLike | None = None,
136
+ ) -> "VariableDefinition":
137
+ """Update this Variable Definition.
138
+
139
+ Will automatically read the relevant file pertaining to this variable definition. Can
140
+ be overridden by specifying the file_path parameter.
141
+
142
+ - Variable definition must have status 'DRAFT'.
143
+ - Supply only the fields to be changed. Other fields will retain their current values.
144
+
145
+ Args:
146
+ file_path: Optionally specify the path to read from.
147
+
148
+ Returns:
149
+ VariableDefinition: Updated Variable definition with all details.
150
+ """
151
+ return self.update_draft(
152
+ _read_file_to_model(
153
+ file_path or self.get_file_path(),
154
+ UpdateDraft,
155
+ ),
156
+ )
109
157
 
110
158
  @vardef_exception_handler
111
159
  def delete_draft(
@@ -128,16 +176,16 @@ class VariableDefinition(CompletePatchOutput):
128
176
  return f"Variable {self.id} safely deleted"
129
177
 
130
178
  @vardef_exception_handler
131
- def get_patch(self, patch_id: int) -> CompletePatchOutput:
179
+ def get_patch(self, patch_id: int) -> "VariableDefinition":
132
180
  """Get a single Patch by ID.
133
181
 
134
182
  Args:
135
183
  patch_id (int): The ID of the patch.
136
184
 
137
185
  Returns:
138
- CompletePatchOutput: The desired patch.
186
+ VariableDefinition: The desired patch.
139
187
  """
140
- return CompletePatchOutput.from_model(
188
+ return VariableDefinition.from_model(
141
189
  PatchesApi(VardefClient.get_client()).get_patch(
142
190
  variable_definition_id=self.id,
143
191
  patch_id=patch_id,
@@ -149,7 +197,7 @@ class VariableDefinition(CompletePatchOutput):
149
197
  self,
150
198
  patch: Patch,
151
199
  valid_from: date | None = None,
152
- ) -> CompletePatchOutput:
200
+ ) -> "VariableDefinition":
153
201
  """Create a new Patch for this Variable Definition.
154
202
 
155
203
  Patches are to be used for minor changes which don't require a new Validity Period.
@@ -167,10 +215,10 @@ class VariableDefinition(CompletePatchOutput):
167
215
  created in the last validity period.
168
216
 
169
217
  Returns:
170
- CompletePatchOutput: Variable Definition with all details.
218
+ VariableDefinition: Variable Definition with all details.
171
219
 
172
220
  """
173
- return CompletePatchOutput.from_model(
221
+ new_patch = VariableDefinition.from_model(
174
222
  PatchesApi(
175
223
  VardefClient.get_client(),
176
224
  ).create_patch(
@@ -180,12 +228,55 @@ class VariableDefinition(CompletePatchOutput):
180
228
  valid_from=valid_from,
181
229
  ),
182
230
  )
231
+ logger.info(
232
+ "Successfully created patch with patch ID '%s' for variable definition '%s' with ID '%s'",
233
+ new_patch.patch_id,
234
+ new_patch.short_name,
235
+ new_patch.id,
236
+ )
237
+ return new_patch
238
+
239
+ @vardef_file_error_handler
240
+ def create_patch_from_file(
241
+ self,
242
+ file_path: PathLike | None = None,
243
+ valid_from: date | None = None,
244
+ ) -> "VariableDefinition":
245
+ """Create a new Patch for this Variable Definition from a file.
246
+
247
+ Will automatically read the relevant file pertaining to this variable definition. Can
248
+ be overridden by specifying the file_path parameter.
249
+
250
+ Patches are to be used for minor changes which don't require a new Validity Period.
251
+ Examples of reasons for creating a new Patch:
252
+ - Correcting a typo
253
+ - Adding a translation
254
+ - Adding a subject field
255
+
256
+ Supply only the fields to be changed. Other fields will retain their current values.
257
+
258
+ Args:
259
+ file_path: Optionally specify the path to read from.
260
+ valid_from: Optional date for selecting a Validity Period to create patch in. The date must
261
+ exactly match the Validity Period `valid_from`. If value is None the patch is
262
+ created in the last validity period.
263
+
264
+ Returns:
265
+ VariableDefinition: Variable Definition with all details.
266
+ """
267
+ return self.create_patch(
268
+ patch=_read_file_to_model(
269
+ file_path or self.get_file_path(),
270
+ Patch,
271
+ ),
272
+ valid_from=valid_from,
273
+ )
183
274
 
184
275
  @vardef_exception_handler
185
276
  def create_validity_period(
186
277
  self,
187
278
  validity_period: ValidityPeriod,
188
- ) -> CompletePatchOutput:
279
+ ) -> "VariableDefinition":
189
280
  """Create a new Validity Period for this Variable Definition.
190
281
 
191
282
  In order to create a new Validity Period input must contain updated
@@ -199,9 +290,9 @@ class VariableDefinition(CompletePatchOutput):
199
290
  validity_period: The input for new Validity Period
200
291
 
201
292
  Returns:
202
- CompletePatchOutput: Variable Definition with all details.
293
+ VariableDefinition: Variable Definition with all details.
203
294
  """
204
- return CompletePatchOutput.from_model(
295
+ new_validity_period = VariableDefinition.from_model(
205
296
  ValidityPeriodsApi(
206
297
  VardefClient.get_client(),
207
298
  ).create_validity_period(
@@ -210,3 +301,110 @@ class VariableDefinition(CompletePatchOutput):
210
301
  validity_period=validity_period,
211
302
  ),
212
303
  )
304
+
305
+ logger.info(
306
+ "Successfully created validity period that is valid from '%s' for variable definition '%s' with ID '%s'",
307
+ new_validity_period.valid_from,
308
+ new_validity_period.short_name,
309
+ new_validity_period.id,
310
+ )
311
+ return new_validity_period
312
+
313
+ @vardef_file_error_handler
314
+ def create_validity_period_from_file(
315
+ self,
316
+ file_path: PathLike | None = None,
317
+ ) -> "VariableDefinition":
318
+ """Create a new ValidityPeriod for this Variable Definition from a file.
319
+
320
+ In order to create a new Validity Period the input file must contain updated
321
+ 'definition' text for all present languages and a new valid from.
322
+
323
+ Args:
324
+ file_path: Optionally specify the path to read from.
325
+
326
+ Returns:
327
+ VariableDefinition: Variable Definition with all details.
328
+ """
329
+ return self.create_validity_period(
330
+ validity_period=_read_file_to_model(
331
+ file_path or self.get_file_path(),
332
+ ValidityPeriod,
333
+ ),
334
+ )
335
+
336
+ def publish_internal(self) -> "VariableDefinition":
337
+ """Publish this variable definition internally."""
338
+ if self.variable_status != VariableStatus.DRAFT.name:
339
+ msg = "That won't work here. Only variable definitions with status DRAFT may be published internally."
340
+ raise ValueError(
341
+ msg,
342
+ )
343
+ update = self.update_draft(
344
+ UpdateDraft(variable_status=VariableStatus.PUBLISHED_INTERNAL),
345
+ )
346
+ logger.info(
347
+ "Variable definition '%s' with ID '%s' successfully published, new status: %s",
348
+ update.short_name,
349
+ update.id,
350
+ update.variable_status,
351
+ )
352
+ return update
353
+
354
+ def publish_external(self) -> "VariableDefinition":
355
+ """Publish this variable definition externally."""
356
+ if self.variable_status == VariableStatus.PUBLISHED_EXTERNAL.name:
357
+ msg = "That won't work here. The variable definition is already published."
358
+ raise ValueError(
359
+ msg,
360
+ )
361
+ if self.variable_status is VariableStatus.DRAFT:
362
+ update = self.update_draft(
363
+ UpdateDraft(variable_status=VariableStatus.PUBLISHED_EXTERNAL),
364
+ )
365
+ else:
366
+ update = self.create_patch(
367
+ Patch(variable_status=VariableStatus.PUBLISHED_EXTERNAL),
368
+ )
369
+ logger.info(
370
+ "Variable definition '%s' with ID '%s' successfully published, new status: %s",
371
+ update.short_name,
372
+ update.id,
373
+ update.variable_status,
374
+ )
375
+ return update
376
+
377
+ def to_file(self) -> "VariableDefinition":
378
+ """Write this variable definition to file."""
379
+ file_path = create_variable_yaml(
380
+ model_instance=self,
381
+ )
382
+ self.set_file_path(file_path)
383
+ logger.info(
384
+ f"Created editable variable definition file at {file_path}", # noqa: G004
385
+ )
386
+ return self
387
+
388
+ def to_dict(self) -> dict:
389
+ """Return as dictionary."""
390
+ return super().to_dict()
391
+
392
+ def __str__(self) -> str:
393
+ """Format as indented YAML."""
394
+ return self._convert_to_yaml_output()
395
+
396
+ def __repr__(self) -> str:
397
+ """Format as indented YAML."""
398
+ return self._convert_to_yaml_output()
399
+
400
+ def _convert_to_yaml_output(self) -> str:
401
+ return yaml.dump(
402
+ self.model_dump(
403
+ mode="json",
404
+ serialize_as_any=True,
405
+ warnings="error",
406
+ ),
407
+ allow_unicode=True,
408
+ default_flow_style=False,
409
+ sort_keys=False,
410
+ )