dapla-toolbelt-metadata 0.6.2__tar.gz → 0.6.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (89) hide show
  1. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/PKG-INFO +3 -3
  2. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/pyproject.toml +38 -51
  3. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/core.py +110 -10
  4. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/standards/name_validator.py +2 -2
  5. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/standards/utils/constants.py +2 -2
  6. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_utils/config.py +1 -2
  7. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_utils/constants.py +23 -0
  8. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_utils/files.py +107 -11
  9. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_utils/template_files.py +8 -15
  10. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_utils/variable_definition_files.py +32 -5
  11. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/resources/vardef_model_descriptions_nb.yaml +53 -17
  12. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/vardef.py +3 -3
  13. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/variable_definition.py +19 -21
  14. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/LICENSE +0 -0
  15. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/README.md +0 -0
  16. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/__init__.py +0 -0
  17. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/_shared/__init__.py +0 -0
  18. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/_shared/config.py +0 -0
  19. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/_shared/enums.py +0 -0
  20. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/_shared/py.typed +0 -0
  21. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/dapla/__init__.py +0 -0
  22. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/dapla/user_info.py +0 -0
  23. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/__init__.py +0 -0
  24. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/code_list.py +0 -0
  25. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/dapla_dataset_path_info.py +0 -0
  26. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/dataset_parser.py +0 -0
  27. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/external_sources/__init__.py +0 -0
  28. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/external_sources/external_sources.py +0 -0
  29. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/model_backwards_compatibility.py +0 -0
  30. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/model_validation.py +0 -0
  31. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/py.typed +0 -0
  32. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/statistic_subject_mapping.py +0 -0
  33. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/utility/__init__.py +0 -0
  34. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/utility/constants.py +0 -0
  35. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/utility/enums.py +0 -0
  36. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/datasets/utility/utils.py +0 -0
  37. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/standards/__init__.py +0 -0
  38. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/standards/standard_validators.py +0 -0
  39. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/standards/utils/__init__.py +0 -0
  40. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/__init__.py +0 -0
  41. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/.openapi-generator/FILES +0 -0
  42. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/.openapi-generator/VERSION +0 -0
  43. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/.openapi-generator-ignore +0 -0
  44. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/README.md +0 -0
  45. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/__init__.py +0 -0
  46. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/__init__.py +0 -0
  47. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/api/__init__.py +0 -0
  48. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/api/data_migration_api.py +0 -0
  49. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/api/draft_variable_definitions_api.py +0 -0
  50. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/api/patches_api.py +0 -0
  51. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/api/validity_periods_api.py +0 -0
  52. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/api/variable_definitions_api.py +0 -0
  53. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/api_client.py +0 -0
  54. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/api_response.py +0 -0
  55. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/configuration.py +0 -0
  56. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/CompleteResponse.md +0 -0
  57. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/Contact.md +0 -0
  58. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/DataMigrationApi.md +0 -0
  59. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/Draft.md +0 -0
  60. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/DraftVariableDefinitionsApi.md +0 -0
  61. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/LanguageStringType.md +0 -0
  62. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/Owner.md +0 -0
  63. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/Patch.md +0 -0
  64. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/PatchesApi.md +0 -0
  65. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/PublicApi.md +0 -0
  66. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/SupportedLanguages.md +0 -0
  67. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/UpdateDraft.md +0 -0
  68. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/ValidityPeriod.md +0 -0
  69. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/ValidityPeriodsApi.md +0 -0
  70. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/VariableDefinitionsApi.md +0 -0
  71. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/docs/VariableStatus.md +0 -0
  72. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/exceptions.py +0 -0
  73. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/__init__.py +0 -0
  74. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/complete_response.py +0 -0
  75. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/contact.py +0 -0
  76. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/draft.py +0 -0
  77. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/language_string_type.py +0 -0
  78. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/owner.py +0 -0
  79. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/patch.py +0 -0
  80. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/problem.py +0 -0
  81. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/update_draft.py +0 -0
  82. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/validity_period.py +0 -0
  83. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/models/variable_status.py +0 -0
  84. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/py.typed +0 -0
  85. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_generated/vardef_client/rest.py +0 -0
  86. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_utils/__init__.py +0 -0
  87. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_utils/_client.py +0 -0
  88. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/_utils/descriptions.py +0 -0
  89. {dapla_toolbelt_metadata-0.6.2 → dapla_toolbelt_metadata-0.6.4}/src/dapla_metadata/variable_definitions/exceptions.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dapla-toolbelt-metadata
