diff --git a/.github/workflows/github-pages-deployment.yml b/.github/workflows/github-pages-deployment.yml index 7aa5d58..eca8530 100644 --- a/.github/workflows/github-pages-deployment.yml +++ b/.github/workflows/github-pages-deployment.yml @@ -1,29 +1,37 @@ -name: GitHub Pages Deployment +name: Deployment to GitHub Pages on: - push: - branches: - main* + push: + branches: + - main* permissions: - id-token: write - pages: write + id-token: write + pages: write jobs: - # ---------------------------------------------------------------- # - # | Building and deployment of Sphinx build | # - # ---------------------------------------------------------------- # - docs: + # Build the Sphinx documentation + build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 - - name: Install Dependencies - run: | - pip install sphinx piccolo-theme myst_parser - - name: Sphinx Build - run: | - sphinx-build ./sphinx-docs/ _build - - name: Upload GitHub Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - path: _build - - name: Push artifact to pages - uses: actions/deploy-pages@v3.0.1 \ No newline at end of file + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - name: Install Dependencies + run: | + pip install sphinx piccolo-theme myst_parser + - name: Sphinx Build + run: | + sphinx-build ./sphinx-docs/ build_outputs_folder + - name: Upload GitHub Pages artifact + uses: actions/upload-pages-artifact@v3 + id: deployment + with: + path: build_outputs_folder/ + # Deploy the Sphinx documentation to GitHub Pages + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/README.md b/README.md index 48c7d97..ac535cc 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ SPDX-License-Identifier: MPL-2.0 [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=alliander-opensource_weather-provider-api&metric=bugs)](https://sonarcloud.io/dashboard?id=alliander-opensource_Weather-Provider-API) +> :warning: **Due to changes in the way that CDS delivers data, ERA5 data does not currently process sea level data +> for the time being. This will be fixed in a future release.** + + # Weather Provider Library and API This API is intended to help you fetch weather data from different data sources in an efficient and uniform way. diff --git a/poetry.lock b/poetry.lock index bb30f8a..d89f025 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "accept-types" @@ -6,8 +6,6 @@ version = "0.4.1" description = "Determine the best content to send in an HTTP response" optional = false python-versions = "*" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "accept-types-0.4.1.tar.gz", hash = "sha256:fb27099716d8f0360408c8ca86d69dbfed44455834b70d1506250abe521b535a"}, {file = "accept_types-0.4.1-py3-none-any.whl", hash = "sha256:c87feccdffb66b02f9343ff387d7fd5c451ccb2e1221fbd37ea0cedef5cf290f"}, @@ -19,8 +17,6 @@ version = "1.0.0" description = "A light, configurable Sphinx theme" optional = false python-versions = ">=3.10" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, @@ -32,8 +28,6 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -45,8 +39,6 @@ version = "4.8.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, @@ -69,8 +61,6 @@ version = "3.3.8" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, @@ -85,8 +75,6 @@ version = "25.1.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, @@ -102,35 +90,32 @@ tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "babel" -version = "2.16.0" +version = "2.17.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ - {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, - {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, + {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, + {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, ] [package.extras] -dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"] [[package]] name = "beautifulsoup4" -version = "4.12.3" +version = "4.13.3" description = "Screen-scraping library" optional = false -python-versions = ">=3.6.0" -groups = ["main"] -markers = "python_version <= \"3.11\"" +python-versions = ">=3.7.0" files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, + {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, + {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, ] [package.dependencies] soupsieve = ">1.2" +typing-extensions = ">=4.0.0" [package.extras] cchardet = ["cchardet"] @@ -145,8 +130,6 @@ version = "0.7.5" description = "Climate Data Store API" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "cdsapi-0.7.5-py2.py3-none-any.whl", hash = "sha256:8586b837aea89ceeae379b388fbb0ace0a19b94b221f731c65632417007f69fb"}, {file = "cdsapi-0.7.5.tar.gz", hash = "sha256:55221c573b8cefe83cc0bfe01a3d31213c82bf9acce70455350dd24b8095c23a"}, @@ -159,15 +142,13 @@ tqdm = "*" [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" -groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] @@ -176,8 +157,6 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -257,8 +236,6 @@ version = "0.9.15.0" description = "Python interface to map GRIB files to the NetCDF Common Data Model following the CF Convention using ecCodes." optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "cfgrib-0.9.15.0-py3-none-any.whl", hash = "sha256:469cfd25dc173863795e596263b3b6b5ea1402b1715f2b7b1d4b995b40b32c18"}, {file = "cfgrib-0.9.15.0.tar.gz", hash = "sha256:d455034e19b9560a75d008ba9d09b2d4e65762adfb2e911f28b841f4b9c6b47f"}, @@ -280,8 +257,6 @@ version = "1.6.4.post1" description = "Time-handling functionality from netcdf4-python" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "cftime-1.6.4.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0baa9bc4850929da9f92c25329aa1f651e2d6f23e237504f337ee9e12a769f5d"}, {file = "cftime-1.6.4.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bb6b087f4b2513c37670bccd457e2a666ca489c5f2aad6e2c0e94604dc1b5b9"}, @@ -330,8 +305,6 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -433,8 +406,6 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -449,8 +420,6 @@ version = "3.1.1" description = "Pickler class to extend the standard pickle.Pickler functionality" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e"}, {file = "cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64"}, @@ -462,12 +431,10 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "(platform_system == \"Windows\" or sys_platform == \"win32\") and python_version <= \"3.11\"", dev = "python_version <= \"3.11\" and sys_platform == \"win32\""} [[package]] name = "coverage" @@ -475,8 +442,6 @@ version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, @@ -554,8 +519,6 @@ version = "2025.1.0" description = "Parallel PyData with Task Scheduling" optional = false python-versions = ">=3.10" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "dask-2025.1.0-py3-none-any.whl", hash = "sha256:db86220c8d19bdf464cbe11a87a2c8f5d537acf586bb02eed6d61a302af5c2fd"}, {file = "dask-2025.1.0.tar.gz", hash = "sha256:bb807586ff20f0f59f3d36fe34eb4a95f75a1aae2a775b521de6dd53727d2063"}, @@ -585,8 +548,6 @@ version = "0.2.0" description = "ESEE Data Stores API Python Client" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "datapi-0.2.0-py3-none-any.whl", hash = "sha256:867c9ac1ae91965c4223236a2927a819bc0fc1e987490e10468db577936e1f7c"}, {file = "datapi-0.2.0.tar.gz", hash = "sha256:9346d1ce8bbada920da2d4fe0c932d8e7b36c8dd8bc7851862e87712cfa32fda"}, @@ -607,8 +568,6 @@ version = "1.2.18" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, @@ -626,8 +585,6 @@ version = "0.3.9" description = "serialize all of Python" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, @@ -643,8 +600,6 @@ version = "2.7.0" description = "DNS toolkit" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, @@ -665,8 +620,6 @@ version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, @@ -678,8 +631,6 @@ version = "1.7.1" description = "Python interface to the ecCodes GRIB and BUFR decoder/encoder" optional = false python-versions = "*" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eccodes-1.7.1-py3-none-any.whl", hash = "sha256:feef7a17e8c2535e41460de704c03ab1d14f013ffbac233a88dda937672b1173"}, {file = "eccodes-1.7.1.tar.gz", hash = "sha256:d3c7e9bab779d35b624cfd7b3331de111602cba6a6f6368efcc12407f30b2697"}, @@ -697,8 +648,6 @@ version = "0.6.3" description = "Wraps ECMWF tools (experimental)" optional = false python-versions = "*" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "ecmwflibs-0.6.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:8afef3352805f0c25f5b8d99d28cb70c2e05ee1cb7f8c6905d77f32fcebdb2a9"}, {file = "ecmwflibs-0.6.3-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:34df32f27c379423075d37d420872a6cf3f07a4ef56a84116f0280b1ba3e8fb4"}, @@ -730,8 +679,6 @@ version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, @@ -747,8 +694,6 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] -markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -763,8 +708,6 @@ version = "0.115.8" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf"}, {file = "fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9"}, @@ -785,23 +728,19 @@ version = "0.0.5" description = "A packages to search for shared libraries on various platforms" optional = false python-versions = "*" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "findlibs-0.0.5.tar.gz", hash = "sha256:7a801571e999d0ee83f9b92cbb598c21f861ee26ca9dba74cea8958ba4335e7e"}, ] [[package]] name = "fsspec" -version = "2024.12.0" +version = "2025.2.0" description = "File-system specification" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ - {file = "fsspec-2024.12.0-py3-none-any.whl", hash = "sha256:b520aed47ad9804237ff878b504267a3b0b441e97508bd6d2d8774e3db85cee2"}, - {file = "fsspec-2024.12.0.tar.gz", hash = "sha256:670700c977ed2fb51e0d9f9253177ed20cbde4a3e5c0283cc5385b5870c8533f"}, + {file = "fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b"}, + {file = "fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd"}, ] [package.extras] @@ -828,7 +767,7 @@ sftp = ["paramiko"] smb = ["smbprotocol"] ssh = ["paramiko"] test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] -test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] tqdm = ["tqdm"] @@ -838,8 +777,6 @@ version = "2.0" description = "The geodesic routines from GeographicLib" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "geographiclib-2.0-py3-none-any.whl", hash = "sha256:6b7225248e45ff7edcee32becc4e0a1504c606ac5ee163a5656d482e0cd38734"}, {file = "geographiclib-2.0.tar.gz", hash = "sha256:f7f41c85dc3e1c2d3d935ec86660dc3b2c848c83e17f9a9e51ba9d5146a15859"}, @@ -851,8 +788,6 @@ version = "2.4.1" description = "Python Geocoding Toolbox" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "geopy-2.4.1-py3-none-any.whl", hash = "sha256:ae8b4bc5c1131820f4d75fce9d4aaaca0c85189b3aa5d64c3dcaf5e3b7b882a7"}, {file = "geopy-2.4.1.tar.gz", hash = "sha256:50283d8e7ad07d89be5cb027338c6365a32044df3ae2556ad3f52f4840b3d0d1"}, @@ -876,8 +811,6 @@ version = "23.0.0" description = "WSGI HTTP Server for UNIX" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, @@ -899,8 +832,6 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -912,8 +843,6 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -928,8 +857,6 @@ version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -941,8 +868,6 @@ version = "8.6.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, @@ -966,8 +891,6 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -979,8 +902,6 @@ version = "6.0.0" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.9.0" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "isort-6.0.0-py3-none-any.whl", hash = "sha256:567954102bb47bb12e0fae62606570faacddd441e45683968c8d1734fb1af892"}, {file = "isort-6.0.0.tar.gz", hash = "sha256:75d9d8a1438a9432a7d7b54f2d3b45cad9a4a0fdba43617d9873379704a8bdf1"}, @@ -996,8 +917,6 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -1015,8 +934,6 @@ version = "4.0.1" description = "Rate limiting utilities" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "limits-4.0.1-py3-none-any.whl", hash = "sha256:67667e669f570cf7be4e2c2bc52f763b3f93bdf66ea945584360bc1a3f251901"}, {file = "limits-4.0.1.tar.gz", hash = "sha256:a54f5c058dfc965319ae3ee78faf222294659e371b46d22cd7456761f7e46d5a"}, @@ -1045,8 +962,6 @@ version = "1.0.0" description = "File-based locks for Python on Linux and Windows" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3"}, {file = "locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632"}, @@ -1058,8 +973,6 @@ version = "0.7.3" description = "Python logging made (stupidly) simple" optional = false python-versions = "<4.0,>=3.5" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, @@ -1078,8 +991,6 @@ version = "5.3.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, @@ -1234,8 +1145,6 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -1260,8 +1169,6 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1332,8 +1239,6 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -1345,8 +1250,6 @@ version = "0.4.2" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, @@ -1366,8 +1269,6 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -1379,8 +1280,6 @@ version = "0.3.3" description = "A package to download several URL as one, as well as supporting multi-part URLs" optional = false python-versions = "*" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "multiurl-0.3.3.tar.gz", hash = "sha256:f4d0b69dcf4a0ed740daa313dbcd4d5665420d305c50ca879285e96dc828093f"}, ] @@ -1397,8 +1296,6 @@ version = "4.0.0" description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," optional = false python-versions = ">=3.10" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d"}, {file = "myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531"}, @@ -1425,8 +1322,6 @@ version = "1.7.2" description = "Provides an object-oriented python interface to the netCDF version 4 library" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "netCDF4-1.7.2-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:5e9b485e3bd9294d25ff7dc9addefce42b3d23c1ee7e3627605277d159819392"}, {file = "netCDF4-1.7.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:118b476fd00d7e3ab9aa7771186d547da645ae3b49c0c7bdab866793ebf22f07"}, @@ -1474,8 +1369,6 @@ version = "2.2.2" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"}, {file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"}, @@ -1540,8 +1433,6 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1553,8 +1444,6 @@ version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, @@ -1640,8 +1529,6 @@ version = "1.4.2" description = "Appendable key-value storage" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f"}, {file = "partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c"}, @@ -1660,8 +1547,6 @@ version = "0.23.0" description = "A modern Sphinx theme." optional = false python-versions = ">=3.6.0" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "piccolo_theme-0.23.0-py3-none-any.whl", hash = "sha256:26ade303009a78c3f0c3e2430ac71931c07fa49fd9097daa9d1c8e9b7135652a"}, {file = "piccolo_theme-0.23.0.tar.gz", hash = "sha256:8e59ca64adb0b8433e9fedd8e7d53af8bc90251054c10027d0d6ab00694d771d"}, @@ -1676,8 +1561,6 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1694,8 +1577,6 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1711,8 +1592,6 @@ version = "0.21.1" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, @@ -1727,8 +1606,6 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1740,8 +1617,6 @@ version = "2.10.6" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, @@ -1762,8 +1637,6 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -1876,8 +1749,6 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -1892,8 +1763,6 @@ version = "3.3.4" description = "python code static checker" optional = false python-versions = ">=3.9.0" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pylint-3.3.4-py3-none-any.whl", hash = "sha256:289e6a1eb27b453b08436478391a48cd53bb0efb824873f949e709350f3de018"}, {file = "pylint-3.3.4.tar.gz", hash = "sha256:74ae7a38b177e69a9b525d0794bd8183820bfa7eb68cc1bee6e8ed22a42be4ce"}, @@ -1922,8 +1791,6 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -1946,8 +1813,6 @@ version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, @@ -1966,8 +1831,6 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -1978,15 +1841,13 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2024.2" +version = "2025.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] [[package]] @@ -1995,8 +1856,6 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2059,8 +1918,6 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2082,8 +1939,6 @@ version = "0.4.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, @@ -2110,8 +1965,6 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2123,8 +1976,6 @@ version = "0.1.9" description = "A rate limiting extension for Starlette and Fastapi" optional = false python-versions = ">=3.7,<4.0" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "slowapi-0.1.9-py3-none-any.whl", hash = "sha256:cfad116cfb84ad9d763ee155c1e5c5cbf00b0d47399a769b227865f5df576e36"}, {file = "slowapi-0.1.9.tar.gz", hash = "sha256:639192d0f1ca01b1c6d95bf6c71d794c3a9ee189855337b4821f7f457dddad77"}, @@ -2142,8 +1993,6 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -2155,8 +2004,6 @@ version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, @@ -2168,8 +2015,6 @@ version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, @@ -2181,8 +2026,6 @@ version = "8.1.3" description = "Python documentation generator" optional = false python-versions = ">=3.10" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, @@ -2218,8 +2061,6 @@ version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, @@ -2236,8 +2077,6 @@ version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, @@ -2254,8 +2093,6 @@ version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, @@ -2272,8 +2109,6 @@ version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -2288,8 +2123,6 @@ version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, @@ -2306,8 +2139,6 @@ version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, @@ -2324,8 +2155,6 @@ version = "0.45.3" description = "The little ASGI library that shines." optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d"}, {file = "starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f"}, @@ -2343,8 +2172,6 @@ version = "0.10.0" description = "Prometheus integration for Starlette" optional = false python-versions = "<4.0,>=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "starlette_prometheus-0.10.0-py3-none-any.whl", hash = "sha256:8b2f997495f809f01387378dad5476b2a1cf2fb2d9b96164394effdecb2d8380"}, {file = "starlette_prometheus-0.10.0.tar.gz", hash = "sha256:77d1e9cfad572206e8de242975bd00a62e33685eb330389f2add04e4d719d47d"}, @@ -2360,8 +2187,6 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -2403,8 +2228,6 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -2416,8 +2239,6 @@ version = "1.0.0" description = "List processing tools and functional utilities" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236"}, {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"}, @@ -2429,8 +2250,6 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -2452,12 +2271,10 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -markers = {main = "python_version <= \"3.11\"", dev = "python_version < \"3.11\""} [[package]] name = "tzdata" @@ -2465,8 +2282,6 @@ version = "2025.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, @@ -2478,8 +2293,6 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -2497,8 +2310,6 @@ version = "0.34.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, @@ -2518,8 +2329,6 @@ version = "1.2.0" description = "A small Python utility to set file creation time on Windows" optional = false python-versions = ">=3.5" -groups = ["main"] -markers = "python_version <= \"3.11\" and sys_platform == \"win32\"" files = [ {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, @@ -2534,8 +2343,6 @@ version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, @@ -2620,15 +2427,13 @@ files = [ [[package]] name = "xarray" -version = "2025.1.1" +version = "2025.1.2" description = "N-D labeled arrays and datasets in Python" optional = false python-versions = ">=3.10" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ - {file = "xarray-2025.1.1-py3-none-any.whl", hash = "sha256:8a69d17c1e4ad09664fd0bc2dbb398e7368eda25bd19456fb919a6eb6490fb72"}, - {file = "xarray-2025.1.1.tar.gz", hash = "sha256:1a3011d00ca92a94ba31b297c2eccd310b87a7dacf5acc8d0468385d4a834342"}, + {file = "xarray-2025.1.2-py3-none-any.whl", hash = "sha256:a7ad6a36c6e0becd67f8aff6a7808d20e4bdcd344debb5205f0a34b1a4a7f8d6"}, + {file = "xarray-2025.1.2.tar.gz", hash = "sha256:e7675c79ac69d274dd3b3c5450ce57176928d2792947576251ed1c7df1783224"}, ] [package.dependencies] @@ -2651,8 +2456,6 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, @@ -2667,6 +2470,6 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", type = ["pytest-mypy"] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = ">=3.10,<3.12" content-hash = "137b6fe75308c8c54cb504610eb8a2b7ab9fa61e10fd54d8824c26f92862b24a" diff --git a/pyproject.toml b/pyproject.toml index f3d00a8..fd0983e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "weather_provider_api" -version = "2.63.0" +version = "2.66.0" description = "Weather Provider Libraries and API" authors = ["Verbindingsteam", "Raoul Linnenbank <58594297+rflinnenbank@users.noreply.github.com>"] license = "MPL-2.0" diff --git a/weather_provider_api/routers/weather/controller.py b/weather_provider_api/routers/weather/controller.py index ff50ac7..13bee21 100644 --- a/weather_provider_api/routers/weather/controller.py +++ b/weather_provider_api/routers/weather/controller.py @@ -40,10 +40,10 @@ def get_weather( source_id: str, model_id: str, fetch_async: bool, - coords: List[List[Tuple[float, float]]], - begin: Optional[datetime.datetime] = None, - end: Optional[datetime.datetime] = None, - factors: List[str] = None, + coords: list[list[tuple[float, float]]], + begin: datetime.datetime | None = None, + end: datetime.datetime | None = None, + factors: list[str] | None = None, ): """Function to use the requested weather model from the requested source to get specific weather factors for a specific time and specific location(s) diff --git a/weather_provider_api/routers/weather/repository/repository.py b/weather_provider_api/routers/weather/repository/repository.py index 45f5f00..4218ec5 100644 --- a/weather_provider_api/routers/weather/repository/repository.py +++ b/weather_provider_api/routers/weather/repository/repository.py @@ -110,7 +110,7 @@ def cleanup(self): self._delete_excess_files() @abstractmethod - def update(self): + def update(self, test_mode: bool = False) -> RepositoryUpdateResult: raise NotImplementedError(NOT_IMPLEMENTED_ERROR) def gather_period(self, begin: datetime, end: datetime, coordinates: List[GeoPosition]) -> xr.Dataset: diff --git a/weather_provider_api/routers/weather/sources/cds/client/cds_api_tools.py b/weather_provider_api/routers/weather/sources/cds/client/cds_api_tools.py index 65ac12a..be9b68c 100644 --- a/weather_provider_api/routers/weather/sources/cds/client/cds_api_tools.py +++ b/weather_provider_api/routers/weather/sources/cds/client/cds_api_tools.py @@ -80,11 +80,11 @@ class CDSRequest(BaseModel): "21:00", "22:00", "23:00", - ] + ], ) data_format: str = "netcdf" download_format: str = "zip" - area: tuple[float, float, float, float] = Field((7.22, 50.75, 3.2, 53.7)) + area: tuple[float, float, float, float] = (53.7, 3.2, 50.75, 7.22) @property def request_parameters(self) -> dict[str, str | list[str] | tuple[float]]: @@ -100,6 +100,7 @@ def request_parameters(self) -> dict[str, str | list[str] | tuple[float]]: "area": self.area, "data_format": self.data_format, "download_format": self.download_format, + "grid_resolution": (0.25, 0.25), } diff --git a/weather_provider_api/routers/weather/sources/cds/client/era5_utils.py b/weather_provider_api/routers/weather/sources/cds/client/era5_utils.py index 1ef4d9c..7966a3e 100644 --- a/weather_provider_api/routers/weather/sources/cds/client/era5_utils.py +++ b/weather_provider_api/routers/weather/sources/cds/client/era5_utils.py @@ -41,7 +41,7 @@ class Era5UpdateSettings(BaseModel): maximum_runtime_in_minutes: int = 2 * 60 # 2 hours -def era5_repository_update(update_settings: Era5UpdateSettings) -> RepositoryUpdateResult: +def era5_repository_update(update_settings: Era5UpdateSettings, test_mode: bool) -> RepositoryUpdateResult: """A function to update a variant of ERA5 data into the repository.""" starting_moment_of_update = datetime.now(UTC) cutoff_time = starting_moment_of_update + relativedelta(minutes=update_settings.maximum_runtime_in_minutes) @@ -54,7 +54,7 @@ def era5_repository_update(update_settings: Era5UpdateSettings) -> RepositoryUpd logger.info(f" - Maximum runtime: {update_settings.maximum_runtime_in_minutes} minutes ({cutoff_time})") try: - _era5_update_month_by_month(update_settings, starting_moment_of_update, cutoff_time) + _era5_update_month_by_month(update_settings, starting_moment_of_update, cutoff_time, test_mode) except Exception as e: logger.error(f"Failed to update ERA5 data. Reason: {e}") return RepositoryUpdateResult.failure @@ -67,7 +67,7 @@ def era5_repository_update(update_settings: Era5UpdateSettings) -> RepositoryUpd def _era5_update_month_by_month( - update_settings: Era5UpdateSettings, starting_moment_of_update: datetime, cutoff_time: datetime + update_settings: Era5UpdateSettings, starting_moment_of_update: datetime, cutoff_time: datetime, test_mode: bool ): """A function to update a variant of ERA5 data into the repository.""" amount_of_months_processed = amount_of_months_not_processable = 0 @@ -90,7 +90,7 @@ def _era5_update_month_by_month( logger.warning("Maximum runtime reached. Stopping update.") break - update_result = _era5_update_month(update_settings, update_month) + update_result = _era5_update_month(update_settings, update_month, test_mode) if update_result == RepositoryUpdateResult.failure: amount_of_months_not_processable += 1 amount_of_months_processed += 1 @@ -111,7 +111,9 @@ def _era5_update_month_by_month( logger.info(f"Average time per month: {average_time_per_month_in_minutes} minutes") -def _era5_update_month(update_settings: Era5UpdateSettings, update_month: datetime) -> RepositoryUpdateResult: +def _era5_update_month( + update_settings: Era5UpdateSettings, update_month: datetime, test_mode: bool +) -> RepositoryUpdateResult: """A function to update a variant of ERA5 data into the repository.""" logger.debug(f" > Processing month: {update_month.year}-{update_month.month}") @@ -123,6 +125,9 @@ def _era5_update_month(update_settings: Era5UpdateSettings, update_month: dateti logger.debug(f" > File {month_file} requires update.") month_file_name = month_file.with_suffix(Era5FileSuffixes.UNFORMATTED) + # Only the first day of each month in test mode, otherwise all days: + day = [str(i) for i in list(range(1, 32))] if not test_mode else ["1"] + try: download_era5_data( update_settings.era5_dataset_to_update_from, @@ -131,9 +136,8 @@ def _era5_update_month(update_settings: Era5UpdateSettings, update_month: dateti variables=update_settings.factors_to_process, year=[str(update_month.year)], month=[str(update_month.month)], - day=[str(i) for i in list(range(1, 32))], + day=day, time=[f"{hour:02d}:00" for hour in range(24)], - area=(53.510403, 3.314971, 50.803721, 7.092053), ), target_location=str(month_file_name), ) @@ -194,7 +198,6 @@ def _verify_first_day_available_for_era5(update_moment: datetime, update_setting month=[str(update_moment.month)], day=[str(update_moment.day)], time=[f"{hour:02d}:00" for hour in range(2)], - area=(53.510403, 3.314971, 50.803721, 7.092053), # The Netherlands area ), target_location=tempfile.NamedTemporaryFile().name, ) @@ -230,7 +233,6 @@ def _finalize_formatted_file(file_path: Path, current_moment: date, verification logger.error(f" > Failed to remove temporary file {file_path.with_suffix(file_suffix)}: {e}") # Rename the file to its proper name: - print("RENAMING FILE", current_moment, verification_date, permanent_month, incomplete_month) if current_moment == verification_date.replace(day=1): # Current month means an incomplete file file_path.with_suffix(Era5FileSuffixes.FORMATTED).rename(file_path.with_suffix(Era5FileSuffixes.INCOMPLETE)) @@ -271,7 +273,8 @@ def file_requires_update(file_path: Path, current_month: date, verification_date return True # An update should both clean the UNFORMATTED file and generate a proper one if not file_path.with_suffix(".nc").exists() or file_path.with_suffix(Era5FileSuffixes.INCOMPLETE).exists(): - logger.debug(" > No file exists, or it is still incomplete: UPDATE REQUIRED ") + logger.debug(" > No file exists, or it is still incomplete: UPDATE REQUIRED") + print("File path: ", file_path) return True # No file matching the mask or incomplete files always mean the update is required! files_in_folder = glob.glob(f"{file_path}*.nc") @@ -343,15 +346,34 @@ def _recombine_multiple_files(unformatted_file: Path) -> None: with zipfile.ZipFile(unformatted_file, "r") as zip_ref: zip_ref.extractall(temp_dir) - # Load the data - - data_stream_land_accum = xr.open_dataset(Path(temp_dir).joinpath("data_stream-oper_stepType-accum.nc")) - data_stream_land_instant = xr.open_dataset(Path(temp_dir).joinpath("data_stream-oper_stepType-instant.nc")) - data_stream_wave_instant = xr.open_dataset(Path(temp_dir).joinpath("data_stream-wave_stepType-instant.nc")) + concatenated_dataset = xr.Dataset() + files_to_load_in_order = [ + "data_stream-oper_stepType-instant", + "data_stream-oper_stepType-accum", + # TODO: Add the following file back in when we can properly handle it + # "data_stream-wave_stepType-instant", # Something about this data doesn't mesh well anymore with the rest... + ] + + # TODO: Load, convert to dataframe, merge, convert back to xarray + concatenated_dataset = xr.Dataset() + for filename in files_to_load_in_order: + file_path = Path(temp_dir).joinpath(f"{filename}.nc") + if not file_path.exists(): + logger.error(f" > Required file {filename}.nc does not exist. Aborting recombination.") + raise FileNotFoundError(f" > Required file {filename}.nc does not exist. Aborting recombination.") + + dataset = xr.open_dataset(file_path) + dataset = dataset.drop("expver", errors="raise") + + if not concatenated_dataset.data_vars: + concatenated_dataset = dataset.copy(deep=True) + else: + concatenated_dataset = xr.merge( + [concatenated_dataset, dataset], join="outer", compat="no_conflicts", combine_attrs="override" + ) - # Merge the data - combined_data = xr.merge([data_stream_land_accum, data_stream_land_instant, data_stream_wave_instant]) - combined_data.to_netcdf(unformatted_file, format="NETCDF4", engine="netcdf4") + concatenated_dataset.to_netcdf(unformatted_file, format="NETCDF4", engine="netcdf4") + # raise ValueError("This is not working yet") def download_era5_data( diff --git a/weather_provider_api/routers/weather/sources/cds/client/era5land_repository.py b/weather_provider_api/routers/weather/sources/cds/client/era5land_repository.py index ca481b2..711ea00 100644 --- a/weather_provider_api/routers/weather/sources/cds/client/era5land_repository.py +++ b/weather_provider_api/routers/weather/sources/cds/client/era5land_repository.py @@ -7,6 +7,7 @@ from pytz import UTC from weather_provider_api.routers.weather.repository.repository import RepositoryUpdateResult, WeatherRepositoryBase +from weather_provider_api.routers.weather.sources.cds.client.cds_api_tools import CDSDataSets from weather_provider_api.routers.weather.sources.cds.client.era5_utils import ( Era5UpdateSettings, era5_repository_update, @@ -52,7 +53,7 @@ def last_day_of_repo(self) -> datetime: last_day_of_repo = last_day_of_repo.replace(hour=0, minute=0, second=0, microsecond=0) return last_day_of_repo - def update(self) -> RepositoryUpdateResult: + def update(self, test_mode: bool = False) -> RepositoryUpdateResult: """The update implementation for the ERA5 Land repository. This function handles all the required actions to update the repository completely, but taking into @@ -69,14 +70,15 @@ def update(self) -> RepositoryUpdateResult: return era5_repository_update( Era5UpdateSettings( filename_prefix=self.file_prefix, - era5_dataset_to_update_from="reanalysis-era5-land", + era5_dataset_to_update_from=CDSDataSets.ERA5LAND, era5_product_type="reanalysis", factor_dictionary=era5land_factors, factors_to_process=[era5land_factors[x] for x in list(era5land_factors.keys())], maximum_runtime_in_minutes=self.runtime_limit, repository_time_range=(self.first_day_of_repo, self.last_day_of_repo), target_storage_location=self.repository_folder, - ) + ), + test_mode=test_mode, ) def _delete_files_outside_of_scope(self): diff --git a/weather_provider_api/routers/weather/sources/cds/client/era5sl_repository.py b/weather_provider_api/routers/weather/sources/cds/client/era5sl_repository.py index 04a6a1a..ef8f6a1 100644 --- a/weather_provider_api/routers/weather/sources/cds/client/era5sl_repository.py +++ b/weather_provider_api/routers/weather/sources/cds/client/era5sl_repository.py @@ -52,7 +52,7 @@ def last_day_of_repo(self) -> datetime: last_day_of_repo = last_day_of_repo.replace(hour=0, minute=0, second=0, microsecond=0) return last_day_of_repo - def update(self) -> RepositoryUpdateResult: + def update(self, test_mode: bool) -> RepositoryUpdateResult: """The update implementation for the ERA5 Single Levels repository. This function handles all the required actions to update the repository completely, but taking into @@ -60,6 +60,9 @@ def update(self) -> RepositoryUpdateResult: the next file wouldn't complete within the runtime_limit, the update process halts. (if no other downloads were made yet, a generous rough estimate is used). + Args: + test_mode: A boolean indicating whether the update process should run in test mode. + Returns: A RepositoryUpdateResult value indicating a completion, time-out or failure of the update process """ @@ -77,7 +80,8 @@ def update(self) -> RepositoryUpdateResult: maximum_runtime_in_minutes=self.runtime_limit, repository_time_range=(self.first_day_of_repo, self.last_day_of_repo), target_storage_location=self.repository_folder, - ) + ), + test_mode=test_mode, ) def _delete_files_outside_of_scope(self): @@ -130,8 +134,11 @@ def _get_file_list_for_period(self, start: datetime, end: datetime): file_month = int(file[len_filename_until_date + 5 : len_filename_until_date + 7]) date_for_filename = datetime(year=file_year, month=file_month, day=15).astimezone(UTC) - - if start.replace(day=1) < date_for_filename < datetime(year=end.year, month=end.month, day=28).astimezone(UTC): + if ( + start.replace(day=1) + < date_for_filename + < datetime(year=end.year, month=end.month, day=28).astimezone(UTC) + ): # If the file is within the requested period, save it to the list of filtered files list_of_filtered_files.append(file) diff --git a/weather_provider_api/routers/weather/utils/serializers.py b/weather_provider_api/routers/weather/utils/serializers.py index 7491072..f33b7d2 100644 --- a/weather_provider_api/routers/weather/utils/serializers.py +++ b/weather_provider_api/routers/weather/utils/serializers.py @@ -26,7 +26,7 @@ def file_or_text_response( model_id: str, request: Union[WeatherContentRequestQuery, WeatherContentRequestMultiLocationQuery], coords: List[Tuple[float, float]], -): +) -> tuple[ScientificJSONResponse | FileResponse, str | None]: if response_format == ResponseFormat.json: return json_response(unserialized_data, coords) elif response_format == ResponseFormat.json_dataset: @@ -41,8 +41,8 @@ def file_response( source_id: str, model_id: str, request: WeatherContentRequestQuery, - coords: List[Tuple[float, float]], -): + coords: list[tuple[float, float]], +) -> tuple[FileResponse, str]: if response_format == ResponseFormat.netcdf4: file_path = to_netcdf4(unserialized_data) mime = "application/x-netcdf4" @@ -64,7 +64,9 @@ def file_response( def generate_filename(source_id: str, model_id: str, request: WeatherContentRequestQuery, extension: str): - file_name = f"weather_{source_id}_{model_id}_{request.begin}-{request.end}{extension}".replace(" ", "T") + file_name = f"weather_{source_id}_{model_id}_{request.begin}-{request.end}{extension}".replace(" ", "T").replace( + ":", "" + ) return file_name diff --git a/weather_provider_api/scripts/update_era5sl_repository.py b/weather_provider_api/scripts/update_era5sl_repository.py index 9db3588..37fffc5 100644 --- a/weather_provider_api/scripts/update_era5sl_repository.py +++ b/weather_provider_api/scripts/update_era5sl_repository.py @@ -1,18 +1,28 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- + # SPDX-FileCopyrightText: 2019-2022 Alliander N.V. # SPDX-License-Identifier: MPL-2.0 +import sys + +from loguru import logger from weather_provider_api.routers.weather.sources.cds.client.era5sl_repository import ( ERA5SLRepository, ) -def main(): +def main(args) -> None: + """Run the update of the ERA5SL repository.""" + test_mode = False + + if len(args) == 2 and args[1] == "testmode": + logger.warning("WARNING: Running in test mode") + test_mode = True + era5sl_repo = ERA5SLRepository() - era5sl_repo.update() + era5sl_repo.update(test_mode) if __name__ == "__main__": - main() + main(sys.argv)