data-validation-engine 0.8.5__tar.gz → 0.10.0__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.
Files changed (109) hide show
  1. data_validation_engine-0.10.0/PKG-INFO +100 -0
  2. data_validation_engine-0.10.0/README.md +61 -0
  3. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/pyproject.toml +18 -14
  4. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/common/error_utils.py +1 -1
  5. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/auditing.py +8 -8
  6. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/contract.py +0 -4
  7. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/reader.py +71 -24
  8. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/rules.py +194 -3
  9. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/exceptions.py +28 -16
  10. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/auditing.py +8 -8
  11. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/contract.py +7 -10
  12. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/duckdb_helpers.py +37 -9
  13. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/readers/csv.py +24 -21
  14. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/readers/json.py +4 -2
  15. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/readers/xml.py +10 -6
  16. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/rules.py +107 -38
  17. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/auditing.py +1 -1
  18. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/backend.py +1 -1
  19. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/contract.py +9 -8
  20. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/readers/csv.py +6 -4
  21. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/readers/json.py +2 -0
  22. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/readers/xml.py +9 -5
  23. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/rules.py +127 -25
  24. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/spark_helpers.py +52 -25
  25. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/utilities.py +1 -1
  26. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/metadata/contract.py +3 -3
  27. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/metadata/reporting.py +9 -7
  28. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/metadata/rules.py +37 -30
  29. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/readers/csv.py +9 -11
  30. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/readers/utilities.py +15 -15
  31. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/readers/xml.py +32 -20
  32. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/readers/xml_linting.py +3 -2
  33. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/utilities.py +39 -8
  34. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/configuration/v1/__init__.py +102 -11
  35. data_validation_engine-0.10.0/src/dve/core_engine/configuration/v1/hierarchy.py +210 -0
  36. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/configuration/v1/steps.py +8 -9
  37. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/constants.py +5 -0
  38. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/engine.py +47 -34
  39. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/functions/implementations.py +0 -110
  40. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/loggers.py +1 -1
  41. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/message.py +16 -16
  42. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/models.py +35 -19
  43. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/type_hints.py +10 -1
  44. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/validation.py +4 -5
  45. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/metadata_parser/domain_types.py +143 -93
  46. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/metadata_parser/function_library.py +2 -2
  47. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/metadata_parser/function_wrapper.py +17 -24
  48. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/metadata_parser/model_generator.py +7 -9
  49. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/metadata_parser/models.py +52 -45
  50. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/metadata_parser/utilities.py +12 -1
  51. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/implementations/file.py +4 -4
  52. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/service.py +5 -2
  53. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/pipeline/pipeline.py +187 -31
  54. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/pipeline/utils.py +31 -5
  55. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/reporting/__init__.py +2 -0
  56. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/reporting/error_report.py +1 -1
  57. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/reporting/excel_report.py +20 -0
  58. data_validation_engine-0.8.5/PKG-INFO +0 -120
  59. data_validation_engine-0.8.5/README.md +0 -81
  60. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/LICENSE +0 -0
  61. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/__init__.py +0 -0
  62. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/common/__init__.py +0 -0
  63. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/__init__.py +0 -0
  64. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/__init__.py +0 -0
  65. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/__init__.py +0 -0
  66. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/backend.py +0 -0
  67. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/core.py +0 -0
  68. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/reference_data.py +0 -0
  69. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/base/utilities.py +0 -0
  70. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/__init__.py +0 -0
  71. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/__init__.py +0 -0
  72. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/readers/__init__.py +0 -0
  73. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/reference_data.py +0 -0
  74. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/types.py +0 -0
  75. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/duckdb/utilities.py +0 -0
  76. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/__init__.py +0 -0
  77. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/readers/__init__.py +0 -0
  78. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/reference_data.py +0 -0
  79. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/implementations/spark/types.py +0 -0
  80. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/metadata/__init__.py +0 -0
  81. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/readers/__init__.py +0 -0
  82. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/backends/types.py +0 -0
  83. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/configuration/__init__.py +0 -0
  84. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/configuration/base.py +0 -0
  85. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/configuration/v1/filters.py +0 -0
  86. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/configuration/v1/rule_stores/__init__.py +0 -0
  87. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/configuration/v1/rule_stores/models.py +0 -0
  88. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/exceptions.py +0 -0
  89. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/functions/__init__.py +0 -0
  90. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/core_engine/templating.py +0 -0
  91. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/metadata_parser/__init__.py +0 -0
  92. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/metadata_parser/exc.py +0 -0
  93. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/__init__.py +0 -0
  94. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/exceptions.py +0 -0
  95. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/__init__.py +0 -0
  96. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/helpers.py +0 -0
  97. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/implementations/__init__.py +0 -0
  98. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/implementations/base.py +0 -0
  99. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/implementations/dbfs.py +0 -0
  100. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/implementations/s3.py +0 -0
  101. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/log_handler.py +0 -0
  102. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/file_handling/utilities.py +0 -0
  103. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/type_hints.py +0 -0
  104. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/parser/utilities.py +0 -0
  105. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/pipeline/__init__.py +0 -0
  106. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/pipeline/duckdb_pipeline.py +0 -0
  107. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/pipeline/foundry_ddb_pipeline.py +0 -0
  108. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/pipeline/spark_pipeline.py +0 -0
  109. {data_validation_engine-0.8.5 → data_validation_engine-0.10.0}/src/dve/reporting/constants.py +0 -0
