dapla-toolbelt-metadata 0.4.2__py3-none-any.whl → 0.6.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 (87) hide show
  1. dapla_metadata/__init__.py +9 -0
  2. dapla_metadata/dapla/__init__.py +4 -0
  3. dapla_metadata/{_shared → dapla}/user_info.py +66 -20
  4. dapla_metadata/datasets/code_list.py +1 -1
  5. dapla_metadata/datasets/core.py +1 -1
  6. dapla_metadata/datasets/dapla_dataset_path_info.py +128 -14
  7. dapla_metadata/datasets/dataset_parser.py +21 -15
  8. dapla_metadata/datasets/model_backwards_compatibility.py +6 -6
  9. dapla_metadata/datasets/model_validation.py +2 -2
  10. dapla_metadata/datasets/utility/constants.py +1 -0
  11. dapla_metadata/datasets/utility/enums.py +1 -1
  12. dapla_metadata/datasets/utility/utils.py +8 -12
  13. dapla_metadata/standards/__init__.py +4 -0
  14. dapla_metadata/standards/name_validator.py +250 -0
  15. dapla_metadata/standards/standard_validators.py +98 -0
  16. dapla_metadata/standards/utils/__init__.py +1 -0
  17. dapla_metadata/standards/utils/constants.py +49 -0
  18. dapla_metadata/variable_definitions/__init__.py +5 -3
  19. dapla_metadata/variable_definitions/{generated → _generated}/.openapi-generator/FILES +0 -5
  20. dapla_metadata/variable_definitions/_generated/.openapi-generator/VERSION +1 -0
  21. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/__init__.py +0 -5
  22. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/__init__.py +0 -1
  23. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/data_migration_api.py +2 -2
  24. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/draft_variable_definitions_api.py +14 -14
  25. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/patches_api.py +15 -15
  26. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api/validity_periods_api.py +8 -281
  27. dapla_metadata/variable_definitions/{generated/vardef_client/api/public_api.py → _generated/vardef_client/api/variable_definitions_api.py} +73 -358
  28. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/__init__.py +2 -6
  29. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/complete_response.py +8 -32
  30. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/contact.py +2 -2
  31. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/draft.py +8 -23
  32. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/language_string_type.py +7 -6
  33. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/owner.py +2 -2
  34. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/patch.py +16 -61
  35. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/problem.py +2 -2
  36. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/update_draft.py +22 -55
  37. dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/models/validity_period.py +14 -48
  38. dapla_metadata/variable_definitions/_generated/vardef_client/models/variable_status.py +33 -0
  39. dapla_metadata/variable_definitions/_utils/__init__.py +1 -0
  40. dapla_metadata/variable_definitions/{_client.py → _utils/_client.py} +5 -3
  41. dapla_metadata/variable_definitions/{config.py → _utils/config.py} +25 -1
  42. dapla_metadata/variable_definitions/_utils/constants.py +41 -0
  43. dapla_metadata/variable_definitions/_utils/descriptions.py +89 -0
  44. dapla_metadata/variable_definitions/_utils/files.py +273 -0
  45. dapla_metadata/variable_definitions/_utils/template_files.py +112 -0
  46. dapla_metadata/variable_definitions/_utils/variable_definition_files.py +93 -0
  47. dapla_metadata/variable_definitions/exceptions.py +141 -11
  48. dapla_metadata/variable_definitions/resources/vardef_model_descriptions_nb.yaml +63 -0
  49. dapla_metadata/variable_definitions/vardef.py +131 -10
  50. dapla_metadata/variable_definitions/variable_definition.py +251 -43
  51. {dapla_toolbelt_metadata-0.4.2.dist-info → dapla_toolbelt_metadata-0.6.0.dist-info}/METADATA +8 -10
  52. dapla_toolbelt_metadata-0.6.0.dist-info/RECORD +89 -0
  53. {dapla_toolbelt_metadata-0.4.2.dist-info → dapla_toolbelt_metadata-0.6.0.dist-info}/WHEEL +1 -1
  54. dapla_metadata/variable_definitions/generated/.openapi-generator/VERSION +0 -1
  55. dapla_metadata/variable_definitions/generated/vardef_client/api/variable_definitions_api.py +0 -1205
  56. dapla_metadata/variable_definitions/generated/vardef_client/models/klass_reference.py +0 -99
  57. dapla_metadata/variable_definitions/generated/vardef_client/models/rendered_contact.py +0 -92
  58. dapla_metadata/variable_definitions/generated/vardef_client/models/rendered_variable_definition.py +0 -235
  59. dapla_metadata/variable_definitions/generated/vardef_client/models/supported_languages.py +0 -33
  60. dapla_metadata/variable_definitions/generated/vardef_client/models/variable_status.py +0 -33
  61. dapla_toolbelt_metadata-0.4.2.dist-info/RECORD +0 -81
  62. /dapla_metadata/variable_definitions/{generated → _generated}/.openapi-generator-ignore +0 -0
  63. /dapla_metadata/variable_definitions/{generated → _generated}/README.md +0 -0
  64. /dapla_metadata/variable_definitions/{generated → _generated}/__init__.py +0 -0
  65. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api_client.py +0 -0
  66. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/api_response.py +0 -0
  67. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/configuration.py +0 -0
  68. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/CompleteResponse.md +0 -0
  69. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/Contact.md +0 -0
  70. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/DataMigrationApi.md +0 -0
  71. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/Draft.md +0 -0
  72. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/DraftVariableDefinitionsApi.md +0 -0
  73. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/LanguageStringType.md +0 -0
  74. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/Owner.md +0 -0
  75. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/Patch.md +0 -0
  76. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/PatchesApi.md +0 -0
  77. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/PublicApi.md +0 -0
  78. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/SupportedLanguages.md +0 -0
  79. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/UpdateDraft.md +0 -0
  80. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/ValidityPeriod.md +0 -0
  81. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/ValidityPeriodsApi.md +0 -0
  82. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/VariableDefinitionsApi.md +0 -0
  83. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/docs/VariableStatus.md +0 -0
  84. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/exceptions.py +0 -0
  85. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/py.typed +0 -0
  86. /dapla_metadata/variable_definitions/{generated → _generated}/vardef_client/rest.py +0 -0
  87. {dapla_toolbelt_metadata-0.4.2.dist-info → dapla_toolbelt_metadata-0.6.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,52 @@
1
+ import logging
1
2
  from datetime import date
3
+ from io import StringIO
4
+ from os import PathLike
5
+ from pathlib import Path
2
6
 
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 (
7
+ import ruamel.yaml
8
+ from pydantic import ConfigDict
9
+ from pydantic import PrivateAttr
10
+
11
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.draft_variable_definitions_api import (
7
12
  DraftVariableDefinitionsApi,
8
13
  )
9
- from dapla_metadata.variable_definitions.generated.vardef_client.api.patches_api import (
14
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.patches_api import (
10
15
  PatchesApi,
11
16
  )
12
- from dapla_metadata.variable_definitions.generated.vardef_client.api.validity_periods_api import (
17
+ from dapla_metadata.variable_definitions._generated.vardef_client.api.validity_periods_api import (
13
18
  ValidityPeriodsApi,
14
19
  )
15
- from dapla_metadata.variable_definitions.generated.vardef_client.models.complete_response import (
20
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.complete_response import (
16
21
  CompleteResponse,
17
22
  )
18
- from dapla_metadata.variable_definitions.generated.vardef_client.models.patch import (
23
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.patch import (
19
24
  Patch,
20
25
  )
21
- from dapla_metadata.variable_definitions.generated.vardef_client.models.update_draft import (
26
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.update_draft import (
22
27
  UpdateDraft,
23
28
  )
24
- from dapla_metadata.variable_definitions.generated.vardef_client.models.validity_period import (
29
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.validity_period import (
25
30
  ValidityPeriod,
26
31
  )
32
+ from dapla_metadata.variable_definitions._generated.vardef_client.models.variable_status import (
33
+ VariableStatus,
34
+ )
35
+ from dapla_metadata.variable_definitions._utils import config
36
+ from dapla_metadata.variable_definitions._utils._client import VardefClient
37
+ from dapla_metadata.variable_definitions._utils.variable_definition_files import (
38
+ _read_file_to_model,
39
+ )
40
+ from dapla_metadata.variable_definitions._utils.variable_definition_files import (
41
+ create_variable_yaml,
42
+ )
43
+ from dapla_metadata.variable_definitions.exceptions import vardef_exception_handler
44
+ from dapla_metadata.variable_definitions.exceptions import vardef_file_error_handler
27
45
 
46
+ logger = logging.getLogger(__name__)
28
47
 
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
48
 
43
-
44
- class VariableDefinition(CompletePatchOutput):
49
+ class VariableDefinition(CompleteResponse):
45
50
  """A Variable Definition.
46
51
 
47
52
  - Provides access to the fields of the specific Variable Definition.
@@ -49,21 +54,33 @@ class VariableDefinition(CompletePatchOutput):
49
54
  - Provides methods allowing maintenance of this Variable Definition.
50
55
 
51
56
  Args:
52
- CompletePatchOutput: The Pydantic model superclass, representing a Variable Definition.
57
+ CompleteResponse: The Pydantic model superclass, representing a Variable Definition.
53
58
  """
54
59
 
60
+ _file_path: Path | None = PrivateAttr(None)
61
+
62
+ model_config = ConfigDict(use_enum_values=True, str_strip_whitespace=True)
63
+
64
+ def get_file_path(self) -> Path | None:
65
+ """Get the file path where the variable definition has been written to for editing."""
66
+ return self._file_path
67
+
68
+ def set_file_path(self, file_path: Path | None) -> None:
69
+ """Set the file path where the variable definition has been written to for editing."""
70
+ self._file_path = file_path
71
+
55
72
  @staticmethod
56
73
  def from_model(
57
74
  model: CompleteResponse,
58
75
  ) -> "VariableDefinition":
59
- """Create a VariableDefinition instance from a CompletePatchOutput or CompletePatchOutput."""
76
+ """Create a VariableDefinition instance from a CompleteResponse."""
60
77
  return VariableDefinition.model_construct(**model.model_dump())
61
78
 
62
79
  @vardef_exception_handler
63
- def list_validity_periods(self) -> list[CompletePatchOutput]:
80
+ def list_validity_periods(self) -> list["VariableDefinition"]:
64
81
  """List all Validity Periods for this Variable Definition."""
65
82
  return [
66
- CompletePatchOutput.from_model(validity_period)
83
+ VariableDefinition.from_model(validity_period)
67
84
  for validity_period in ValidityPeriodsApi(
68
85
  VardefClient.get_client(),
69
86
  ).list_validity_periods(
@@ -72,10 +89,10 @@ class VariableDefinition(CompletePatchOutput):
72
89
  ]
73
90
 
74
91
  @vardef_exception_handler
75
- def list_patches(self) -> list[CompletePatchOutput]:
92
+ def list_patches(self) -> list["VariableDefinition"]:
76
93
  """List all Patches for this Variable Definition."""
77
94
  return [
78
- CompletePatchOutput.from_model(patch)
95
+ VariableDefinition.from_model(patch)
79
96
  for patch in PatchesApi(VardefClient.get_client()).list_patches(
80
97
  variable_definition_id=self.id,
81
98
  )
@@ -85,7 +102,7 @@ class VariableDefinition(CompletePatchOutput):
85
102
  def update_draft(
86
103
  self,
87
104
  update_draft: UpdateDraft,
88
- ) -> CompletePatchOutput:
105
+ ) -> "VariableDefinition":
89
106
  """Update this Variable Definition.
90
107
 
91
108
  - Variable definition must have status 'DRAFT'.
@@ -95,9 +112,9 @@ class VariableDefinition(CompletePatchOutput):
95
112
  update_draft: The input with updated values.
96
113
 
97
114
  Returns:
98
- CompletePatchOutput: Updated Variable definition with all details.
115
+ VariableDefinition: Updated Variable definition with all details.
99
116
  """
100
- return CompletePatchOutput.from_model(
117
+ updated = VariableDefinition.from_model(
101
118
  DraftVariableDefinitionsApi(
102
119
  VardefClient.get_client(),
103
120
  ).update_variable_definition_by_id(
@@ -106,6 +123,40 @@ class VariableDefinition(CompletePatchOutput):
106
123
  update_draft=update_draft,
107
124
  ),
108
125
  )
126
+ self.__dict__.update(updated)
127
+
128
+ logger.info(
129
+ "Successfully updated variable definition '%s' with ID '%s'",
130
+ updated.short_name,
131
+ updated.id,
132
+ )
133
+ return updated
134
+
135
+ @vardef_file_error_handler
136
+ def update_draft_from_file(
137
+ self,
138
+ file_path: PathLike | None = None,
139
+ ) -> "VariableDefinition":
140
+ """Update this Variable Definition.
141
+
142
+ Will automatically read the relevant file pertaining to this variable definition. Can
143
+ be overridden by specifying the file_path parameter.
144
+
145
+ - Variable definition must have status 'DRAFT'.
146
+ - Supply only the fields to be changed. Other fields will retain their current values.
147
+
148
+ Args:
149
+ file_path: Optionally specify the path to read from.
150
+
151
+ Returns:
152
+ VariableDefinition: Updated Variable definition with all details.
153
+ """
154
+ return self.update_draft(
155
+ _read_file_to_model(
156
+ file_path or self.get_file_path(),
157
+ UpdateDraft,
158
+ ),
159
+ )
109
160
 
110
161
  @vardef_exception_handler
111
162
  def delete_draft(
@@ -128,16 +179,16 @@ class VariableDefinition(CompletePatchOutput):
128
179
  return f"Variable {self.id} safely deleted"
129
180
 
130
181
  @vardef_exception_handler
131
- def get_patch(self, patch_id: int) -> CompletePatchOutput:
182
+ def get_patch(self, patch_id: int) -> "VariableDefinition":
132
183
  """Get a single Patch by ID.
133
184
 
134
185
  Args:
135
186
  patch_id (int): The ID of the patch.
136
187
 
137
188
  Returns:
138
- CompletePatchOutput: The desired patch.
189
+ VariableDefinition: The desired patch.
139
190
  """
140
- return CompletePatchOutput.from_model(
191
+ return VariableDefinition.from_model(
141
192
  PatchesApi(VardefClient.get_client()).get_patch(
142
193
  variable_definition_id=self.id,
143
194
  patch_id=patch_id,
@@ -149,7 +200,7 @@ class VariableDefinition(CompletePatchOutput):
149
200
  self,
150
201
  patch: Patch,
151
202
  valid_from: date | None = None,
152
- ) -> CompletePatchOutput:
203
+ ) -> "VariableDefinition":
153
204
  """Create a new Patch for this Variable Definition.
154
205
 
155
206
  Patches are to be used for minor changes which don't require a new Validity Period.
@@ -167,10 +218,10 @@ class VariableDefinition(CompletePatchOutput):
167
218
  created in the last validity period.
168
219
 
169
220
  Returns:
170
- CompletePatchOutput: Variable Definition with all details.
221
+ VariableDefinition: Variable Definition with all details.
171
222
 
172
223
  """
173
- return CompletePatchOutput.from_model(
224
+ new_patch = VariableDefinition.from_model(
174
225
  PatchesApi(
175
226
  VardefClient.get_client(),
176
227
  ).create_patch(
@@ -180,12 +231,57 @@ class VariableDefinition(CompletePatchOutput):
180
231
  valid_from=valid_from,
181
232
  ),
182
233
  )
234
+ self.__dict__.update(new_patch)
235
+
236
+ logger.info(
237
+ "Successfully created patch with patch ID '%s' for variable definition '%s' with ID '%s'",
238
+ new_patch.patch_id,
239
+ new_patch.short_name,
240
+ new_patch.id,
241
+ )
242
+ return new_patch
243
+
244
+ @vardef_file_error_handler
245
+ def create_patch_from_file(
246
+ self,
247
+ file_path: PathLike | None = None,
248
+ valid_from: date | None = None,
249
+ ) -> "VariableDefinition":
250
+ """Create a new Patch for this Variable Definition from a file.
251
+
252
+ Will automatically read the relevant file pertaining to this variable definition. Can
253
+ be overridden by specifying the file_path parameter.
254
+
255
+ Patches are to be used for minor changes which don't require a new Validity Period.
256
+ Examples of reasons for creating a new Patch:
257
+ - Correcting a typo
258
+ - Adding a translation
259
+ - Adding a subject field
260
+
261
+ Supply only the fields to be changed. Other fields will retain their current values.
262
+
263
+ Args:
264
+ file_path: Optionally specify the path to read from.
265
+ valid_from: Optional date for selecting a Validity Period to create patch in. The date must
266
+ exactly match the Validity Period `valid_from`. If value is None the patch is
267
+ created in the last validity period.
268
+
269
+ Returns:
270
+ VariableDefinition: Variable Definition with all details.
271
+ """
272
+ return self.create_patch(
273
+ patch=_read_file_to_model(
274
+ file_path or self.get_file_path(),
275
+ Patch,
276
+ ),
277
+ valid_from=valid_from,
278
+ )
183
279
 
184
280
  @vardef_exception_handler
185
281
  def create_validity_period(
186
282
  self,
187
283
  validity_period: ValidityPeriod,
188
- ) -> CompletePatchOutput:
284
+ ) -> "VariableDefinition":
189
285
  """Create a new Validity Period for this Variable Definition.
190
286
 
191
287
  In order to create a new Validity Period input must contain updated
@@ -199,9 +295,9 @@ class VariableDefinition(CompletePatchOutput):
199
295
  validity_period: The input for new Validity Period
200
296
 
201
297
  Returns:
202
- CompletePatchOutput: Variable Definition with all details.
298
+ VariableDefinition: Variable Definition with all details.
203
299
  """
204
- return CompletePatchOutput.from_model(
300
+ new_validity_period = VariableDefinition.from_model(
205
301
  ValidityPeriodsApi(
206
302
  VardefClient.get_client(),
207
303
  ).create_validity_period(
@@ -210,3 +306,115 @@ class VariableDefinition(CompletePatchOutput):
210
306
  validity_period=validity_period,
211
307
  ),
212
308
  )
309
+ self.__dict__.update(new_validity_period)
310
+
311
+ logger.info(
312
+ "Successfully created validity period that is valid from '%s' for variable definition '%s' with ID '%s'",
313
+ new_validity_period.valid_from,
314
+ new_validity_period.short_name,
315
+ new_validity_period.id,
316
+ )
317
+ return new_validity_period
318
+
319
+ @vardef_file_error_handler
320
+ def create_validity_period_from_file(
321
+ self,
322
+ file_path: PathLike | None = None,
323
+ ) -> "VariableDefinition":
324
+ """Create a new ValidityPeriod for this Variable Definition from a file.
325
+
326
+ In order to create a new Validity Period the input file must contain updated
327
+ 'definition' text for all present languages and a new valid from.
328
+
329
+ Args:
330
+ file_path: Optionally specify the path to read from.
331
+
332
+ Returns:
333
+ VariableDefinition: Variable Definition with all details.
334
+ """
335
+ return self.create_validity_period(
336
+ validity_period=_read_file_to_model(
337
+ file_path or self.get_file_path(),
338
+ ValidityPeriod,
339
+ ),
340
+ )
341
+
342
+ def publish_internal(self) -> "VariableDefinition":
343
+ """Publish this variable definition internally."""
344
+ if self.variable_status != VariableStatus.DRAFT.name:
345
+ msg = "That won't work here. Only variable definitions with status DRAFT may be published internally."
346
+ raise ValueError(
347
+ msg,
348
+ )
349
+ update = self.update_draft(
350
+ UpdateDraft(variable_status=VariableStatus.PUBLISHED_INTERNAL),
351
+ )
352
+ logger.info(
353
+ "Variable definition '%s' with ID '%s' successfully published, new status: %s",
354
+ update.short_name,
355
+ update.id,
356
+ update.variable_status,
357
+ )
358
+ return update
359
+
360
+ def publish_external(self) -> "VariableDefinition":
361
+ """Publish this variable definition externally."""
362
+ if self.variable_status == VariableStatus.PUBLISHED_EXTERNAL.name:
363
+ msg = "That won't work here. The variable definition is already published."
364
+ raise ValueError(
365
+ msg,
366
+ )
367
+ if self.variable_status is VariableStatus.DRAFT:
368
+ update = self.update_draft(
369
+ UpdateDraft(variable_status=VariableStatus.PUBLISHED_EXTERNAL),
370
+ )
371
+ else:
372
+ update = self.create_patch(
373
+ Patch(variable_status=VariableStatus.PUBLISHED_EXTERNAL),
374
+ )
375
+ logger.info(
376
+ "Variable definition '%s' with ID '%s' successfully published, new status: %s",
377
+ update.short_name,
378
+ update.id,
379
+ update.variable_status,
380
+ )
381
+ return update
382
+
383
+ def to_file(self) -> "VariableDefinition":
384
+ """Write this variable definition to file."""
385
+ file_path = create_variable_yaml(
386
+ model_instance=self,
387
+ )
388
+ self.set_file_path(file_path)
389
+ logger.info(
390
+ f"Created editable variable definition file at {file_path}", # noqa: G004
391
+ )
392
+ return self
393
+
394
+ def to_dict(self) -> dict:
395
+ """Return as dictionary."""
396
+ return super().to_dict()
397
+
398
+ def __str__(self) -> str:
399
+ """Format as indented YAML."""
400
+ return self._convert_to_yaml_output()
401
+
402
+ def __repr__(self) -> str:
403
+ """Format as indented YAML."""
404
+ return self._convert_to_yaml_output()
405
+
406
+ def _convert_to_yaml_output(self) -> str:
407
+ stream = StringIO()
408
+ with ruamel.yaml.YAML(
409
+ output=stream,
410
+ ) as yaml:
411
+ yaml.default_flow_style = False
412
+ yaml.allow_unicode = True
413
+ yaml.dump(
414
+ self.model_dump(
415
+ mode="json",
416
+ serialize_as_any=True,
417
+ warnings="error",
418
+ ),
419
+ )
420
+ return stream.getvalue()