diff --git a/.github/ISSUE_TEMPLATE/issue_template_release.md b/.github/ISSUE_TEMPLATE/issue_template_release.md
index 2c35176e..2f181b98 100644
--- a/.github/ISSUE_TEMPLATE/issue_template_release.md
+++ b/.github/ISSUE_TEMPLATE/issue_template_release.md
@@ -15,6 +15,18 @@ assignees: ''
## Preparation
- [ ] 🐙 Create a `Draft GitHub Release` with the release version number `vx.x.x` as title
- [ ] Merge all open Pull Requests to `develop`
+- [ ] Update the fallback XSD directory:
+ * Update the directory and create a pull request. E.g.:
+ ```bash
+ if python3 scripts/update_fallback_xsd.py; then
+ BRANCHNAME="update-fallback-xsd-$(date -Idate)"
+ git switch -c "$BRANCHNAME"
+ git add open_mastr/resources
+ git commit -m 'Update fallback XSD'
+ git push origin "$BRANCHNAME"
+ fi
+ ```
+ * Check and merge the pull request
- [ ] Run tests locally with `pytest` and apply linting with `pre-commit run -a`
## Create a `release` branch
- [ ] Checkout `develop` and branch with `git checkout -b release-vx.x.x`
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e294a0e7..1d6b2d94 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ and the versioning aims to respect [Semantic Versioning](http://semver.org/spec/
## [v0.xx.x] Unreleased - 202x-xx-xx
### Added
+ - Add script for updating fallback XSD
+ [#766](https://github.com/OpenEnergyPlatform/open-MaStR/pull/766)
- Add `Mastr.generate_data_model` method that downloads the current MaStR documentation and generates SQLAlchemy tables from the XSD definitions; supports `english=True` for English column names
[#718](https://github.com/OpenEnergyPlatform/open-MaStR/pull/718)
- Add `mastr_table_to_db_table` argument to `Mastr.download` to pass a custom database schema
@@ -22,6 +24,8 @@ and the versioning aims to respect [Semantic Versioning](http://semver.org/spec/
### Changed
+ - Change fallback XSD from zipped docs to plain directory with XSD files
+ [#766](https://github.com/OpenEnergyPlatform/open-MaStR/pull/766)
- Fix race condition in parallel bulk XML import that resulted in silent data loss
[#765](https://github.com/OpenEnergyPlatform/open-MaStR/pull/765)
- Add support for reading XSD files from both zipped / unzipped documentation sources
diff --git a/open_mastr/mastr.py b/open_mastr/mastr.py
index 0d3cb781..badc53b2 100644
--- a/open_mastr/mastr.py
+++ b/open_mastr/mastr.py
@@ -45,12 +45,6 @@
# setup logger
log = setup_logger()
-FALLBACK_DOCS_PATH = (
- Path(__file__).parent
- / "resources"
- / "Dokumentation-MaStR-Gesamtdatenexport-20260216-Fallback.zip"
-)
-
class Mastr:
"""`Mastr` is used to download the MaStR database.
@@ -195,12 +189,13 @@ def generate_data_model(
english=english,
)
except Exception as e:
+ fallback_docs_path = _get_fallback_xsd()
log.exception(
f"Encountered {e!r} when downloading or processing MaStR documentation."
- f" Falling back to stored docs at {FALLBACK_DOCS_PATH}"
+ f" Falling back to stored docs at {fallback_docs_path}"
)
return _generate_data_model_from_downloaded_docs(
- zipped_docs_file_path=FALLBACK_DOCS_PATH,
+ zipped_docs_file_path=fallback_docs_path,
data=data,
catalog_value_as_str=catalog_value_as_str,
metadata=metadata,
@@ -519,3 +514,13 @@ def _generate_data_model_from_downloaded_docs(
] = sqlalchemy_model
return mastr_table_to_db_table
+
+
+def _get_fallback_xsd() -> Path:
+ fallback_xsds = list((Path(__file__).parent / "resources").glob("fallback-xsd-*"))
+ if not fallback_xsds:
+ raise RuntimeError("Cannot find any fallback XSD directory")
+ fallback_xsd = fallback_xsds[0]
+ if len(fallback_xsds) > 0:
+ log.warning(f"Found multiple fallback XSD directories. Using {fallback_xsd}")
+ return fallback_xsd
diff --git a/open_mastr/resources/Dokumentation-MaStR-Gesamtdatenexport-20260216-Fallback.zip b/open_mastr/resources/Dokumentation-MaStR-Gesamtdatenexport-20260216-Fallback.zip
deleted file mode 100644
index 0e29b97f..00000000
Binary files a/open_mastr/resources/Dokumentation-MaStR-Gesamtdatenexport-20260216-Fallback.zip and /dev/null differ
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenEegBiomasse.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegBiomasse.xsd
new file mode 100644
index 00000000..d19bade6
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegBiomasse.xsd
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenEegGeothermieGrubengasDruckentspannung.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegGeothermieGrubengasDruckentspannung.xsd
new file mode 100644
index 00000000..f87d4340
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegGeothermieGrubengasDruckentspannung.xsd
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenEegSolar.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegSolar.xsd
new file mode 100644
index 00000000..c385058a
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegSolar.xsd
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenEegSpeicher.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegSpeicher.xsd
new file mode 100644
index 00000000..127c7bd0
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegSpeicher.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenEegWasser.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegWasser.xsd
new file mode 100644
index 00000000..d4afa570
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegWasser.xsd
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenEegWind.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegWind.xsd
new file mode 100644
index 00000000..03740698
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenEegWind.xsd
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenGasSpeicher.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenGasSpeicher.xsd
new file mode 100644
index 00000000..fb1c8208
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenGasSpeicher.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenKwk.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenKwk.xsd
new file mode 100644
index 00000000..a742e46c
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenKwk.xsd
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/AnlagenStromSpeicher.xsd b/open_mastr/resources/fallback-xsd-20260712/AnlagenStromSpeicher.xsd
new file mode 100644
index 00000000..19c1bcc6
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/AnlagenStromSpeicher.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Bilanzierungsgebiete.xsd b/open_mastr/resources/fallback-xsd-20260712/Bilanzierungsgebiete.xsd
new file mode 100644
index 00000000..ae1ad690
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Bilanzierungsgebiete.xsd
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenAenderungNetzbetreiberzuordnungen.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenAenderungNetzbetreiberzuordnungen.xsd
new file mode 100644
index 00000000..6868a114
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenAenderungNetzbetreiberzuordnungen.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenBiomasse.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenBiomasse.xsd
new file mode 100644
index 00000000..2b036de2
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenBiomasse.xsd
@@ -0,0 +1,319 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenGasErzeuger.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenGasErzeuger.xsd
new file mode 100644
index 00000000..e4a4dd2d
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenGasErzeuger.xsd
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenGasSpeicher.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenGasSpeicher.xsd
new file mode 100644
index 00000000..47155031
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenGasSpeicher.xsd
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenGasverbraucher.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenGasverbraucher.xsd
new file mode 100644
index 00000000..ba8087c1
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenGasverbraucher.xsd
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenGenehmigung.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenGenehmigung.xsd
new file mode 100644
index 00000000..9c188338
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenGenehmigung.xsd
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenGeothermieGrubengasDruckentspannung.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenGeothermieGrubengasDruckentspannung.xsd
new file mode 100644
index 00000000..4e1bdf70
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenGeothermieGrubengasDruckentspannung.xsd
@@ -0,0 +1,265 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenKernkraft.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenKernkraft.xsd
new file mode 100644
index 00000000..4eeb54c8
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenKernkraft.xsd
@@ -0,0 +1,252 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenSolar.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenSolar.xsd
new file mode 100644
index 00000000..b83a5632
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenSolar.xsd
@@ -0,0 +1,421 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenStromSpeicher.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenStromSpeicher.xsd
new file mode 100644
index 00000000..aea10fb2
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenStromSpeicher.xsd
@@ -0,0 +1,365 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenStromVerbraucher.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenStromVerbraucher.xsd
new file mode 100644
index 00000000..b62384d2
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenStromVerbraucher.xsd
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenVerbrennung.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenVerbrennung.xsd
new file mode 100644
index 00000000..4017c71a
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenVerbrennung.xsd
@@ -0,0 +1,449 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenWasser.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenWasser.xsd
new file mode 100644
index 00000000..d6c17d78
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenWasser.xsd
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/EinheitenWind.xsd b/open_mastr/resources/fallback-xsd-20260712/EinheitenWind.xsd
new file mode 100644
index 00000000..cf0f70f9
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/EinheitenWind.xsd
@@ -0,0 +1,437 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Einheitentypen.xsd b/open_mastr/resources/fallback-xsd-20260712/Einheitentypen.xsd
new file mode 100644
index 00000000..4f07c149
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Einheitentypen.xsd
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Ertuechtigungen.xsd b/open_mastr/resources/fallback-xsd-20260712/Ertuechtigungen.xsd
new file mode 100644
index 00000000..1af9c119
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Ertuechtigungen.xsd
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/GeloeschteUndDeaktivierteEinheiten.xsd b/open_mastr/resources/fallback-xsd-20260712/GeloeschteUndDeaktivierteEinheiten.xsd
new file mode 100644
index 00000000..13a33132
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/GeloeschteUndDeaktivierteEinheiten.xsd
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/GeloeschteUndDeaktivierteMarktakteure.xsd b/open_mastr/resources/fallback-xsd-20260712/GeloeschteUndDeaktivierteMarktakteure.xsd
new file mode 100644
index 00000000..5e91ce2d
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/GeloeschteUndDeaktivierteMarktakteure.xsd
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Katalogkategorien.xsd b/open_mastr/resources/fallback-xsd-20260712/Katalogkategorien.xsd
new file mode 100644
index 00000000..5166806d
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Katalogkategorien.xsd
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Katalogwerte.xsd b/open_mastr/resources/fallback-xsd-20260712/Katalogwerte.xsd
new file mode 100644
index 00000000..1be2b756
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Katalogwerte.xsd
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Lokationen.xsd b/open_mastr/resources/fallback-xsd-20260712/Lokationen.xsd
new file mode 100644
index 00000000..1119a04f
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Lokationen.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Lokationstypen.xsd b/open_mastr/resources/fallback-xsd-20260712/Lokationstypen.xsd
new file mode 100644
index 00000000..297b103c
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Lokationstypen.xsd
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Marktakteure.xsd b/open_mastr/resources/fallback-xsd-20260712/Marktakteure.xsd
new file mode 100644
index 00000000..63a63462
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Marktakteure.xsd
@@ -0,0 +1,1166 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/MarktakteureUndRollen.xsd b/open_mastr/resources/fallback-xsd-20260712/MarktakteureUndRollen.xsd
new file mode 100644
index 00000000..41ae9828
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/MarktakteureUndRollen.xsd
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Marktfunktionen.xsd b/open_mastr/resources/fallback-xsd-20260712/Marktfunktionen.xsd
new file mode 100644
index 00000000..8ecebc59
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Marktfunktionen.xsd
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Marktrollen.xsd b/open_mastr/resources/fallback-xsd-20260712/Marktrollen.xsd
new file mode 100644
index 00000000..1d1bfd17
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Marktrollen.xsd
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Netzanschlusspunkte.xsd b/open_mastr/resources/fallback-xsd-20260712/Netzanschlusspunkte.xsd
new file mode 100644
index 00000000..edbc98ba
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Netzanschlusspunkte.xsd
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/open_mastr/resources/fallback-xsd-20260712/Netze.xsd b/open_mastr/resources/fallback-xsd-20260712/Netze.xsd
new file mode 100644
index 00000000..773331b4
--- /dev/null
+++ b/open_mastr/resources/fallback-xsd-20260712/Netze.xsd
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/scripts/update_fallback_xsd.py b/scripts/update_fallback_xsd.py
new file mode 100644
index 00000000..b1d229eb
--- /dev/null
+++ b/scripts/update_fallback_xsd.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+
+
+from __future__ import annotations
+
+from datetime import datetime
+from pathlib import Path
+from typing import Any, Optional
+from zoneinfo import ZoneInfo
+import logging
+import os
+import shutil
+import sys
+import tempfile
+
+from open_mastr.mastr import _get_fallback_xsd
+from open_mastr.utils.xsd_tables import _iterate_xsd_files
+from open_mastr.xml_download.utils_download_bulk import download_documentation
+
+
+def update_fallback_xsd() -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ new_fallback_xsd = _make_new_fallback_xsd(Path(tmpdir))
+
+ current_fallback_xsd = _get_fallback_xsd()
+ logging.info(
+ f"Comparing old {current_fallback_xsd} with new {new_fallback_xsd}"
+ )
+ diff = _directory_diff(current_fallback_xsd, new_fallback_xsd)
+ if not diff:
+ logging.info("No difference between current and new XSD. Not updating.")
+ sys.exit(1)
+
+ logging.info(
+ "New fallback XSD is different. Replacing current fallback XSD."
+ f"\n Diff: {diff}"
+ )
+
+ resources_dir = Path(__file__).parent.parent / "open_mastr/resources/"
+ for old_dir in resources_dir.glob("fallback-xsd-*"):
+ logging.info(f"Removing {old_dir}")
+ shutil.rmtree(old_dir)
+
+ new_fallback_xsd_in_resources = resources_dir / new_fallback_xsd.name
+ logging.info(f"Creating {new_fallback_xsd_in_resources}")
+ shutil.copytree(new_fallback_xsd, new_fallback_xsd_in_resources)
+
+
+def _make_new_fallback_xsd(top_dir: Path) -> Path:
+ today = datetime.now(tz=ZoneInfo("Europe/Berlin")).date()
+ today_str = today.strftime("%Y%m%d")
+
+ new_fallback_dir = top_dir / f"fallback-xsd-{today_str}"
+ logging.info("Making new fallback XSD in {new_fallback_dir}")
+ new_fallback_dir.mkdir()
+
+ zipped_docs_path = top_dir / "docs.zip"
+ logging.info(f"Downloading docs to {zipped_docs_path}")
+ download_documentation(zipped_docs_path, bulk_date_string=today_str)
+ logging.info(f"Copying XSD files to {new_fallback_dir}")
+ for filename, xsd_file in _iterate_xsd_files(zipped_docs_path):
+ with xsd_file:
+ with open(new_fallback_dir / filename, "wb") as new_file:
+ new_file.write(xsd_file.read())
+ return new_fallback_dir
+
+
+def _directory_diff(
+ left: str | os.PathLike[str], right: str | os.PathLike[str]
+) -> Optional[dict[str, list[Any]]]:
+ """
+ Recursively compare filenames, entry types, and file contents.
+
+ Returns:
+ {
+ "only_in_left": ["path/to/file", ...],
+ "only_in_right": ["path/to/file", ...],
+ "different": [
+ {"path": "file.txt", "reason": "content"},
+ {"path": "item", "reason": "type", "left": "file", "right": "directory"},
+ ...
+ ],
+ }
+ """
+ left = Path(left)
+ right = Path(right)
+
+ if not left.is_dir() or not right.is_dir():
+ raise ValueError("Both paths must be existing directories")
+
+ def entries(root: Path) -> dict[str, tuple[str, Path]]:
+ result: dict[str, tuple[str, Path]] = {}
+
+ for current, dirs, files in os.walk(root, followlinks=False):
+ current_path = Path(current)
+
+ # Treat symlinked directories as symlinks, rather than traversing them.
+ for name in dirs[:]:
+ path = current_path / name
+ relative = str(path.relative_to(root))
+ if path.is_symlink():
+ result[relative] = ("symlink", path)
+ dirs.remove(name)
+ else:
+ result[relative] = ("directory", path)
+
+ for name in files:
+ path = current_path / name
+ relative = str(path.relative_to(root))
+ result[relative] = (
+ "symlink" if path.is_symlink() else "file",
+ path,
+ )
+
+ return result
+
+ def files_equal(first: Path, second: Path, chunk_size: int = 1024 * 1024) -> bool:
+ if first.stat().st_size != second.stat().st_size:
+ return False
+
+ with first.open("rb") as first_file, second.open("rb") as second_file:
+ while True:
+ first_chunk = first_file.read(chunk_size)
+ second_chunk = second_file.read(chunk_size)
+ if first_chunk != second_chunk:
+ return False
+ if not first_chunk:
+ return True
+
+ left_entries = entries(left)
+ right_entries = entries(right)
+
+ only_in_left = sorted(left_entries.keys() - right_entries.keys())
+ only_in_right = sorted(right_entries.keys() - left_entries.keys())
+ different = []
+
+ for relative in sorted(left_entries.keys() & right_entries.keys()):
+ left_type, left_path = left_entries[relative]
+ right_type, right_path = right_entries[relative]
+
+ if left_type != right_type:
+ different.append(
+ {
+ "path": relative,
+ "reason": "type",
+ "left": left_type,
+ "right": right_type,
+ }
+ )
+ elif left_type == "file" and not files_equal(left_path, right_path):
+ different.append({"path": relative, "reason": "content"})
+ elif left_type == "symlink" and os.readlink(left_path) != os.readlink(
+ right_path
+ ):
+ different.append({"path": relative, "reason": "symlink_target"})
+
+ if not only_in_left and not only_in_right and not different:
+ return None
+
+ return {
+ "only_in_left": only_in_left,
+ "only_in_right": only_in_right,
+ "different": different,
+ }
+
+
+if __name__ == "__main__":
+ update_fallback_xsd()
diff --git a/tests/conftest.py b/tests/conftest.py
index ec2856c2..65aa5e75 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,11 +1,12 @@
import shutil
from datetime import date
from pathlib import Path
+from zipfile import ZipFile
import pytest
from open_mastr import Mastr
-from open_mastr.mastr import FALLBACK_DOCS_PATH
+from open_mastr.mastr import _get_fallback_xsd
MOCKUP_XML_ZIP = Path(__file__).parent / "data" / "Gesamtdatenexport_mockup.zip"
NUMBER_ROWS_IN_MOCK_XML_FILES = 100
@@ -42,12 +43,19 @@ def mockup_xml_zip_in_output_dir(output_dir: Path) -> Path:
@pytest.fixture
def mockup_docs_zip_in_output_dir(output_dir: Path) -> Path:
- """Copy the mockup docs zip into the output directory and return its path."""
+ """Create a mockup docs zip in the output directory and return its path."""
docs_dir = output_dir / "data" / "docs_download"
docs_dir.mkdir(parents=True, exist_ok=True)
- dest = (
+ mockup_docs_zip_path = (
docs_dir
/ f"Dokumentation MaStR Gesamtdatenexport_{date.today().strftime('%Y%m%d')}.zip"
)
- shutil.copy(FALLBACK_DOCS_PATH, dest)
- return dest
+
+ fallback_xsd_dir = _get_fallback_xsd()
+ with ZipFile(mockup_docs_zip_path, "w") as mockup_zip:
+ for file in fallback_xsd_dir.glob("*.xsd"):
+ mockup_zip.write(
+ file, f"xsd/{file.relative_to(fallback_xsd_dir).as_posix()}"
+ )
+
+ return mockup_docs_zip_path