3
- Version: 0.6.2
3
+ Version: 0.6.4
4
4
  Summary: Dapla Toolbelt Metadata
5
5
  License: MIT
6
6
  Author: Team Metadata
7
7
  Author-email: metadata@ssb.no
8
- Requires-Python: >=3.10,<4.0
8
+ Requires-Python: >=3.10
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python :: 3
@@ -24,7 +24,7 @@ Requires-Dist: pyjwt (>=2.8.0)
24
24
  Requires-Dist: python-dotenv (>=1.0.1)
25
25
  Requires-Dist: requests (>=2.31.0)
26
26
  Requires-Dist: ruamel-yaml (>=0.18.10)
27
- Requires-Dist: ssb-datadoc-model (>=6.0.0,<7.0.0)
27
+ Requires-Dist: ssb-datadoc-model (==6.0.0)
28
28
  Requires-Dist: ssb-klass-python (>=1.0.1)
29
29
  Requires-Dist: typing-extensions (>=4.12.2)
30
30
  Project-URL: Changelog, https://github.com/statisticsnorway/dapla-toolbelt-metadata/releases
@@ -1,36 +1,45 @@
1
- [tool.poetry]
1
+ [project]
2
2
  name = "dapla-toolbelt-metadata"
3
- version = "0.6.2"
4
3
  description = "Dapla Toolbelt Metadata"
5
- authors = ["Team Metadata <metadata@ssb.no>"]
6
4
  license = "MIT"
5
+ version = "0.6.4"
6
+ dynamic = ["classifiers"]
7
7
  readme = "README.md"
8
+ authors = [{ name = "Team Metadata", email = "metadata@ssb.no" }]
9
+ requires-python = '>=3.10'
10
+ dependencies = [
11
+ 'pyarrow >=8.0.0',
12
+ 'pydantic >=2.5.2',
13
+ 'arrow >=1.3.0',
14
+ 'python-dotenv >=1.0.1',
15
+ 'requests >=2.31.0',
16
+ 'beautifulsoup4 >=4.12.3',
17
+ 'cloudpathlib[gs] >=0.17.0',
18
+ 'pyjwt >=2.8.0',
19
+ 'ssb-klass-python >=1.0.1',
20
+ 'ssb-datadoc-model ==6.0.0',
21
+ 'typing-extensions >=4.12.2',
22
+ 'ruamel-yaml >=0.18.10',
23
+ 'google-auth >=2.38.0',
24
+ 'lxml >=5.3.1',
25
+ ]
26
+
27
+ [project.urls]
8
28
  homepage = "https://github.com/statisticsnorway/dapla-toolbelt-metadata"
9
29
  repository = "https://github.com/statisticsnorway/dapla-toolbelt-metadata"
10
30
  documentation = "https://statisticsnorway.github.io/dapla-toolbelt-metadata"
11
- classifiers = ["Development Status :: 4 - Beta"]
31
+ Changelog = "https://github.com/statisticsnorway/dapla-toolbelt-metadata/releases"
12
32
 
33
+ [tool.poetry]
34
+ requires-poetry = '>=2.0,<3.0'
13
35
  packages = [{ include = "dapla_metadata", from = "src" }]
36
+ classifiers = ["Development Status :: 4 - Beta"]
14
37
 
15
- [tool.poetry.urls]
16
- Changelog = "https://github.com/statisticsnorway/dapla-toolbelt-metadata/releases"
38
+ [tool.poetry.requires-plugins]
39
+ poetry-plugin-export = ">=1.9.0" # Used with nox
17
40
 
18
41
  [tool.poetry.dependencies]
19
- python = ">=3.10,<4.0"
20
- pyarrow = ">=8.0.0"
21
- pydantic = ">=2.5.2"
22
- arrow = ">=1.3.0"
23
- python-dotenv = ">=1.0.1"
24
- requests = ">=2.31.0"
25
- beautifulsoup4 = ">=4.12.3"
26
- cloudpathlib = { extras = ["gs"], version = ">=0.17.0" }
27
- pyjwt = ">=2.8.0"
28
- ssb-klass-python = ">=1.0.1"
29
- ssb-datadoc-model = "^6.0.0"
30
- typing-extensions = ">=4.12.2"
31
- ruamel-yaml = ">=0.18.10"
32
- google-auth = ">=2.38.0"
33
- lxml = ">=5.3.1"
42
+ python = ">=3.10,<4.0" # This is currently needed for compatibility with ssb-klass-python
34
43
 
