hubeau-data 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. hubeau_data-0.0.1/.github/workflows/ci.yml +31 -0
  2. hubeau_data-0.0.1/.gitignore +58 -0
  3. hubeau_data-0.0.1/.helix/languages.toml +11 -0
  4. hubeau_data-0.0.1/.pre-commit-config.yaml +16 -0
  5. hubeau_data-0.0.1/CHANGELOG.md +48 -0
  6. hubeau_data-0.0.1/CONTRIBUTING.md +52 -0
  7. hubeau_data-0.0.1/LICENCE.md +21 -0
  8. hubeau_data-0.0.1/PKG-INFO +207 -0
  9. hubeau_data-0.0.1/README.md +172 -0
  10. hubeau_data-0.0.1/examples/demo.ipynb +141 -0
  11. hubeau_data-0.0.1/examples/demo.py +80 -0
  12. hubeau_data-0.0.1/pyproject.toml +94 -0
  13. hubeau_data-0.0.1/scripts/__init__.py +0 -0
  14. hubeau_data-0.0.1/scripts/eau_potable/__init__.py +0 -0
  15. hubeau_data-0.0.1/scripts/eau_potable/check_health.py +59 -0
  16. hubeau_data-0.0.1/scripts/ecoulement/__init__.py +0 -0
  17. hubeau_data-0.0.1/scripts/ecoulement/check_health.py +56 -0
  18. hubeau_data-0.0.1/scripts/hydrobiologie/__init__.py +0 -0
  19. hubeau_data-0.0.1/scripts/hydrobiologie/check_health.py +56 -0
  20. hubeau_data-0.0.1/scripts/hydrometrie/__init__.py +0 -0
  21. hubeau_data-0.0.1/scripts/hydrometrie/check_health.py +56 -0
  22. hubeau_data-0.0.1/scripts/hydrometrie/inspect_fields.py +123 -0
  23. hubeau_data-0.0.1/scripts/phytopharmaceutiques/__init__.py +0 -0
  24. hubeau_data-0.0.1/scripts/phytopharmaceutiques/check_health.py +38 -0
  25. hubeau_data-0.0.1/scripts/piezometrie/__init__.py +0 -0
  26. hubeau_data-0.0.1/scripts/piezometrie/check_health.py +59 -0
  27. hubeau_data-0.0.1/scripts/poisson/__init__.py +0 -0
  28. hubeau_data-0.0.1/scripts/poisson/check_health.py +56 -0
  29. hubeau_data-0.0.1/scripts/prelevements/__init__.py +0 -0
  30. hubeau_data-0.0.1/scripts/prelevements/check_health.py +56 -0
  31. hubeau_data-0.0.1/scripts/qualite_nappes/__init__.py +0 -0
  32. hubeau_data-0.0.1/scripts/qualite_nappes/check_health.py +59 -0
  33. hubeau_data-0.0.1/scripts/qualite_rivieres/__init__.py +0 -0
  34. hubeau_data-0.0.1/scripts/qualite_rivieres/analyze_time_series.py +227 -0
  35. hubeau_data-0.0.1/scripts/qualite_rivieres/check_health.py +56 -0
  36. hubeau_data-0.0.1/scripts/qualite_rivieres/check_undocumented_fields.py +93 -0
  37. hubeau_data-0.0.1/scripts/qualite_rivieres/inspect_models.py +112 -0
  38. hubeau_data-0.0.1/scripts/temperature/__init__.py +0 -0
  39. hubeau_data-0.0.1/scripts/temperature/check_health.py +56 -0
  40. hubeau_data-0.0.1/src/hubeau_data/__init__.py +1 -0
  41. hubeau_data-0.0.1/src/hubeau_data/api/__init__.py +0 -0
  42. hubeau_data-0.0.1/src/hubeau_data/api/async_eau_potable.py +31 -0
  43. hubeau_data-0.0.1/src/hubeau_data/api/async_ecoulement.py +42 -0
  44. hubeau_data-0.0.1/src/hubeau_data/api/async_hydrobiologie.py +42 -0
  45. hubeau_data-0.0.1/src/hubeau_data/api/async_hydrometrie.py +51 -0
  46. hubeau_data-0.0.1/src/hubeau_data/api/async_phytopharmaceutiques.py +55 -0
  47. hubeau_data-0.0.1/src/hubeau_data/api/async_piezometrie.py +42 -0
  48. hubeau_data-0.0.1/src/hubeau_data/api/async_poisson.py +53 -0
  49. hubeau_data-0.0.1/src/hubeau_data/api/async_prelevements.py +42 -0
  50. hubeau_data-0.0.1/src/hubeau_data/api/async_qualite_nappes.py +45 -0
  51. hubeau_data-0.0.1/src/hubeau_data/api/async_qualite_rivieres.py +46 -0
  52. hubeau_data-0.0.1/src/hubeau_data/api/async_temperature.py +31 -0
  53. hubeau_data-0.0.1/src/hubeau_data/api/eau_potable.py +140 -0
  54. hubeau_data-0.0.1/src/hubeau_data/api/ecoulement.py +152 -0
  55. hubeau_data-0.0.1/src/hubeau_data/api/hydrobiologie.py +154 -0
  56. hubeau_data-0.0.1/src/hubeau_data/api/hydrometrie.py +176 -0
  57. hubeau_data-0.0.1/src/hubeau_data/api/phytopharmaceutiques.py +147 -0
  58. hubeau_data-0.0.1/src/hubeau_data/api/piezometrie.py +152 -0
  59. hubeau_data-0.0.1/src/hubeau_data/api/poisson.py +164 -0
  60. hubeau_data-0.0.1/src/hubeau_data/api/prelevements.py +152 -0
  61. hubeau_data-0.0.1/src/hubeau_data/api/qualite_nappes.py +154 -0
  62. hubeau_data-0.0.1/src/hubeau_data/api/qualite_rivieres.py +156 -0
  63. hubeau_data-0.0.1/src/hubeau_data/api/temperature.py +140 -0
  64. hubeau_data-0.0.1/src/hubeau_data/async_base.py +33 -0
  65. hubeau_data-0.0.1/src/hubeau_data/async_client.py +53 -0
  66. hubeau_data-0.0.1/src/hubeau_data/base.py +23 -0
  67. hubeau_data-0.0.1/src/hubeau_data/client.py +30 -0
  68. hubeau_data-0.0.1/src/hubeau_data/models/__init__.py +0 -0
  69. hubeau_data-0.0.1/src/hubeau_data/models/eau_potable.py +109 -0
  70. hubeau_data-0.0.1/src/hubeau_data/models/ecoulement.py +152 -0
  71. hubeau_data-0.0.1/src/hubeau_data/models/geojson.py +88 -0
  72. hubeau_data-0.0.1/src/hubeau_data/models/health.py +98 -0
  73. hubeau_data-0.0.1/src/hubeau_data/models/hydrobiologie.py +230 -0
  74. hubeau_data-0.0.1/src/hubeau_data/models/hydrometrie.py +228 -0
  75. hubeau_data-0.0.1/src/hubeau_data/models/phytopharmaceutiques.py +161 -0
  76. hubeau_data-0.0.1/src/hubeau_data/models/piezometrie.py +140 -0
  77. hubeau_data-0.0.1/src/hubeau_data/models/poisson.py +310 -0
  78. hubeau_data-0.0.1/src/hubeau_data/models/prelevements.py +174 -0
  79. hubeau_data-0.0.1/src/hubeau_data/models/qualite_nappes.py +216 -0
  80. hubeau_data-0.0.1/src/hubeau_data/models/qualite_rivieres.py +353 -0
  81. hubeau_data-0.0.1/src/hubeau_data/models/temperature.py +135 -0
  82. hubeau_data-0.0.1/src/hubeau_data/py.typed +0 -0
  83. hubeau_data-0.0.1/src/hubeau_data/retry.py +39 -0
  84. hubeau_data-0.0.1/tests/test_async_apis.py +134 -0
  85. hubeau_data-0.0.1/tests/test_async_hydrometrie.py +80 -0
  86. hubeau_data-0.0.1/tests/test_eau_potable.py +103 -0
  87. hubeau_data-0.0.1/tests/test_ecoulement.py +139 -0
  88. hubeau_data-0.0.1/tests/test_hydrobiologie.py +155 -0
  89. hubeau_data-0.0.1/tests/test_hydrometrie.py +219 -0
  90. hubeau_data-0.0.1/tests/test_phytopharmaceutiques.py +142 -0
  91. hubeau_data-0.0.1/tests/test_piezometrie.py +151 -0
  92. hubeau_data-0.0.1/tests/test_poisson.py +189 -0
  93. hubeau_data-0.0.1/tests/test_prelevements.py +141 -0
  94. hubeau_data-0.0.1/tests/test_qualite_nappes.py +116 -0
  95. hubeau_data-0.0.1/tests/test_qualite_rivieres.py +142 -0
  96. hubeau_data-0.0.1/tests/test_retry.py +88 -0
  97. hubeau_data-0.0.1/tests/test_temperature.py +109 -0
  98. hubeau_data-0.0.1/uv.lock +2757 -0
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ qa:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v5
17
+ with:
18
+ enable-cache: true
19
+ python-version: "3.13"
20
+
21
+ - name: Install dependencies
22
+ run: uv sync
23
+
24
+ - name: Run Ruff Linter
25
+ run: uv run ruff check .
26
+
27
+ - name: Run Mypy Type Checking
28
+ run: uv run mypy .
29
+
30
+ - name: Run Mocked Tests
31
+ run: uv run pytest -m "not live"
@@ -0,0 +1,58 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ dist/
11
+ build/
12
+ *.egg-info/
13
+ *.egg
14
+
15
+ # Virtual environments
16
+ venv/
17
+ env/
18
+ ENV/
19
+ .env/
20
+ .venv/
21
+
22
+ # Poetry
23
+ poetry.lock
24
+
25
+ # Unit test / coverage reports
26
+ htmlcov/
27
+ .tox/
28
+ .coverage
29
+ .coverage.*
30
+ .cache
31
+ nosetests.xml
32
+ coverage.xml
33
+ *.cover
34
+
35
+ # Jupyter Notebook checkpoints
36
+ .ipynb_checkpoints
37
+
38
+ # PyCharm settings
39
+ .idea/
40
+ *.iml
41
+
42
+ # Mac OS
43
+ .DS_Store
44
+
45
+ # Windows
46
+ Thumbs.db
47
+
48
+ # Local environment variables and secrets
49
+ .env
50
+ .env.*
51
+
52
+ # Ignore mypy daemon status file (auto-generated, local only)
53
+ .dmypy.json
54
+
55
+ # data generated during exploration
56
+ data/
57
+ .virtual_documents/
58
+ .virtual_documents/
@@ -0,0 +1,11 @@
1
+ [language-server.jedi-language-server]
2
+ command = ".venv/bin/jedi-language-server"
3
+ args = []
4
+
5
+ [language-server.jedi-language-server.config]
6
+ workspace.environmentPath = ".venv"
7
+ jediSettings.autoImportModules = ["httpx", "pydantic", "pandas"]
8
+
9
+ [[language]]
10
+ name = "python"
11
+ language-servers = ["ruff", "jedi-language-server"]
@@ -0,0 +1,16 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-check
5
+ name: Ruff Check
6
+ entry: uv run ruff check .
7
+ language: system
8
+ pass_filenames: false
9
+ stages: [pre-commit]
10
+
11
+ - id: ci-check
12
+ name: Run Full CI (Lint, Type, Test)
13
+ entry: bash -c "uv run ruff check . && uv run mypy . && uv run pytest -m 'not live'"
14
+ language: system
15
+ pass_filenames: false
16
+ stages: [pre-push]
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ### Added
9
+
10
+ - Full Hub'Eau API coverage — 11 APIs implemented:
11
+ `hydrometrie`, `qualite_rivieres`, `piezometrie`, `qualite_nappes`,
12
+ `ecoulement`, `temperature`, `prelevements`, `hydrobiologie`,
13
+ `poisson`, `eau_potable`, `phytopharmaceutiques`
14
+ - `check_health(n_requests)` on every API — latency stats, healthy ratio
15
+ - `data_coverage(...)` on every API — data availability windows
16
+ - CLI health check scripts for every API under `scripts/<api>/check_health.py`
17
+ - Typed `Params` models for every endpoint across all APIs
18
+ - Pydantic v2 models for all API responses
19
+ - Pydantic mypy plugin for strict type checking
20
+ - Mocked test suite using `pytest-httpx` — CI runs without network dependency
21
+ - `@pytest.mark.live` marker to separate integration from unit tests
22
+ - `xfail` markers on known unstable endpoints (qualite_rivieres, qualite_nappes,
23
+ temperature chronique, prelevements chroniques)
24
+ - `models/health.py` — shared `EndpointStatus`, `HealthReport`, `DataWindow`,
25
+ `CoverageReport` models
26
+ - Optional dependency groups: `[dataframe]`, `[geo]`, `[viz]`, `[all]`
27
+ - `CHANGELOG.md` + `CONTRIBUTING.md`
28
+ - CI badge in README
29
+
30
+ ### Changed
31
+
32
+ - Migrated all API methods from `**kwargs` to typed `Params` models
33
+ - Rewrote README: full API coverage table, assertive tone, accurate quickstart
34
+ - Split runtime dependencies — core install requires only `httpx` and `pydantic`
35
+ - Pinned `mypy<2.0` pending pydantic plugin compatibility with mypy 2.x
36
+ - Removed `SimpleHydrometrieClient` — `HubeauClient` is the single entry point
37
+
38
+ ### Fixed
39
+
40
+ - `get_obs_elab` was outside `HydrometrieAPI` class (indentation bug)
41
+ - `code_entite` → `code_station` for hydrometrie observations (API mismatch)
42
+ - `Station` and `Site` fields relaxed to `Optional` — Hub'Eau returns null on many fields
43
+ - `phytopharmaceutiques` BASE_URL corrected to `/api/v1/vente_achat_phyto`
44
+
45
+ ### Skipped
46
+
47
+ - **Surveillance Littoral** — API being decommissioned by Hub'Eau
48
+ - **Indicateurs Services** — API under maintenance
@@ -0,0 +1,52 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ git clone https://github.com/pfei/hubeau-data.git
7
+ cd hubeau-data
8
+ uv sync --all-extras
9
+ ```
10
+
11
+ ## Quality checks
12
+
13
+ Run these before every commit (pre-commit handles this automatically after `uv run pre-commit install`):
14
+
15
+ ```bash
16
+ uv run ruff check . # lint
17
+ uv run mypy . # type check (strict)
18
+ uv run pytest -m "not live" # fast mocked tests
19
+ ```
20
+
21
+ To run the full suite including real API calls:
22
+
23
+ ```bash
24
+ uv run pytest -m "live" -s
25
+ ```
26
+
27
+ ## Test markers
28
+
29
+ - Tests without a marker: mocked, deterministic, run in CI
30
+ - `@pytest.mark.live`: real network calls — may be slow or flaky depending on upstream API health
31
+
32
+ ## Commit conventions
33
+
34
+ This project uses [Conventional Commits](https://www.conventionalcommits.org/):
35
+
36
+ Examples:
37
+
38
+ - `feat(hydrometrie): add pagination support for obs_elab`
39
+ - `fix(qualite_rivieres): handle empty data response`
40
+ - `refactor(models): migrate SiteParams to typed fields`
41
+ - `docs: update README quickstart`
42
+ - `test: add mocked tests for hydrometrie endpoints`
43
+ - `build: add optional dependency groups`
44
+
45
+ ## Adding a new Hub'Eau API
46
+
47
+ - 1. Add Pydantic response models in `src/hubeau_data/models/<api_name>.py`
48
+ - 2. Add typed `Params` models in the same file
49
+ - 3. Add the API client in `src/hubeau_data/api/<api_name>.py`
50
+ - 4. Register it on `HubeauClient` in `src/hubeau_data/client.py`
51
+ - 5. Add mocked + live tests in `tests/test_<api_name>.py`
52
+ - 6. Update `README.md` API coverage table
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pierre Feilles
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: hubeau-data
3
+ Version: 0.0.1
4
+ Summary: Pythonic, typed, modern client for the Hubeau water data APIs.
5
+ Project-URL: Homepage, https://github.com/pfei/hubeau-data
6
+ Project-URL: Repository, https://github.com/pfei/hubeau-data
7
+ Project-URL: Changelog, https://github.com/pfei/hubeau-data/blob/main/CHANGELOG.md
8
+ Author-email: Pierre Feilles <pierre.feilles@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENCE.md
11
+ Keywords: api-client,france,hubeau,hydrology,open-data,water
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Scientific/Engineering :: Hydrology
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.13
19
+ Requires-Dist: httpx>=0.28.1
20
+ Requires-Dist: pydantic>=2.7.0
21
+ Requires-Dist: tenacity>=9.1.4
22
+ Provides-Extra: all
23
+ Requires-Dist: geopandas>=1.0.1; extra == 'all'
24
+ Requires-Dist: matplotlib>=3.10.3; extra == 'all'
25
+ Requires-Dist: pandas>=2.2.0; extra == 'all'
26
+ Requires-Dist: shapely>=2.1.1; extra == 'all'
27
+ Provides-Extra: dataframe
28
+ Requires-Dist: pandas>=2.2.0; extra == 'dataframe'
29
+ Provides-Extra: geo
30
+ Requires-Dist: geopandas>=1.0.1; extra == 'geo'
31
+ Requires-Dist: shapely>=2.1.1; extra == 'geo'
32
+ Provides-Extra: viz
33
+ Requires-Dist: matplotlib>=3.10.3; extra == 'viz'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # hubeau-data
37
+
38
+ [![CI](https://github.com/pfei/hubeau-data/actions/workflows/ci.yml/badge.svg)](https://github.com/pfei/hubeau-data/actions/workflows/ci.yml)
39
+ [![Python Version](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/)
40
+ [![Checked with mypy](https://img.shields.io/badge/mypy-strict-green.svg)](https://mypy.readthedocs.io/en/stable/config_file.html#using-a-pyproject-toml-file)
41
+ [![Linting: ruff](https://img.shields.io/badge/linting-ruff-orange.svg)](https://github.com/astral-sh/ruff)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
43
+ [![Package Manager: uv](https://img.shields.io/badge/managed%20by-uv-purple.svg)](https://github.com/astral-sh/uv)
44
+
45
+ Typed, modern Python client for the [Hub'Eau](https://hubeau.eaufrance.fr/) water data APIs.
46
+
47
+ Hub'Eau exposes 15+ REST APIs for French national water data — but no official typed Python client exists.
48
+ This library fills that gap: Pydantic v2 models, strict typing, and a clean interface ready for data science workflows.
49
+
50
+ ## Quickstart
51
+
52
+ ```python
53
+ from hubeau_data.client import HubeauClient
54
+ from hubeau_data.models.hydrometrie import ObservationTrParams
55
+ from hubeau_data.models.qualite_rivieres import StationPcParams
56
+
57
+ client = HubeauClient()
58
+
59
+ # Hydrométrie — real-time observations
60
+ params = ObservationTrParams(code_station=["Y120201001"], size=3)
61
+ observations = client.hydrometrie.get_observations_tr(params=params)
62
+ print(observations[0].date_obs, observations[0].resultat_obs)
63
+
64
+ # Qualité Rivières — water quality stations
65
+ stations = client.qualite_rivieres.get_stations(
66
+ params=StationPcParams(code_departement=["75"], size=3)
67
+ )
68
+ print(stations[0].code_station, stations[0].libelle_station)
69
+
70
+ # Eau potable — drinking water analyses for a commune
71
+ from hubeau_data.models.eau_potable import ResultatEauPotableParams
72
+ resultats = client.eau_potable.get_resultats_dis(
73
+ params=ResultatEauPotableParams(code_commune=["75056"], size=5)
74
+ )
75
+ print(resultats[0].libelle_parametre, resultats[0].resultat_numerique)
76
+
77
+ # Phytopharmaceutiques — national pesticide sales
78
+ from hubeau_data.models.phytopharmaceutiques import VenteSubstanceParams
79
+ ventes = client.phytopharmaceutiques.get_ventes_substances(
80
+ params=VenteSubstanceParams(type_territoire="National", size=5)
81
+ )
82
+ print(ventes[0].libelle_substance, ventes[0].quantite, ventes[0].annee)
83
+
84
+ # API health check — works on every API
85
+ report = client.hydrometrie.check_health(n_requests=3)
86
+ print(report.summary())
87
+
88
+ # Data coverage — spot-check stations
89
+ cov = client.hydrometrie.data_coverage(code_station="Y120201001")
90
+ print(cov.summary())
91
+ ```
92
+
93
+ ## Async client
94
+
95
+ For bulk data collection — e.g. fetching many stations before inserting into a database —
96
+ `AsyncHubeauClient` mirrors the sync client and supports `asyncio.gather()` for parallel requests:
97
+
98
+ ```python
99
+ import asyncio
100
+ from hubeau_data.async_client import AsyncHubeauClient
101
+ from hubeau_data.models.hydrometrie import ObservationTrParams
102
+
103
+ async def main():
104
+ async with AsyncHubeauClient() as client:
105
+ codes = ["Y120201001", "K418001001", "A1234567"]
106
+ tasks = [
107
+ client.hydrometrie.get_observations_tr(
108
+ params=ObservationTrParams(code_station=[c], size=10)
109
+ )
110
+ for c in codes
111
+ ]
112
+ results = await asyncio.gather(*tasks)
113
+ for code, obs in zip(codes, results):
114
+ print(code, len(obs), "observations")
115
+
116
+ asyncio.run(main())
117
+ ```
118
+
119
+ All 11 APIs are available on `AsyncHubeauClient` with the same method names as the sync client
120
+ (`get_sites`, `get_stations`, etc.) — just `await` them. Retry logic (tenacity) applies to async
121
+ requests too. `check_health` and `data_coverage` are sync-only (diagnostic tools, not bulk operations).
122
+
123
+ ## API Coverage
124
+
125
+ | API | Status | Notes |
126
+ |-----|--------|-------|
127
+ | **Hydrométrie** | ✅ Supported | Sites, stations, real-time and elaborated observations |
128
+ | **Qualité des cours d'eau** | ⚠️ Partial | Stations and analyses. Upstream API has known stability issues |
129
+ | **Piézométrie** | ✅ Supported | Stations, chroniques, chroniques temps réel |
130
+ | **Qualité des nappes** | ⚠️ Partial | Stations and analyses. Known 503/timeout issues |
131
+ | **Écoulement** | ✅ Supported | Stations, observations, campaigns |
132
+ | **Température** | ✅ Supported | Stations and chroniques |
133
+ | **Prélèvements en eau** | ✅ Supported | Ouvrages, points de prélèvement, chroniques |
134
+ | **Hydrobiologie** | ✅ Supported | Stations, indices (IBGN/IBMR/IBD/IPR), taxons |
135
+ | **Poisson** | ✅ Supported | Stations, indicateurs IPR/IPR+, observations, operations |
136
+ | **Qualité eau potable** | ✅ Supported | Communes/UDI links, analysis results |
137
+ | **Phytopharmaceutiques** | ✅ Supported | Purchases and sales by substance and product |
138
+ | **Surveillance Littoral** | 🚫 Skipped | API being decommissioned by Hub'Eau |
139
+ | **Indicateurs Services** | 🚧 Maintenance | API under maintenance — see services.eaufrance.fr |
140
+
141
+ All supported APIs expose `check_health(n_requests)` and `data_coverage(...)`, and are available
142
+ on both `HubeauClient` (sync) and `AsyncHubeauClient` (async, except health/coverage).
143
+
144
+ ## Features
145
+
146
+ - Pydantic v2 models for all responses — strict runtime validation, IDE autocomplete
147
+ - Typed query `Params` models for every endpoint — no more `**kwargs`
148
+ - Sync (`HubeauClient`) and async (`AsyncHubeauClient`) clients, same method names
149
+ - Automatic retry with exponential backoff (tenacity) on transient errors — Hub'Eau APIs have known stability issues
150
+ - `check_health(n_requests)` — latency stats per endpoint, healthy ratio
151
+ - `data_coverage(...)` — data availability windows per station or territory
152
+ - Optional extras: `[dataframe]`, `[geo]`, `[viz]` — install only what you need
153
+
154
+ ## Stack
155
+
156
+ - Python 3.13+, `mypy --strict`, `ruff`, `uv`, `hatchling`, src-layout
157
+ - `httpx` + `tenacity` for resilient sync/async HTTP
158
+ - `pytest-httpx` mocked test suite — CI runs without network dependency
159
+
160
+ ## Installation & Development
161
+
162
+ ```zsh
163
+ git clone https://github.com/pfei/hubeau-data.git
164
+ cd hubeau-data
165
+ uv sync # core only
166
+ uv sync --all-extras # with pandas, geopandas, matplotlib
167
+ ```
168
+
169
+ ```zsh
170
+ uv run ruff check . # lint
171
+ uv run mypy . # type check
172
+ uv run pytest -m "not live" # fast mocked tests (CI)
173
+ uv run pytest -m "live" -s # real network integration tests
174
+ ```
175
+
176
+ ## Examples & Scripts
177
+
178
+ ```zsh
179
+ uv run python examples/demo.py
180
+ uv run jupyter lab # open examples/demo.ipynb
181
+ ```
182
+
183
+ Health check scripts for every API under `scripts/<api>/check_health.py`:
184
+
185
+ ```zsh
186
+ uv run python scripts/hydrometrie/check_health.py --n-requests 3 --random
187
+ uv run python scripts/qualite_rivieres/check_health.py --n-requests 2
188
+ uv run python scripts/eau_potable/check_health.py --commune 75056
189
+ uv run python scripts/phytopharmaceutiques/check_health.py
190
+ ```
191
+
192
+ Exploration scripts under `scripts/qualite_rivieres/` and `scripts/hydrometrie/`.
193
+
194
+ ## Roadmap
195
+
196
+ - [x] Full Hub'Eau API coverage (11 APIs implemented)
197
+ - [x] `check_health` and `data_coverage` on all APIs
198
+ - [x] Typed `Params` models for every endpoint
199
+ - [x] Automatic retry with exponential backoff (tenacity)
200
+ - [x] Async client (`AsyncHubeauClient`, all 11 APIs)
201
+ - [x] Optional dependency groups — `pandas`, `geopandas`, `matplotlib` as extras
202
+ - [x] `CHANGELOG.md` + `CONTRIBUTING.md`
203
+ - [ ] PyPI release
204
+
205
+ ## License
206
+
207
+ MIT © Pierre Feilles
@@ -0,0 +1,172 @@
1
+ # hubeau-data
2
+
3
+ [![CI](https://github.com/pfei/hubeau-data/actions/workflows/ci.yml/badge.svg)](https://github.com/pfei/hubeau-data/actions/workflows/ci.yml)
4
+ [![Python Version](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/)
5
+ [![Checked with mypy](https://img.shields.io/badge/mypy-strict-green.svg)](https://mypy.readthedocs.io/en/stable/config_file.html#using-a-pyproject-toml-file)
6
+ [![Linting: ruff](https://img.shields.io/badge/linting-ruff-orange.svg)](https://github.com/astral-sh/ruff)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![Package Manager: uv](https://img.shields.io/badge/managed%20by-uv-purple.svg)](https://github.com/astral-sh/uv)
9
+
10
+ Typed, modern Python client for the [Hub'Eau](https://hubeau.eaufrance.fr/) water data APIs.
11
+
12
+ Hub'Eau exposes 15+ REST APIs for French national water data — but no official typed Python client exists.
13
+ This library fills that gap: Pydantic v2 models, strict typing, and a clean interface ready for data science workflows.
14
+
15
+ ## Quickstart
16
+
17
+ ```python
18
+ from hubeau_data.client import HubeauClient
19
+ from hubeau_data.models.hydrometrie import ObservationTrParams
20
+ from hubeau_data.models.qualite_rivieres import StationPcParams
21
+
22
+ client = HubeauClient()
23
+
24
+ # Hydrométrie — real-time observations
25
+ params = ObservationTrParams(code_station=["Y120201001"], size=3)
26
+ observations = client.hydrometrie.get_observations_tr(params=params)
27
+ print(observations[0].date_obs, observations[0].resultat_obs)
28
+
29
+ # Qualité Rivières — water quality stations
30
+ stations = client.qualite_rivieres.get_stations(
31
+ params=StationPcParams(code_departement=["75"], size=3)
32
+ )
33
+ print(stations[0].code_station, stations[0].libelle_station)
34
+
35
+ # Eau potable — drinking water analyses for a commune
36
+ from hubeau_data.models.eau_potable import ResultatEauPotableParams
37
+ resultats = client.eau_potable.get_resultats_dis(
38
+ params=ResultatEauPotableParams(code_commune=["75056"], size=5)
39
+ )
40
+ print(resultats[0].libelle_parametre, resultats[0].resultat_numerique)
41
+
42
+ # Phytopharmaceutiques — national pesticide sales
43
+ from hubeau_data.models.phytopharmaceutiques import VenteSubstanceParams
44
+ ventes = client.phytopharmaceutiques.get_ventes_substances(
45
+ params=VenteSubstanceParams(type_territoire="National", size=5)
46
+ )
47
+ print(ventes[0].libelle_substance, ventes[0].quantite, ventes[0].annee)
48
+
49
+ # API health check — works on every API
50
+ report = client.hydrometrie.check_health(n_requests=3)
51
+ print(report.summary())
52
+
53
+ # Data coverage — spot-check stations
54
+ cov = client.hydrometrie.data_coverage(code_station="Y120201001")
55
+ print(cov.summary())
56
+ ```
57
+
58
+ ## Async client
59
+
60
+ For bulk data collection — e.g. fetching many stations before inserting into a database —
61
+ `AsyncHubeauClient` mirrors the sync client and supports `asyncio.gather()` for parallel requests:
62
+
63
+ ```python
64
+ import asyncio
65
+ from hubeau_data.async_client import AsyncHubeauClient
66
+ from hubeau_data.models.hydrometrie import ObservationTrParams
67
+
68
+ async def main():
69
+ async with AsyncHubeauClient() as client:
70
+ codes = ["Y120201001", "K418001001", "A1234567"]
71
+ tasks = [
72
+ client.hydrometrie.get_observations_tr(
73
+ params=ObservationTrParams(code_station=[c], size=10)
74
+ )
75
+ for c in codes
76
+ ]
77
+ results = await asyncio.gather(*tasks)
78
+ for code, obs in zip(codes, results):
79
+ print(code, len(obs), "observations")
80
+
81
+ asyncio.run(main())
82
+ ```
83
+
84
+ All 11 APIs are available on `AsyncHubeauClient` with the same method names as the sync client
85
+ (`get_sites`, `get_stations`, etc.) — just `await` them. Retry logic (tenacity) applies to async
86
+ requests too. `check_health` and `data_coverage` are sync-only (diagnostic tools, not bulk operations).
87
+
88
+ ## API Coverage
89
+
90
+ | API | Status | Notes |
91
+ |-----|--------|-------|
92
+ | **Hydrométrie** | ✅ Supported | Sites, stations, real-time and elaborated observations |
93
+ | **Qualité des cours d'eau** | ⚠️ Partial | Stations and analyses. Upstream API has known stability issues |
94
+ | **Piézométrie** | ✅ Supported | Stations, chroniques, chroniques temps réel |
95
+ | **Qualité des nappes** | ⚠️ Partial | Stations and analyses. Known 503/timeout issues |
96
+ | **Écoulement** | ✅ Supported | Stations, observations, campaigns |
97
+ | **Température** | ✅ Supported | Stations and chroniques |
98
+ | **Prélèvements en eau** | ✅ Supported | Ouvrages, points de prélèvement, chroniques |
99
+ | **Hydrobiologie** | ✅ Supported | Stations, indices (IBGN/IBMR/IBD/IPR), taxons |
100
+ | **Poisson** | ✅ Supported | Stations, indicateurs IPR/IPR+, observations, operations |
101
+ | **Qualité eau potable** | ✅ Supported | Communes/UDI links, analysis results |
102
+ | **Phytopharmaceutiques** | ✅ Supported | Purchases and sales by substance and product |
103
+ | **Surveillance Littoral** | 🚫 Skipped | API being decommissioned by Hub'Eau |
104
+ | **Indicateurs Services** | 🚧 Maintenance | API under maintenance — see services.eaufrance.fr |
105
+
106
+ All supported APIs expose `check_health(n_requests)` and `data_coverage(...)`, and are available
107
+ on both `HubeauClient` (sync) and `AsyncHubeauClient` (async, except health/coverage).
108
+
109
+ ## Features
110
+
111
+ - Pydantic v2 models for all responses — strict runtime validation, IDE autocomplete
112
+ - Typed query `Params` models for every endpoint — no more `**kwargs`
113
+ - Sync (`HubeauClient`) and async (`AsyncHubeauClient`) clients, same method names
114
+ - Automatic retry with exponential backoff (tenacity) on transient errors — Hub'Eau APIs have known stability issues
115
+ - `check_health(n_requests)` — latency stats per endpoint, healthy ratio
116
+ - `data_coverage(...)` — data availability windows per station or territory
117
+ - Optional extras: `[dataframe]`, `[geo]`, `[viz]` — install only what you need
118
+
119
+ ## Stack
120
+
121
+ - Python 3.13+, `mypy --strict`, `ruff`, `uv`, `hatchling`, src-layout
122
+ - `httpx` + `tenacity` for resilient sync/async HTTP
123
+ - `pytest-httpx` mocked test suite — CI runs without network dependency
124
+
125
+ ## Installation & Development
126
+
127
+ ```zsh
128
+ git clone https://github.com/pfei/hubeau-data.git
129
+ cd hubeau-data
130
+ uv sync # core only
131
+ uv sync --all-extras # with pandas, geopandas, matplotlib
132
+ ```
133
+
134
+ ```zsh
135
+ uv run ruff check . # lint
136
+ uv run mypy . # type check
137
+ uv run pytest -m "not live" # fast mocked tests (CI)
138
+ uv run pytest -m "live" -s # real network integration tests
139
+ ```
140
+
141
+ ## Examples & Scripts
142
+
143
+ ```zsh
144
+ uv run python examples/demo.py
145
+ uv run jupyter lab # open examples/demo.ipynb
146
+ ```
147
+
148
+ Health check scripts for every API under `scripts/<api>/check_health.py`:
149
+
150
+ ```zsh
151
+ uv run python scripts/hydrometrie/check_health.py --n-requests 3 --random
152
+ uv run python scripts/qualite_rivieres/check_health.py --n-requests 2
153
+ uv run python scripts/eau_potable/check_health.py --commune 75056
154
+ uv run python scripts/phytopharmaceutiques/check_health.py
155
+ ```
156
+
157
+ Exploration scripts under `scripts/qualite_rivieres/` and `scripts/hydrometrie/`.
158
+
159
+ ## Roadmap
160
+
161
+ - [x] Full Hub'Eau API coverage (11 APIs implemented)
162
+ - [x] `check_health` and `data_coverage` on all APIs
163
+ - [x] Typed `Params` models for every endpoint
164
+ - [x] Automatic retry with exponential backoff (tenacity)
165
+ - [x] Async client (`AsyncHubeauClient`, all 11 APIs)
166
+ - [x] Optional dependency groups — `pandas`, `geopandas`, `matplotlib` as extras
167
+ - [x] `CHANGELOG.md` + `CONTRIBUTING.md`
168
+ - [ ] PyPI release
169
+
170
+ ## License
171
+
172
+ MIT © Pierre Feilles