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