35
44
  [tool.poetry.group.dev.dependencies]
36
45
  pygments = ">=2.18.0"
@@ -64,7 +73,7 @@ types-beautifulsoup4 = ">=4.12.0.20240511"
64
73
  ipykernel = "^6.29.5"
65
74
  rich = "^13.9.4"
66
75
  bpython = "^0.24"
67
- testcontainers = { version = "^4.8.2", extras = ["generic"] }
76
+ testcontainers = { version = ">=4.8.2", extras = ["generic"] }
68
77
  pytest-asyncio = "^0.26.0"
69
78
 
70
79
  [tool.pytest.ini_options]
@@ -77,11 +86,7 @@ tests = ["tests", "*/tests", "*/test"]
77
86
  [tool.coverage.run]
78
87
  branch = true
79
88
  source = ["dapla_metadata"]
80
- omit = [
81
- "tests/*",
82
- "__init__.py",
83
- "*/dapla_metadata/variable_definitions/_generated/*",
84
- ]
89
+ omit = ["tests/*", "__init__.py", "*/dapla_metadata/variable_definitions/_generated/*"]
85
90
  relative_files = true
86
91
 
87
92
  [tool.coverage.report]
@@ -105,7 +110,6 @@ warn_unreachable = false
105
110
  ignore_errors = true
106
111
  disable_error_code = ["unreachable"]
107
112
 
108
-
109
113
  [[tool.mypy.overrides]]
110
114
  # Allow missing type hints in third-party libraries without type information.