@@ -0,0 +1,100 @@
1
+ Metadata-Version: 2.4
2
+ Name: data-validation-engine
3
+ Version: 0.10.0
4
+ Summary: `nhs data validation engine` is a framework used to validate data
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Author: NHS England
8
+ Author-email: england.contactus@nhs.net
9
+ Requires-Python: >=3.11,<3.13
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Classifier: Typing :: Typed
17
+ Requires-Dist: Jinja2 (==3.1.6)
18
+ Requires-Dist: boto3 (>=1.34.162,<1.36)
19
+ Requires-Dist: botocore (>=1.34.162,<1.36)
20
+ Requires-Dist: delta-spark (>=3.0.0,<=3.2.0)
21
+ Requires-Dist: duckdb (>=1.4,<1.4.5)
22
+ Requires-Dist: lxml (==6.1.1)
23
+ Requires-Dist: numpy (==1.26.4)
24
+ Requires-Dist: openpyxl (==3.1.5)
25
+ Requires-Dist: pandas (==2.3.3)
26
+ Requires-Dist: polars (==0.20.31)
27
+ Requires-Dist: pyarrow (==23.0.1)
28
+ Requires-Dist: pydantic (==2.13.4)
29
+ Requires-Dist: pyspark (>=3.5.0,<=3.5.5)
30
+ Requires-Dist: typing_extensions (==4.16.0)
31
+ Requires-Dist: urllib3 (==2.8.0)
32
+ Project-URL: Changelog, https://github.com/NHSDigital/data-validation-engine/blob/main/CHANGELOG.md
33
+ Project-URL: Documentation, https://nhsdigital.github.io/data-validation-engine/
34
+ Project-URL: Homepage, https://github.com/NHSDigital/data-validation-engine
35
+ Project-URL: Issues, https://github.com/NHSDigital/data-validation-engine/issues
36
+ Project-URL: Repository, https://github.com/NHSDigital/data-validation-engine.git
37
+ Description-Content-Type: text/markdown
38
+
39
+ <h1 style="display: flex; align-items: center; gap: 10px;">
40
+ <img src="https://raw.githubusercontent.com/NHSDigital/data-validation-engine/616b55890306db4546177f7effac48ca241857ec/overrides/.icons/nhseng.svg" alt="" width="5%" height="100%" align="left">
41
+ Data Validation Engine
42
+ </h1>
43
+
44
+ ![License](https://img.shields.io/github/license/NHSDigital/data-validation-engine)
45
+ ![PyPi](https://img.shields.io/pypi/v/data-validation-engine)
46
+ ![Conda](https://anaconda.org/nhs/data-validation-engine/badges/version.svg)
47
+ [![CI Unit Tests](https://github.com/NHSDigital/data-validation-engine/actions/workflows/ci_testing.yml/badge.svg)](https://github.com/NHSDigital/data-validation-engine/actions/workflows/ci_testing.yml)
48
+ [![CI Formatting & Linting](https://github.com/NHSDigital/data-validation-engine/actions/workflows/ci_linting.yml/badge.svg)](https://github.com/NHSDigital/data-validation-engine/actions/workflows/ci_linting.yml)
49
+
50
+ The Data Validation Engine (DVE) is a configuration-driven data validation library created and used by NHS England. It lets users define validation rules once and apply them across multiple dataset collections - supporting consistent, accurate data checks.
51
+
52
+ __The DVE offers__:
53
+
54
+ - SQL configuration-based validations
55
+ - Format normalization to Parquet for a unified data representation
56
+ - Data modelling and typecasting
57
+ - Business-rule validations executed on supported backends such as Spark and DuckDB, with the option to add custom backends
58
+ - Validate and enforce referential integrity checks with minimal configuration
59
+ - Deriving new fields and entities
60
+ - Clear validation reporting, including summary insights and record-level error messages
61
+
62
+ As mentioned above, the DVE is "configuration driven" which means the majority of development for you as a user will be building a JSON document to describe how the data will be validated. The JSON document is known as a `dischema` (data ingest schema) file and example files can be accessed [here ↗️](https://github.com/NHSDigital/data-validation-engine/tree/main/tests/testdata). If you'd like to learn more about JSON document and how to build one from scratch, then please read the documentation [here ↗️](https://nhsdigital.github.io/data-validation-engine/).
63
+
64
+ ## Installation and usage
65
+
66
+ Please see the documentation [here ↗️](https://nhsdigital.github.io/data-validation-engine/user_guidance/install/).
67
+
68
+ ## Requesting new features and raising bug reports
69
+ **Before creating new issues, please check to see if the same bug/feature has been created already. Where a duplicate is created, the ticket will be closed and referenced to an existing issue.**
70
+
71
+ If you have spotted a bug with the DVE then please raise an issue [here ↗️](https://github.com/nhsengland/Data-Validation-Engine/issues) using the "bug template".
72
+
73
+ If you have feature request then please follow the same process whilst using the "Feature request template".
74
+
75
+ ## Upcoming features
76
+ Below is a list of features that we would like to implement or have been requested.
77
+ | Feature | Release Version | Released? |
78
+ | ------------------------------------------------------------------------------- | ----------------- | --------- |
79
+ | Open source release | 0.1.0 | Yes |
80
+ | Uplift to Python 3.11 | 0.2.0 | Yes |
81
+ | Uplift Pyspark to 3.5 | 0.8.0 | Yes |
82
+ | Allow DVE to run on Python 3.12+ | 0.8.0 | Yes |
83
+ | Upgrade to Pydantic 2.0 | 0.9.0 | Yes |
84
+ | Upgrade DuckDB to v1.4 | 0.10.0 | Yes |
85
+ | Uplift Pyspark to 4.0+ | TBA | No |
86
+ | Polars upgrade to v1+ | TBA | No |
87
+ | DuckDB upgrade to v1.5+ | TBA | No |
88
+ | Python 3.13 & 3.14 upgrade | TBA | No |
89
+ | Create a more user friendly interface for building and modifying dischema files | TBA | No |
90
+
91
+ If you are interested in getting any of the unreleased features listed above available, then please read the [Contributing](#Contributing) section and then submit us a pull request.
92
+
93
+ ## Contributing
94
+ Please see guidance [here ↗️](https://github.com/NHSDigital/data-validation-engine/blob/main/CONTRIBUTE.md).
95
+
96
+ ## Legal
97
+ This codebase is released under the MIT License. This covers both the codebase and any sample code in the documentation.
98
+
99
+ Any HTML or Markdown documentation is [© Crown copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/) and available under the terms of the [Open Government 3.0 licence](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).
100
+
@@ -0,0 +1,61 @@
1
+ <h1 style="display: flex; align-items: center; gap: 10px;">
2
+ <img src="https://raw.githubusercontent.com/NHSDigital/data-validation-engine/616b55890306db4546177f7effac48ca241857ec/overrides/.icons/nhseng.svg" alt="" width="5%" height="100%" align="left">
3
+ Data Validation Engine
4
+ </h1>
5
+
6
+ ![License](https://img.shields.io/github/license/NHSDigital/data-validation-engine)
7
+ ![PyPi](https://img.shields.io/pypi/v/data-validation-engine)
8
+ ![Conda](https://anaconda.org/nhs/data-validation-engine/badges/version.svg)
9
+ [![CI Unit Tests](https://github.com/NHSDigital/data-validation-engine/actions/workflows/ci_testing.yml/badge.svg)](https://github.com/NHSDigital/data-validation-engine/actions/workflows/ci_testing.yml)
10
+ [![CI Formatting & Linting](https://github.com/NHSDigital/data-validation-engine/actions/workflows/ci_linting.yml/badge.svg)](https://github.com/NHSDigital/data-validation-engine/actions/workflows/ci_linting.yml)
11
+
12
+ The Data Validation Engine (DVE) is a configuration-driven data validation library created and used by NHS England. It lets users define validation rules once and apply them across multiple dataset collections - supporting consistent, accurate data checks.
13
+
14
+ __The DVE offers__:
15
+
16
+ - SQL configuration-based validations
17
+ - Format normalization to Parquet for a unified data representation
18
+ - Data modelling and typecasting
19
+ - Business-rule validations executed on supported backends such as Spark and DuckDB, with the option to add custom backends
20
+ - Validate and enforce referential integrity checks with minimal configuration
21
+ - Deriving new fields and entities
22
+ - Clear validation reporting, including summary insights and record-level error messages
23
+
24
+ As mentioned above, the DVE is "configuration driven" which means the majority of development for you as a user will be building a JSON document to describe how the data will be validated. The JSON document is known as a `dischema` (data ingest schema) file and example files can be accessed [here ↗️](https://github.com/NHSDigital/data-validation-engine/tree/main/tests/testdata). If you'd like to learn more about JSON document and how to build one from scratch, then please read the documentation [here ↗️](https://nhsdigital.github.io/data-validation-engine/).
25
+
26
+ ## Installation and usage
27
+
28
+ Please see the documentation [here ↗️](https://nhsdigital.github.io/data-validation-engine/user_guidance/install/).
29
+
30
+ ## Requesting new features and raising bug reports
31
+ **Before creating new issues, please check to see if the same bug/feature has been created already. Where a duplicate is created, the ticket will be closed and referenced to an existing issue.**
32
+
33
+ If you have spotted a bug with the DVE then please raise an issue [here ↗️](https://github.com/nhsengland/Data-Validation-Engine/issues) using the "bug template".
34
+
35
+ If you have feature request then please follow the same process whilst using the "Feature request template".
36
+
37
+ ## Upcoming features
38
+ Below is a list of features that we would like to implement or have been requested.
39
+ | Feature | Release Version | Released? |
40
+ | ------------------------------------------------------------------------------- | ----------------- | --------- |
41
+ | Open source release | 0.1.0 | Yes |
42
+ | Uplift to Python 3.11 | 0.2.0 | Yes |
43
+ | Uplift Pyspark to 3.5 | 0.8.0 | Yes |
44
+ | Allow DVE to run on Python 3.12+ | 0.8.0 | Yes |
45
+ | Upgrade to Pydantic 2.0 | 0.9.0 | Yes |
46
+ | Upgrade DuckDB to v1.4 | 0.10.0 | Yes |
47
+ | Uplift Pyspark to 4.0+ | TBA | No |
48
+ | Polars upgrade to v1+ | TBA | No |
49
+ | DuckDB upgrade to v1.5+ | TBA | No |
50
+ | Python 3.13 & 3.14 upgrade | TBA | No |
51
+ | Create a more user friendly interface for building and modifying dischema files | TBA | No |
52
+
53
+ If you are interested in getting any of the unreleased features listed above available, then please read the [Contributing](#Contributing) section and then submit us a pull request.
54
+
55
+ ## Contributing
56
+ Please see guidance [here ↗️](https://github.com/NHSDigital/data-validation-engine/blob/main/CONTRIBUTE.md).
57
+
58
+ ## Legal
59
+ This codebase is released under the MIT License. This covers both the codebase and any sample code in the documentation.
60
+
61
+ Any HTML or Markdown documentation is [© Crown copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/) and available under the terms of the [Open Government 3.0 licence](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).
@@ -7,11 +7,11 @@ authors = [
7
7
  ]
8
8
  readme = "README.md"
9
9
  classifiers = [
10
+ "Development Status :: 4 - Beta",
10
11
  "Programming Language :: Python :: 3",
11
- "Programming Language :: Python :: 3.10",
12
12
  "Programming Language :: Python :: 3.11",
13
13
  "Programming Language :: Python :: 3.12",
14
- "Operating System :: OS Independent",
14
+ "Operating System :: POSIX :: Linux",
15
15
  "Topic :: Software Development :: Libraries",
16
16
  "Typing :: Typed",
17
17
  ]
@@ -25,17 +25,17 @@ Issues = "https://github.com/NHSDigital/data-validation-engine/issues"
25
25
  Changelog = "https://github.com/NHSDigital/data-validation-engine/blob/main/CHANGELOG.md"
26
26
 
27
27
  [tool.poetry]
28
- version = "0.8.5"
28
+ version = "0.10.0"
29
29
  packages = [
30
30
  { include = "dve", from = "src" },
31
31
  ]
32
32
 
33
33
  [tool.poetry.dependencies]
34
- python = ">=3.10,<3.13" # breaking changes beyond 3.12
34
+ python = ">=3.11,<3.13" # breaking changes beyond 3.12
35
35
  boto3 = ">=1.34.162,<1.36" # breaking change beyond 1.36
36
36
  botocore = ">=1.34.162,<1.36" # breaking change beyond 1.36
37
37
  delta-spark = ">=3.0.0,<=3.2.0"
38
- duckdb = "1.1.3" # breaking changes beyond 1.1
38
+ duckdb = ">=1.4,<1.4.5"
39
39
  Jinja2 = "3.1.6"
40
40
  lxml = "6.1.1"
41
41
  numpy = "1.26.4"
@@ -43,10 +43,10 @@ openpyxl = "3.1.5"
43
43
  pandas = "2.3.3"
44
44
  polars = "0.20.31"
45
45
  pyarrow = "23.0.1"
46
- pydantic = "1.10.19"
46
+ pydantic = "2.13.4"
47
47
  pyspark = ">=3.5.0,<=3.5.5"
48
- typing_extensions = "4.15.0"
49
- urllib3 = "2.7.0" # dependency of boto3 & botocore
48
+ typing_extensions = "4.16.0"
49
+ urllib3 = "2.8.0" # dependency of boto3 & botocore
50
50
 
51
51
  [tool.poetry.group.dev]
52
52
  optional = true
@@ -59,8 +59,11 @@ include-groups = [
59
59
  commitizen = "4.9.1"
60
60
  pre-commit = "4.3.0"
61
61
  charset-normalizer = "3.4.6"
62
- python-discovery = "1.2.0"
62
+ python-discovery = "1.6.1"
63
63
  idna = "3.15" # dependency of requests
64
+ virtualenv = "21.14.9"
65
+ certifi = "2026.10.23"
66
+ filelock = "4.2.1"
64
67
 
65
68
  [tool.poetry.group.test]
66
69
  optional = true
@@ -71,7 +74,7 @@ coverage = "7.11.0"
71
74
  moto = {extras = ["s3"], version = "4.2.14"}
72
75
  cryptography = "50.0.0" # dependency of `moto`
73
76
  requests = "2.33.0" # dependency of `moto`
74
- Werkzeug = "3.1.6"
77
+ Werkzeug = "3.1.9"
75
78
  pytest = "9.0.3"
76
79
  pytest-lazy-fixtures = "1.4.0" # switched from https://github.com/TvoroG/pytest-lazy-fixture as it's no longer supported
77
80
  xlsx2csv = "0.8.4" # polars requirement
@@ -80,11 +83,12 @@ xlsx2csv = "0.8.4" # polars requirement
80
83
  optional = true
81
84
 
82
85
  [tool.poetry.group.lint.dependencies]
83
- black = "26.3.1"
86
+ black = "26.10.1"
84
87
  astroid = "3.3.9"
85
88
  isort = "5.13.2"
86
89
  pylint = "3.3.9"
87
- mypy = "1.11.2"
90
+ mypy = "1.20.2"
91
+ librt = "0.11.0" # mypy dependency
88
92
  boto3-stubs = {extras = ["essential"], version = "1.26.72"}
89
93
  botocore-stubs = "1.29.72"
90
94
  pandas-stubs = "1.2.0.62"
@@ -107,7 +111,7 @@ mkdocs = "1.6.1"
107
111
  mkdocstrings = { version = "1.0.3", extras = ["python"] }
108
112
  griffelib = "2.0.1"
109
113
  pymdown-extensions = "11.0.1"
110
- zensical = "0.0.46"
114
+ zensical = "0.0.63"
111
115
 
112
116
  [tool.ruff]
113
117
  line-length = 100
@@ -291,7 +295,7 @@ redefining-builtins-modules = [
291
295
 
292
296
  [tool.commitizen]
293
297
  name = "cz_conventional_commits"
294
- tag_format = "v$major.$minor.$patch"
298
+ tag_format = "v$major.$minor.${patch}$prerelease"
295
299
  version_scheme = "pep440"
296
300
  version_provider = "poetry"
297
301
  update_changelog_on_bump = true
@@ -5,7 +5,7 @@ import json
5
5
  import logging
6
6
  from collections.abc import Iterable
7
7
  from itertools import chain
8
- from multiprocessing import Queue
8
+ from queue import Queue
9
9
  from threading import Thread
10
10
  from typing import Optional, Union
11
11
 
@@ -15,7 +15,7 @@ from queue import Queue as ThreadQueue
15
15
  from types import TracebackType
16
16
  from typing import Any, ClassVar, Generic, Optional, TypeVar, Union
17
17
 
18
- from pydantic import ValidationError, validate_arguments
18
+ from pydantic import ValidationError, validate_call
19
19
  from typing_extensions import Literal, get_origin
20
20
 
21
21
  from dve.core_engine.models import (
@@ -99,8 +99,8 @@ class BaseAuditor(Generic[AuditReturnType]):
99
99
  def schema(self) -> dict[str, type]:
100
100
  """Determine python schema of auditor"""
101
101
  return {
102
- fld: str if get_origin(mdl.type_) == Literal else mdl.type_
103
- for fld, mdl in self._record_type.__fields__.items()
102
+ fld: str if get_origin(mdl.annotation) == Literal else mdl.annotation # type: ignore
103
+ for fld, mdl in self._record_type.model_fields.items()
104
104
  }
105
105
 
106
106
  @staticmethod
@@ -196,7 +196,7 @@ class BaseAuditingManager(
196
196
  """Convert AuditReturnType to iterable of dictionaries"""
197
197
  raise NotImplementedError()
198
198
 
199
- @validate_arguments
199
+ @validate_call
200
200
  def add_processing_records(self, processing_records: list[ProcessingStatusRecord]):
201
201
  """Add an entry to the processing_status auditor."""
202
202
  if self.pool:
@@ -208,7 +208,7 @@ class BaseAuditingManager(
208
208
  records=[dict(rec) for rec in processing_records]
209
209
  )
210
210
 
211
- @validate_arguments
211
+ @validate_call
212
212
  def add_submission_statistics_records(self, sub_stats: list[SubmissionStatisticsRecord]):
213
213
  """Add an entry to the submission statistics auditor."""
214
214
  if self.pool:
@@ -218,7 +218,7 @@ class BaseAuditingManager(
218
218
  )
219
219
  return self._submission_statistics.add_records(records=[dict(rec) for rec in sub_stats])
220
220
 
221
- @validate_arguments
221
+ @validate_call
222
222
  def add_transfer_records(self, transfer_records: list[TransferRecord]):
223
223
  """Add an entry to the transfers auditor"""
224
224
  if self.pool:
@@ -227,7 +227,7 @@ class BaseAuditingManager(
227
227
  )
228
228
  return self._transfers.add_records(records=[dict(rec) for rec in transfer_records])
229
229
 
230
- @validate_arguments
230
+ @validate_call
231
231
  def add_new_submissions(
232
232
  self,
233
233
  submissions: list[SubmissionMetadata],
@@ -250,7 +250,7 @@ class BaseAuditingManager(
250
250
  processing_status="received",
251
251
  job_run_id=job_run_id,
252
252
  **ts_info,
253
- ).dict(),
253
+ ).model_dump(),
254
254
  }
255
255
  processing_status_recs.append(processing_rec)
256
256
  if sub_info:
@@ -443,10 +443,6 @@ class BaseDataContract(Generic[EntityType], ABC):
443
443
  ],
444
444
  )
445
445
 
446
- if contract_metadata.cache_originals:
447
- for entity_name in list(entities):
448
- entities[f"Original{entity_name}"] = entities[entity_name]
449
-
450
446
  return entities, feedback_errors_uri, successful, processing_errors_uri
451
447
 
452
448
  def read_parquet(self, path: URI, **kwargs) -> EntityType:
@@ -8,8 +8,16 @@ from typing import Any, ClassVar, Optional, TypeVar
8
8
  from pydantic import BaseModel
9
9
  from typing_extensions import Protocol
10
10
 
11
- from dve.core_engine.backends.exceptions import MessageBearingError, ReaderLacksEntityTypeSupport
11
+ from dve.core_engine.backends.exceptions import (
12
+ CriticalMessageBearingError,
13
+ MessageBearingError,
14
+ ReaderLacksEntityTypeSupport,
15
+ )
12
16
  from dve.core_engine.backends.types import EntityName, EntityType
17
+ from dve.core_engine.configuration.v1 import (
18
+ AllowedAdditionalReaderChecks,
19
+ _ReaderAdditionalChecksConfig,
20
+ )
13
21
  from dve.core_engine.message import FeedbackMessage
14
22
  from dve.core_engine.type_hints import URI, ArbitraryFunction, WrapDecorator
15
23
  from dve.parser.file_handling.service import open_stream
@@ -65,6 +73,10 @@ class BaseFileReader(ABC):
65
73
  decorated with the '@read_function' decorator, and is used in `read_entity_type`.
66
74
 
67
75
  """
76
+ ft_error_code: Optional[str] = "MalformedFile"
77
+ """Default error code for when a file/submission cannot be parsed succesfully."""
78
+ ft_error_message: Optional[str] = "The resource doesn't seem to be a valid text file"
79
+ """Default error message for when a file/submission cannot be parsed succesfully."""
68
80
 
69
81
  def __init_subclass__(cls, *_, **__) -> None:
70
82
  """When this class is subclassed, create and populate the `__read_methods__`
@@ -109,7 +121,10 @@ class BaseFileReader(ABC):
109
121
  entity_name: EntityName,
110
122
  schema: type[BaseModel],
111
123
  all_model_fields: Optional[set[str]] = None,
112
- ) -> EntityType:
124
+ additional_checks: Optional[
125
+ dict[AllowedAdditionalReaderChecks, _ReaderAdditionalChecksConfig]
126
+ ] = None,
127
+ ):
113
128
  """Read to the specified entity type, if supported.
114
129
 
115
130
  NOTE: Simple types should either be returned as strings (if present) or
@@ -117,21 +132,47 @@ class BaseFileReader(ABC):
117
132
  data contract.
118
133
 
119
134
  """
120
- if entity_name == Iterator[dict[str, Any]]:
121
- return self.read_to_py_iterator(
135
+ additional_checks = additional_checks or {}
136
+
137
+ self.raise_if_not_sensible_file(resource, entity_name)
138
+
139
+ if entity_type == Iterator[dict[str, Any]]:
140
+ entity = self.read_to_py_iterator(
122
141
  resource, entity_name, schema, all_model_fields # type: ignore
123
142
  )
124
143
 
125
- self.raise_if_not_sensible_file(resource, entity_name)
144
+ else:
126
145
 
127
- try:
128
- reader_func = self.__read_methods__[entity_type]
129
- except KeyError as err:
130
- raise ReaderLacksEntityTypeSupport(entity_type=entity_type) from err
146
+ try:
147
+ reader_func = self.__read_methods__[entity_type]
148
+ except KeyError as err:
149
+ raise ReaderLacksEntityTypeSupport(entity_type=entity_type) from err
131
150
 
132
- return reader_func(
133
- self, resource, entity_name, schema, all_model_fields=all_model_fields # type: ignore
134
- )
151
+ entity = reader_func(
152
+ self,
153
+ resource,
154
+ entity_name,
155
+ schema,
156
+ all_model_fields=all_model_fields, # type: ignore
157
+ )
158
+
159
+ if config := additional_checks.get("check_empty"):
160
+ if self.check_entity_empty(entity):
161
+ raise MessageBearingError(
162
+ f"The mandatory entity {entity_name} is empty",
163
+ messages=[
164
+ FeedbackMessage(
165
+ entity=entity_name,
166
+ record=None,
167
+ failure_type="submission",
168
+ error_location=entity_name,
169
+ error_code=config.error_code,
170
+ error_message=config.error_message,
171
+ )
172
+ ],
173
+ )
174
+
175
+ return entity
135
176
 
136
177
  def add_record_index(self, entity: EntityType, **kwargs) -> EntityType:
137
178
  """Add a record index to the entity"""
@@ -141,6 +182,10 @@ class BaseFileReader(ABC):
141
182
  """Drop a record index to the entity"""
142
183
  raise NotImplementedError(f"drop_record_index not implemented in {self.__class__}")
143
184
 
185
+ def check_entity_empty(self, entity: EntityType) -> bool:
186
+ """Determine if the entity supplied is empty"""
187
+ raise NotImplementedError(f"check_entity_empty not implemented in {self.__class__}")
188
+
144
189
  def write_parquet(
145
190
  self,
146
191
  entity: EntityType,
@@ -171,20 +216,22 @@ class BaseFileReader(ABC):
171
216
  return False
172
217
  return True
173
218
 
174
- def raise_if_not_sensible_file(self, resource: URI, entity_name: str):
219
+ def raise_if_not_sensible_file(
220
+ self,
221
+ resource: URI,
222
+ entity_name: str,
223
+ ):
175
224
  """Sense check that the file is a text file. Raise error if doesn't
176
225
  appear to be the case."""
177
226
  if not self._check_likely_text_file(resource):
178
- raise MessageBearingError(
227
+ raise CriticalMessageBearingError(
179
228
  "The submitted file doesn't appear to be text",
180
- messages=[
181
- FeedbackMessage(
182
- entity=entity_name,
183
- record=None,
184
- failure_type="submission",
185
- error_location="Whole File",
186
- error_code="MalformedFile",
187
- error_message="The resource doesn't seem to be a valid text file",
188
- )
189
- ],
229
+ message=FeedbackMessage(
230
+ entity=entity_name,
231
+ record=None,
232
+ failure_type="submission",
233
+ error_location="Whole File",
234
+ error_code=self.ft_error_code,
235
+ error_message=self.ft_error_message,
236
+ ),
190
237
  )