111
115
  module = [
@@ -127,27 +131,15 @@ ignore_missing_imports = true
127
131
  # Also don't require type annotations
128
132
  [[tool.mypy.overrides]]
129
133
  module = ["tests.*"]
130
- disable_error_code = [
131
- "var-annotated",
132
- "has-type",
133
- "no-any-return",
134
- "no-untyped-def",
135
- ]
134
+ disable_error_code = ["var-annotated", "has-type", "no-any-return", "no-untyped-def"]
136
135
 
137
136
  [tool.ruff]
138
- force-exclude = true # Apply excludes to pre-commit
137
+ force-exclude = true # Apply excludes to pre-commit
139
138
  show-fixes = true
140
139
  src = ["src", "tests"]
141
- target-version = "py310" # Minimum Python version supported
140
+ target-version = "py310" # Minimum Python version supported
142
141
  include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
143
- extend-exclude = [
144
- "__pycache__",
145
- "old",
146
- ".ipynb_checkpoints",
147
- "noxfile.py",
148
- "docs/conf.py",
149
- "_generated/",
150
- ]
142
+ extend-exclude = ["__pycache__", "old", ".ipynb_checkpoints", "noxfile.py", "docs/conf.py", "_generated/"]
151
143
 
152
144
  # Ruff rules may be customized as desired: https://docs.astral.sh/ruff/rules/
153
145
  [tool.ruff.lint]
@@ -181,12 +173,7 @@ fixture-parentheses = false
181
173
  max-args = 8
182
174
 
183
175
  [tool.ruff.lint.pep8-naming]
184
- classmethod-decorators = [
185
- "classmethod",
186
- "validator",
187
- "root_validator",
188
- "pydantic.validator",
189
- ]
176
+ classmethod-decorators = ["classmethod", "validator", "root_validator", "pydantic.validator"]
190
177
 
191
178
  [tool.ruff.lint.per-file-ignores]
192
179
  "*/__init__.py" = ["F401"]
@@ -206,5 +193,5 @@ classmethod-decorators = [
206
193
  ]
207
194
 
208
195
  [build-system]
209
- requires = ["poetry-core>=1.0.0"]
196
+ requires = ["poetry-core>=2.0"]
210
197
  build-backend = "poetry.core.masonry.api"
@@ -109,8 +109,11 @@ class Datadoc:
109
109
  self.dataset_path: pathlib.Path | CloudPath | None = None
110
110
  self.dataset = model.Dataset()
111
111
  self.variables: list = []
112
+ self.pseudo_variables: list[model.PseudoVariable] = []
112
113
  self.variables_lookup: dict[str, model.Variable] = {}
114
+ self.pseudo_variables_lookup: dict[str, model.PseudoVariable] = {}
113
115
  self.explicitly_defined_metadata_document = False
116
+ self.dataset_consistency_status: list = []
114
117
  if metadata_document_path:
115
118
  self.metadata_document = normalize_path(metadata_document_path)
116
119
  self.explicitly_defined_metadata_document = True
@@ -148,11 +151,19 @@ class Datadoc:
148
151
  """
149
152
  extracted_metadata: model.DatadocMetadata | None = None
150
153
  existing_metadata: model.DatadocMetadata | None = None
154
+ existing_pseudonymization: model.PseudonymizationMetadata | None = None
155
+
151
156
  if self.metadata_document and self.metadata_document.exists():
152
157
  existing_metadata = self._extract_metadata_from_existing_document(
153
158
  self.metadata_document,
154
159
  )
155
160
 
161
+ existing_pseudonymization = (
162
+ self._extract_pseudonymization_from_existing_document(
163
+ self.metadata_document,
164
+ )
165
+ )
166
+
156
167
  if (
157
168
  self.dataset_path is not None
158
169
  and self.dataset == model.Dataset()
@@ -169,11 +180,14 @@ class Datadoc:
169
180
  and existing_metadata is not None
170
181
  ):
171
182
  existing_file_path = self._get_existing_file_path(extracted_metadata)
172
- self._check_ready_to_merge(
183
+ self.dataset_consistency_status = self._check_dataset_consistency(
173
184
  self.dataset_path,
174
185
  Path(existing_file_path),
175
186
  extracted_metadata,
176
187
  existing_metadata,
188
+ )
189
+ self._check_ready_to_merge(
190
+ self.dataset_consistency_status,
177
191
  errors_as_warnings=self.errors_as_warnings,
178
192
  )
179
193
  merged_metadata = self._merge_metadata(
@@ -188,10 +202,15 @@ class Datadoc:
188
202
  self._set_metadata(merged_metadata)
189
203
  else:
190
204
  self._set_metadata(existing_metadata or extracted_metadata)
205
+
206
+ if existing_pseudonymization:
207
+ self._set_pseudonymization_metadata(existing_pseudonymization)
208
+
191
209
  set_default_values_variables(self.variables)
192
210
  set_default_values_dataset(self.dataset)
193
211
  set_dataset_owner(self.dataset)
194
212
  self._create_variables_lookup()
213
+ self._create_pseudo_variables_lookup()
195
214
 
196
215
  def _get_existing_file_path(
197
216
  self,
@@ -218,35 +237,48 @@ class Datadoc:
218
237
  self.dataset = merged_metadata.dataset
219
238
  self.variables = merged_metadata.variables
220
239
 
240
+ def _set_pseudonymization_metadata(
241
+ self,
242
+ existing_pseudonymization: model.PseudonymizationMetadata | None,
243
+ ) -> None:
244
+ if not existing_pseudonymization or not (
245
+ existing_pseudonymization.pseudo_variables
246
+ ):
247
+ msg = "Could not read pseudonymization metadata"
248
+ raise ValueError(msg)
249
+ self.pseudo_variables = existing_pseudonymization.pseudo_variables
250
+
221
251
  def _create_variables_lookup(self) -> None:
222
252
  self.variables_lookup = {
223
253
  v.short_name: v for v in self.variables if v.short_name
224
254
  }
225
255
 
256
+ def _create_pseudo_variables_lookup(self) -> None:
257
+ self.pseudo_variables_lookup = {
258
+ v.short_name: v for v in self.pseudo_variables if v.short_name
259
+ }
260
+
226
261
  @staticmethod
227
- def _check_ready_to_merge(
262
+ def _check_dataset_consistency(
228
263
  new_dataset_path: Path | CloudPath,
229
264
  existing_dataset_path: Path,
230
265
  extracted_metadata: model.DatadocMetadata,
231
266
  existing_metadata: model.DatadocMetadata,
232
- *,
233
- errors_as_warnings: bool,
234
- ) -> None:
235
- """Check if the datasets are consistent enough to make a successful merge of metadata.
267
+ ) -> list[dict[str, object]]:
268
+ """Run consistency tests.
236
269
 
237
270
  Args:
238
271
  new_dataset_path: Path to the dataset to be documented.
239
272
  existing_dataset_path: Path stored in the existing metadata.
240
273
  extracted_metadata: Metadata extracted from a physical dataset.
241
274
  existing_metadata: Metadata from a previously created metadata document.
242
- errors_as_warnings: True if failing checks should be raised as warnings, not errors.
243
275
 
244
- Raises:
245
- InconsistentDatasetsError: If inconsistencies are found and `errors_as_warnings == False`
276
+ Returns:
277
+ List if dict with property name and boolean success flag
246
278
  """
247
279
  new_dataset_path_info = DaplaDatasetPathInfo(new_dataset_path)
248
280
  existing_dataset_path_info = DaplaDatasetPathInfo(existing_dataset_path)
249
- results = [
281
+ return [
250
282
  {
251
283
  "name": "Bucket name",
252
284
  "success": (
@@ -290,6 +322,20 @@ class Datadoc:
290
322
  ),
291
323
  },
292
324
  ]
325
+
326
+ @staticmethod
327
+ def _check_ready_to_merge(
328
+ results: list[dict[str, object]], *, errors_as_warnings: bool
329
+ ) -> None:
330
+ """Check if the datasets are consistent enough to make a successful merge of metadata.
331
+
332
+ Args:
333
+ results: List if dict with property name and boolean success flag
334
+ errors_as_warnings: True if failing checks should be raised as warnings, not errors.
335
+
336
+ Raises:
337
+ InconsistentDatasetsError: If inconsistencies are found and `errors_as_warnings == False`
338
+ """
293
339
  if failures := [result for result in results if not result["success"]]:
294
340
  msg = f"{INCONSISTENCIES_MESSAGE} {', '.join(str(f['name']) for f in failures)}"
295
341
  if errors_as_warnings:
@@ -384,6 +430,42 @@ class Datadoc:
384
430
  )
385
431
  return None
386
432
 
433
+ def _extract_pseudonymization_from_existing_document(
434
+ self,
435
+ document: pathlib.Path | CloudPath,
436
+ ) -> model.PseudonymizationMetadata | None:
437
+ """Read pseudo metadata from an existing metadata document.
438
+
439
+ If there is pseudo metadata in the document supplied, the method validates and returns the pseudonymization structure.
440
+
441
+ Args:
442
+ document: A path to the existing metadata document.
443
+
444
+ Raises:
445
+ json.JSONDecodeError: If the metadata document cannot be parsed.
446
+ """
447
+ try:
448
+ with document.open(mode="r", encoding="utf-8") as file:
449
+ fresh_metadata = json.load(file)
450
+ except json.JSONDecodeError:
451
+ logger.warning(
452
+ "Could not open existing metadata file %s.",
453
+ document,
454
+ exc_info=True,
455
+ )
456
+ return None
457
+
458
+ if not is_metadata_in_container_structure(fresh_metadata):
459
+ return None
460
+
461
+ pseudonymization_metadata = fresh_metadata.get("pseudonymization")
462
+ if pseudonymization_metadata is None:
463
+ return None
464
+
465
+ return model.PseudonymizationMetadata.model_validate_json(
466
+ json.dumps(pseudonymization_metadata),
467
+ )
468
+
387
469
  def _extract_subject_field_from_path(
388
470
  self,
389
471
  dapla_dataset_path_info: DaplaDatasetPathInfo,
@@ -501,6 +583,11 @@ class Datadoc:
501
583
  )
502
584
  if self.container:
503
585
  self.container.datadoc = datadoc
586
+ if not self.container.pseudonymization:
587
+ self.container.pseudonymization = model.PseudonymizationMetadata(
588
+ pseudo_dataset=model.PseudoDataset()
589
+ )
590
+ self.container.pseudonymization.pseudo_variables = self.pseudo_variables
504
591
  else:
505
592
  self.container = model.MetadataContainer(datadoc=datadoc)
506
593
  if self.metadata_document:
@@ -530,3 +617,16 @@ class Datadoc:
530
617
  self.dataset,
531
618
  ) + num_obligatory_variables_fields_completed(self.variables)
532
619
  return calculate_percentage(num_set_fields, num_all_fields)
620
+
621
+ def add_pseudo_variable(self, variable_short_name: str) -> None:
622
+ """Adds a new pseudo variable to the list of pseudonymized variables."""
623
+ if self.variables_lookup[variable_short_name] is not None:
624
+ pseudo_variable = model.PseudoVariable(short_name=variable_short_name)
625
+ self.pseudo_variables.append(pseudo_variable)
626
+ self.pseudo_variables_lookup[variable_short_name] = pseudo_variable
627
+
628
+ def get_pseudo_variable(
629
+ self, variable_short_name: str
630
+ ) -> model.PseudoVariable | None:
631
+ """Finds a pseudo variable by shortname."""
632
+ return self.pseudo_variables_lookup.get(variable_short_name)
@@ -128,9 +128,9 @@ class NamingStandardReport:
128
128
  """Returns an appropriate message based on the success rate."""
129
129
  rate = self.success_rate()
130
130
  if rate is not None:
131
- if rate == 100:
131
+ if 95 <= rate <= 100:
132
132
  return SSB_NAMING_STANDARD_REPORT_RESULT_BEST
133
- if 70 < rate < 100:
133
+ if 70 < rate < 95:
134
134
  return SSB_NAMING_STANDARD_REPORT_RESULT_GOOD
135
135
  if 40 <= rate <= 70:
136
136
  return SSB_NAMING_STANDARD_REPORT_RESULT_AVERAGE
@@ -9,7 +9,7 @@ NAME_STANDARD_SUCCESS = "Filene dine er i samsvar med SSB-navnestandarden"
9
9
  NAME_STANDARD_VIOLATION = "Det er oppdaget brudd på SSB-navnestandard:"
10
10
 
11
11
  MISSING_BUCKET_NAME = "Filnavn mangler bøttenavn ref: https://manual.dapla.ssb.no/statistikkere/navnestandard.html#obligatoriske-mapper"
12
- MISSING_VERSION = "Filnavn mangler versjon ref: https://manual.dapla.ssb.no/statistikkere/navnestandard.html#filnavn"
12
+ MISSING_VERSION = "Filnavn mangler versjon, hvis ikke filen er nyeste versjon kan dette være brudd på navnestandarden ref: https://manual.dapla.ssb.no/statistikkere/navnestandard.html#versjonering-av-datasett"
13
13
  MISSING_PERIOD = "Filnavn mangler gyldighetsperiode ref: https://manual.dapla.ssb.no/statistikkere/navnestandard.html#filnavn"
14
14
  MISSING_SHORT_NAME = "Kortnavn for statistikk mangler ref: https://manual.dapla.ssb.no/statistikkere/navnestandard.html#obligatoriske-mapper"
15
15
  MISSING_DATA_STATE = "Mappe for datatilstand mangler ref: https://manual.dapla.ssb.no/statistikkere/navnestandard.html#obligatoriske-mapper"
@@ -26,7 +26,7 @@ BUCKET_NAME_UNKNOWN = "Kan ikke validere bøttenavn"
26
26
 
27
27
  SSB_NAMING_STANDARD_REPORT = "SSB navnestandard rapport"
28
28
  SSB_NAMING_STANDARD_REPORT_SUCCESS_RATE = "Suksess rate"
29
- SSB_NAMING_STANDARD_REPORT_RESULT_BEST = "🚀 Fantastisk! Alt bestått! 🎉\n"
29
+ SSB_NAMING_STANDARD_REPORT_RESULT_BEST = "🚀 Fantastisk! 🎉\n"
30
30
  SSB_NAMING_STANDARD_REPORT_RESULT_GOOD = (
31
31
  "✅ Bra jobba! Fortsatt litt rom for forbedring. 😊\n"
32
32
  )
@@ -59,8 +59,7 @@ def get_vardef_host() -> str:
59
59
  case DaplaEnvironment.TEST:
60
60
  return VARDEF_HOST_TEST
61
61
  case DaplaEnvironment.DEV:
62
- msg = "Vardef is not available in dev."
63
- raise NotImplementedError(msg)
62
+ return VARDEF_HOST_TEST
64
63
  case _:
65
64
  return get_config_item("VARDEF_HOST") or "http://localhost:8080"
66
65
 
@@ -39,3 +39,26 @@ MACHINE_GENERATED_FIELDS = [
39
39
 
40
40
  OPTIONAL_FIELD = "~ Valgfritt felt ~"
41
41
  REQUIRED_FIELD = "! Obligatorisk felt !"
42
+
43
+ YAML_STR_TAG = "tag:yaml.org,2002:str"
44
+
45
+ BLOCK_FIELDS = [
46
+ "definition",
47
+ "name",
48
+ "contact.title",
49
+ "comment",
50
+ ]
51
+
52
+ DOUBLE_QUOTE_FIELDS = [
53
+ "unit_types",
54
+ "subject_fields",
55
+ "related_variable_definition_uris",
56
+ "owner",
57
+ "short_name",
58
+ "classification_reference",
59
+ "measurement_type",
60
+ "external_reference_uri",
61
+ "created_by",
62
+ "id",
63
+ "last_updated_by",
64
+ ]
@@ -4,12 +4,16 @@ import logging
4
4
  from datetime import datetime
5
5
  from pathlib import Path
6
6
  from typing import TYPE_CHECKING
7
+ from typing import Any
7
8
  from typing import cast
8
9
 
9
10
  import pytz
10
11
  from pydantic.config import JsonDict
11
12
  from ruamel.yaml import YAML
12
13
  from ruamel.yaml import CommentedMap
14
+ from ruamel.yaml import RoundTripRepresenter
15
+ from ruamel.yaml.scalarstring import DoubleQuotedScalarString
16
+ from ruamel.yaml.scalarstring import LiteralScalarString
13
17
 
14
18
  from dapla_metadata.variable_definitions._generated.vardef_client.models.complete_response import (
15
19
  CompleteResponse,
@@ -18,6 +22,8 @@ from dapla_metadata.variable_definitions._generated.vardef_client.models.variabl
18
22
  VariableStatus,
19
23
  )
20
24
  from dapla_metadata.variable_definitions._utils import config
25
+ from dapla_metadata.variable_definitions._utils.constants import BLOCK_FIELDS
26
+ from dapla_metadata.variable_definitions._utils.constants import DOUBLE_QUOTE_FIELDS
21
27
  from dapla_metadata.variable_definitions._utils.constants import (
22
28
  MACHINE_GENERATED_FIELDS,
23
29
  )
@@ -40,6 +46,7 @@ from dapla_metadata.variable_definitions._utils.constants import (
40
46
  from dapla_metadata.variable_definitions._utils.constants import (
41
47
  VARIABLE_STATUS_FIELD_NAME,
42
48
  )
49
+ from dapla_metadata.variable_definitions._utils.constants import YAML_STR_TAG
43
50
  from dapla_metadata.variable_definitions._utils.descriptions import (
44
51
  apply_norwegian_descriptions_to_model,
45
52
  )
@@ -112,6 +119,15 @@ def _get_variable_definitions_dir():
112
119
  return folder_path
113
120
 
114
121
 
122
+ def _set_field_requirement(field_name: str, field: Any) -> str | None:
123
+ """Determine the field requirement status."""
124
+ if field_name not in MACHINE_GENERATED_FIELDS:
125
+ if field.is_required() or field_name == VARIABLE_STATUS_FIELD_NAME:
126
+ return REQUIRED_FIELD
127
+ return OPTIONAL_FIELD
128
+ return None
129
+
130
+
115
131
  def _populate_commented_map(
116
132
  field_name: str,
117
133
  value: str,
@@ -120,16 +136,17 @@ def _populate_commented_map(
120
136
  ) -> None:
121
137
  """Add data to a CommentedMap."""
122
138
  commented_map[field_name] = value
123
- field = model_instance.model_fields[field_name]
139
+ field = type(model_instance).model_fields[field_name]
124
140
  description: JsonValue = cast(
125
141
  JsonDict,
126
142
  field.json_schema_extra,
127
143
  )[NORWEGIAN_DESCRIPTIONS]
144
+ field_requirement: str | None = _set_field_requirement(field_name, field)
128
145
  if description is not None:
129
146
  new_description = (
130
- (REQUIRED_FIELD if field.is_required() else OPTIONAL_FIELD)
131
- + "\n"
132
- + str(description)
147
+ ("\n" + field_requirement + "\n" + str(description))
148
+ if field_requirement
149
+ else ("\n" + str(description))
133
150
  )
134
151
  commented_map.yaml_set_comment_before_after_key(
135
152
  field_name,
@@ -174,14 +191,22 @@ def _validate_and_create_directory(custom_directory: Path) -> Path:
174
191
  return custom_directory
175
192
 
176
193
 
177
- def _configure_yaml() -> YAML:
178
- yaml = YAML() # Use ruamel.yaml library
179
- yaml.default_flow_style = False # Ensures pretty YAML formatting
180
-
194
+ def configure_yaml(yaml: YAML) -> YAML:
195
+ """Common Yaml config for variable definitions."""
196
+ yaml.Representer = RoundTripRepresenter # Preserve the order of keys etc.
197
+ yaml.default_flow_style = False # Ensures pretty YAML formatting block style
198
+ yaml.allow_unicode = True # Support special characters
199
+ yaml.preserve_quotes = True
200
+ yaml.width = 4096 # prevent wrapping lines
201
+ yaml.indent(
202
+ mapping=4,
203
+ sequence=6,
204
+ offset=4,
205
+ ) # Ensure indentation for nested keys and lists
181
206
  yaml.representer.add_representer(
182
207
  VariableStatus,
183
208
  lambda dumper, data: dumper.represent_scalar(
184
- "tag:yaml.org,2002:str",
209
+ YAML_STR_TAG,
185
210
  data.value,
186
211
  ),
187
212
  )
@@ -189,6 +214,76 @@ def _configure_yaml() -> YAML:
189
214
  return yaml
190
215
 
191
216
 
217
+ def _safe_get(data: dict, keys: list):
218
+ """Safely navigate nested dictionaries."""
219
+ for key in keys:
220
+ if not isinstance(data, dict) or key not in data or data[key] is None:
221
+ return None
222
+ data = data[key]
223
+ return data
224
+
225
+
226
+ def _apply_literal_scalars(field: dict):
227
+ """Helper function to wrap `LanguageStringType` values in `LiteralScalarString`.
228
+
229
+ This function wraps each non-`None` language value in a `LanguageStringType` field
230
+ in the `LiteralScalarString` YAML type, ensuring proper YAML formatting with block style.
231
+ """
232
+ for lang, value in field.items():
233
+ if value is not None:
234
+ field[lang] = LiteralScalarString(value)
235
+
236
+
237
+ def _apply_double_quotes_to_dict_values(field: dict):
238
+ """Helper function to wrap dictionary values in `DoubleQuotedScalarString`.
239
+
240
+ This function wraps each non-`None` value in a dictionary, including values inside lists,
241
+ in the `DoubleQuotedScalarString` YAML type, ensuring proper YAML formatting with double quotes.
242
+ """
243
+ for sub_key, sub_value in field.items():
244
+ if isinstance(sub_value, list):
245
+ field[sub_key] = [
246
+ DoubleQuotedScalarString(item) for item in sub_value if item is not None
247
+ ]
248
+ elif sub_value is not None:
249
+ field[sub_key] = DoubleQuotedScalarString(sub_value)
250
+
251
+
252
+ def pre_process_data(data: dict) -> dict:
253
+ """Format variable definition model fields with ruamel YAML scalar string types.
254
+
255
+ This method sets the appropriate scalar string type (either `LiteralScalarString` or `DoubleQuotedScalarString`)
256
+ for fields of the variable definition model, based on predefined lists of fields.
257
+
258
+ It processes both nested dictionaries and lists, ensuring each element is formatted with the correct YAML string type.
259
+
260
+ Args:
261
+ data (dict): A dictionary containing the variable definition data.
262
+
263
+ Returns:
264
+ dict: The updated dictionary with model fields formatted as ruamel.yaml scalar string types.
265
+ """
266
+ for key in BLOCK_FIELDS:
267
+ keys = key.split(".")
268
+ field = _safe_get(data, keys)
269
+ if isinstance(field, dict):
270
+ _apply_literal_scalars(field)
271
+
272
+ for key in DOUBLE_QUOTE_FIELDS:
273
+ keys = key.split(".")
274
+ field = _safe_get(data, keys)
275
+ if isinstance(field, list):
276
+ data[key] = [
277
+ DoubleQuotedScalarString(item) for item in field if item is not None
278
+ ]
279
+ elif isinstance(field, str):
280
+ data[key] = DoubleQuotedScalarString(data[key])
281
+ elif isinstance(field, dict):
282
+ _apply_double_quotes_to_dict_values(field)
283
+
284
+ return data
285
+
286
+
192
287
  def _model_to_yaml_with_comments(
193
288
  model_instance: CompleteResponse,
194
289
  file_name: str,
@@ -209,7 +304,8 @@ def _model_to_yaml_with_comments(
209
304
  Returns:
210
305
  Path: The file path of the generated YAML file.
211
306
  """
212
- yaml = _configure_yaml()
307
+ yaml = YAML()
308
+ configure_yaml(yaml)
213
309
 
214
310
  from dapla_metadata.variable_definitions.variable_definition import (
215
311
  VariableDefinition,
@@ -223,7 +319,7 @@ def _model_to_yaml_with_comments(
223
319
  serialize_as_any=True,
224
320
  warnings="error",
225
321
  )
226
-
322
+ data = pre_process_data(data)
227
323
  # One CommentMap for each section in the yaml file
228
324
  machine_generated_map = CommentedMap()
229
325
  commented_map = CommentedMap()