aiodpc 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aiodpc-0.1.0/.gitignore +29 -0
- aiodpc-0.1.0/LICENSE +22 -0
- aiodpc-0.1.0/PKG-INFO +201 -0
- aiodpc-0.1.0/README.md +175 -0
- aiodpc-0.1.0/pyproject.toml +115 -0
- aiodpc-0.1.0/src/aiodpc/__init__.py +62 -0
- aiodpc-0.1.0/src/aiodpc/client.py +351 -0
- aiodpc-0.1.0/src/aiodpc/const.py +42 -0
- aiodpc-0.1.0/src/aiodpc/discovery.py +87 -0
- aiodpc-0.1.0/src/aiodpc/exceptions.py +23 -0
- aiodpc-0.1.0/src/aiodpc/geo.py +76 -0
- aiodpc-0.1.0/src/aiodpc/models.py +219 -0
- aiodpc-0.1.0/src/aiodpc/parsers.py +417 -0
- aiodpc-0.1.0/src/aiodpc/py.typed +0 -0
- aiodpc-0.1.0/src/aiodpc/urls.py +62 -0
- aiodpc-0.1.0/tests/__init__.py +89 -0
- aiodpc-0.1.0/tests/conftest.py +45 -0
- aiodpc-0.1.0/tests/fixtures/criticality_today.json +35 -0
- aiodpc-0.1.0/tests/fixtures/criticality_tomorrow.json +35 -0
- aiodpc-0.1.0/tests/fixtures/phenomena_domani.json +19 -0
- aiodpc-0.1.0/tests/fixtures/phenomena_dopodomani.json +6 -0
- aiodpc-0.1.0/tests/fixtures/phenomena_oggi.json +31 -0
- aiodpc-0.1.0/tests/fixtures/real/criticality_20261113_1434_today.json +1 -0
- aiodpc-0.1.0/tests/fixtures/real/criticality_page.html +7 -0
- aiodpc-0.1.0/tests/fixtures/real/phenomena_20261113_oggi.json +1 -0
- aiodpc-0.1.0/tests/fixtures/real/vigilance_20261113_oggi.json +1 -0
- aiodpc-0.1.0/tests/fixtures/real/vigilance_page.html +4 -0
- aiodpc-0.1.0/tests/fixtures/vigilance_domani.json +33 -0
- aiodpc-0.1.0/tests/fixtures/vigilance_dopodomani.json +33 -0
- aiodpc-0.1.0/tests/fixtures/vigilance_oggi.json +33 -0
- aiodpc-0.1.0/tests/server.py +138 -0
- aiodpc-0.1.0/tests/test_client.py +573 -0
- aiodpc-0.1.0/tests/test_discovery.py +82 -0
- aiodpc-0.1.0/tests/test_geo.py +63 -0
- aiodpc-0.1.0/tests/test_models.py +142 -0
- aiodpc-0.1.0/tests/test_parsers.py +520 -0
- aiodpc-0.1.0/tests/test_real_data.py +196 -0
aiodpc-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Virtual environments
|
|
2
|
+
.venv/
|
|
3
|
+
venv/
|
|
4
|
+
env/
|
|
5
|
+
|
|
6
|
+
# Python
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*.egg-info/
|
|
10
|
+
|
|
11
|
+
# Tool caches
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
|
|
16
|
+
# Coverage
|
|
17
|
+
.coverage
|
|
18
|
+
.coverage.*
|
|
19
|
+
coverage.xml
|
|
20
|
+
htmlcov/
|
|
21
|
+
|
|
22
|
+
# Build artifacts
|
|
23
|
+
build/
|
|
24
|
+
dist/
|
|
25
|
+
|
|
26
|
+
# Editors and OS
|
|
27
|
+
.idea/
|
|
28
|
+
.vscode/
|
|
29
|
+
.DS_Store
|
aiodpc-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 caiosweet
|
|
4
|
+
Copyright (c) 2026 Webysther Sperandio
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
aiodpc-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: aiodpc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async client for the Italian Civil Protection Department (DPC) alert and weather vigilance bulletins
|
|
5
|
+
Author: Webysther Sperandio
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: alert,allerta,asyncio,civil protection,dpc,italy,protezione civile,weather
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Framework :: AsyncIO
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.13
|
|
22
|
+
Requires-Dist: aiohttp>=3.10
|
|
23
|
+
Requires-Dist: geojson-utils<0.1,>=0.0.3
|
|
24
|
+
Requires-Dist: tzdata; sys_platform == 'win32'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# aiodpc
|
|
28
|
+
|
|
29
|
+
Async Python client for the open data of the Italian Civil Protection
|
|
30
|
+
Department (Dipartimento della Protezione Civile, DPC): the national alert
|
|
31
|
+
bulletins.
|
|
32
|
+
|
|
33
|
+
- **Bollettino di criticità idrogeologica e idraulica**: the alert level
|
|
34
|
+
(green, yellow, orange, red) of each alert zone for today and tomorrow, for
|
|
35
|
+
the hydraulic, thunderstorm and hydrogeological risks.
|
|
36
|
+
- **Bollettino di vigilanza meteorologica**: the expected precipitation of each
|
|
37
|
+
vigilance zone for today, tomorrow and the day after, and the significant
|
|
38
|
+
weather phenomena (heavy rain, wind, sea storms, frost, fog, temperatures).
|
|
39
|
+
|
|
40
|
+
Given a location (coordinates, optionally a municipality), aiodpc finds its
|
|
41
|
+
zone and returns typed results. It is built on `asyncio` and `aiohttp`, has no
|
|
42
|
+
Home Assistant dependency and is fully typed.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install aiodpc
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Python 3.13 or later is required.
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import asyncio
|
|
56
|
+
|
|
57
|
+
from aiodpc import AlertLevel, DpcClient, DpcError, Risk
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def main() -> None:
|
|
61
|
+
async with DpcClient() as client:
|
|
62
|
+
try:
|
|
63
|
+
criticality = await client.get_criticality(41.9, 12.5)
|
|
64
|
+
vigilance = await client.get_vigilance(
|
|
65
|
+
41.9, 12.5, phenomena_radius_km=50
|
|
66
|
+
)
|
|
67
|
+
except DpcError as err:
|
|
68
|
+
print(f"DPC data unavailable: {err}")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
print(f"Criticality bulletin {criticality.id}")
|
|
72
|
+
for day in criticality.days:
|
|
73
|
+
print(f"{day.date} {day.zone_name}: {day.summary.alert}")
|
|
74
|
+
for risk, assessment in day.risks.items():
|
|
75
|
+
print(f" {risk.name.lower()}: {assessment.level.name}")
|
|
76
|
+
|
|
77
|
+
if (today := criticality.today()) and today.max_level >= AlertLevel.YELLOW:
|
|
78
|
+
print("Alert today:", today.risks[Risk.HYDROGEOLOGICAL].info)
|
|
79
|
+
|
|
80
|
+
for day in vigilance.days:
|
|
81
|
+
print(f"{day.date} {day.zone_name}: {day.precipitation}")
|
|
82
|
+
for phenomenon in day.phenomena:
|
|
83
|
+
print(
|
|
84
|
+
f" {phenomenon.category} {phenomenon.description}"
|
|
85
|
+
f" {phenomenon.distance_km} km {phenomenon.direction}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
asyncio.run(main())
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Pass your own session with `DpcClient(session)`; the client never closes a
|
|
93
|
+
session it did not create.
|
|
94
|
+
|
|
95
|
+
### Days and expiry
|
|
96
|
+
|
|
97
|
+
Each day of a bulletin carries its calendar `date` and its `expires` time (the
|
|
98
|
+
midnight, Europe/Rome, that ends the day). Look days up by date instead of by
|
|
99
|
+
position: `bulletin.day_for(date)`, `bulletin.today()` and
|
|
100
|
+
`bulletin.tomorrow()`. After midnight the bulletin published the day before
|
|
101
|
+
still answers `today()` with its second day until DPC publishes a new one.
|
|
102
|
+
|
|
103
|
+
By default, days that are already over are not downloaded
|
|
104
|
+
(`skip_expired=False` to get them all, e.g. for a past `bulletin_id`).
|
|
105
|
+
|
|
106
|
+
### Polling
|
|
107
|
+
|
|
108
|
+
The bulletin files are large (several MB per day). To poll, check the latest
|
|
109
|
+
ID and fetch the bulletin only when it changed:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from aiodpc import BulletinKind
|
|
113
|
+
|
|
114
|
+
latest = await client.latest_bulletin_id(BulletinKind.CRITICALITY)
|
|
115
|
+
if latest != criticality.id:
|
|
116
|
+
criticality = await client.get_criticality(41.9, 12.5, bulletin_id=latest)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The criticality bulletin is published every day by 16:00 (Europe/Rome) and may
|
|
120
|
+
be re-issued later the same day ("Aggiornamento"); the vigilance bulletin by
|
|
121
|
+
15:00.
|
|
122
|
+
|
|
123
|
+
### Municipality
|
|
124
|
+
|
|
125
|
+
With `municipality="Roma"`, the zones that list the municipality are
|
|
126
|
+
considered and the one with the highest level is used; `zone_name` is the zone
|
|
127
|
+
containing the coordinates. Large municipalities span several zones.
|
|
128
|
+
|
|
129
|
+
### Coverage check
|
|
130
|
+
|
|
131
|
+
`await client.is_location_covered(lat, lon)` returns whether the coordinates
|
|
132
|
+
are inside a DPC alert zone, for example to validate a configuration.
|
|
133
|
+
|
|
134
|
+
### Errors
|
|
135
|
+
|
|
136
|
+
All errors derive from `DpcError`:
|
|
137
|
+
|
|
138
|
+
- `DpcConnectionError`: network error, HTTP error status or timeout.
|
|
139
|
+
- `DpcParseError`: invalid JSON or unexpected data format.
|
|
140
|
+
- `DpcLocationError`: the location is not inside any zone of the bulletin.
|
|
141
|
+
|
|
142
|
+
Invalid arguments (coordinates, bulletin IDs) raise `ValueError`.
|
|
143
|
+
|
|
144
|
+
### Translations and codes
|
|
145
|
+
|
|
146
|
+
The texts written by DPC are kept in Italian (`Assessment.info`,
|
|
147
|
+
`Assessment.alert`, `VigilanceDay.precipitation`,
|
|
148
|
+
`Phenomenon.description`). Codes are exposed for translation and icons:
|
|
149
|
+
`AlertLevel`, `Risk` (value: the DPC code, e.g. `idraulico`),
|
|
150
|
+
`PhenomenonCategory` (e.g. `PRECIPITAZIONI`) and `Phenomenon.code` (the DPC
|
|
151
|
+
`id_fenomeno`).
|
|
152
|
+
|
|
153
|
+
### Low-level API
|
|
154
|
+
|
|
155
|
+
- `aiodpc.parsers`: pure parsing functions taking the decoded GeoJSON, to parse
|
|
156
|
+
one download for several locations or to test without network.
|
|
157
|
+
- `aiodpc.urls`: URLs of the bulletin files.
|
|
158
|
+
- `aiodpc.discovery`: the latest bulletin ID is read from the DPC map pages by
|
|
159
|
+
default (`MapPageIdSource`); implement `BulletinIdSource` to use another
|
|
160
|
+
source.
|
|
161
|
+
|
|
162
|
+
## Data source and attribution
|
|
163
|
+
|
|
164
|
+
The data is published by the Dipartimento della Protezione Civile on
|
|
165
|
+
[mappe.protezionecivile.gov.it](https://mappe.protezionecivile.gov.it/) and
|
|
166
|
+
GitHub
|
|
167
|
+
([pcm-dpc/DPC-Bollettini-Criticita-Idrogeologica-Idraulica](https://github.com/pcm-dpc/DPC-Bollettini-Criticita-Idrogeologica-Idraulica),
|
|
168
|
+
[pcm-dpc/DPC-Bollettini-Vigilanza-Meteorologica](https://github.com/pcm-dpc/DPC-Bollettini-Vigilanza-Meteorologica))
|
|
169
|
+
under the [Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/)
|
|
170
|
+
license. Applications showing the data must credit:
|
|
171
|
+
|
|
172
|
+
> Dipartimento della Protezione Civile – Presidenza del Consiglio dei Ministri
|
|
173
|
+
|
|
174
|
+
The text is available as `aiodpc.ATTRIBUTION`.
|
|
175
|
+
|
|
176
|
+
This project is not affiliated with or endorsed by DPC. The bulletins are
|
|
177
|
+
official, but this library is not: for safety decisions, always refer to
|
|
178
|
+
the official DPC and regional channels.
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
python -m venv .venv && . .venv/bin/activate
|
|
184
|
+
pip install --editable . --group dev # pip >= 25.1
|
|
185
|
+
ruff format --check . && ruff check .
|
|
186
|
+
mypy
|
|
187
|
+
pytest --cov
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The tests never reach DPC: a local HTTPS server answers for the DPC hosts.
|
|
191
|
+
`tests/fixtures/real` holds unmodified excerpts of real bulletins to guard the
|
|
192
|
+
data format.
|
|
193
|
+
|
|
194
|
+
## Credits
|
|
195
|
+
|
|
196
|
+
aiodpc derives from the [DPC Alert](https://github.com/caiosweet/Home-Assistant-custom-components-DPC-Alert)
|
|
197
|
+
Home Assistant integration by caiosweet (MIT license).
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT, see [LICENSE](LICENSE).
|
aiodpc-0.1.0/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# aiodpc
|
|
2
|
+
|
|
3
|
+
Async Python client for the open data of the Italian Civil Protection
|
|
4
|
+
Department (Dipartimento della Protezione Civile, DPC): the national alert
|
|
5
|
+
bulletins.
|
|
6
|
+
|
|
7
|
+
- **Bollettino di criticità idrogeologica e idraulica**: the alert level
|
|
8
|
+
(green, yellow, orange, red) of each alert zone for today and tomorrow, for
|
|
9
|
+
the hydraulic, thunderstorm and hydrogeological risks.
|
|
10
|
+
- **Bollettino di vigilanza meteorologica**: the expected precipitation of each
|
|
11
|
+
vigilance zone for today, tomorrow and the day after, and the significant
|
|
12
|
+
weather phenomena (heavy rain, wind, sea storms, frost, fog, temperatures).
|
|
13
|
+
|
|
14
|
+
Given a location (coordinates, optionally a municipality), aiodpc finds its
|
|
15
|
+
zone and returns typed results. It is built on `asyncio` and `aiohttp`, has no
|
|
16
|
+
Home Assistant dependency and is fully typed.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install aiodpc
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Python 3.13 or later is required.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import asyncio
|
|
30
|
+
|
|
31
|
+
from aiodpc import AlertLevel, DpcClient, DpcError, Risk
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def main() -> None:
|
|
35
|
+
async with DpcClient() as client:
|
|
36
|
+
try:
|
|
37
|
+
criticality = await client.get_criticality(41.9, 12.5)
|
|
38
|
+
vigilance = await client.get_vigilance(
|
|
39
|
+
41.9, 12.5, phenomena_radius_km=50
|
|
40
|
+
)
|
|
41
|
+
except DpcError as err:
|
|
42
|
+
print(f"DPC data unavailable: {err}")
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
print(f"Criticality bulletin {criticality.id}")
|
|
46
|
+
for day in criticality.days:
|
|
47
|
+
print(f"{day.date} {day.zone_name}: {day.summary.alert}")
|
|
48
|
+
for risk, assessment in day.risks.items():
|
|
49
|
+
print(f" {risk.name.lower()}: {assessment.level.name}")
|
|
50
|
+
|
|
51
|
+
if (today := criticality.today()) and today.max_level >= AlertLevel.YELLOW:
|
|
52
|
+
print("Alert today:", today.risks[Risk.HYDROGEOLOGICAL].info)
|
|
53
|
+
|
|
54
|
+
for day in vigilance.days:
|
|
55
|
+
print(f"{day.date} {day.zone_name}: {day.precipitation}")
|
|
56
|
+
for phenomenon in day.phenomena:
|
|
57
|
+
print(
|
|
58
|
+
f" {phenomenon.category} {phenomenon.description}"
|
|
59
|
+
f" {phenomenon.distance_km} km {phenomenon.direction}"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
asyncio.run(main())
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Pass your own session with `DpcClient(session)`; the client never closes a
|
|
67
|
+
session it did not create.
|
|
68
|
+
|
|
69
|
+
### Days and expiry
|
|
70
|
+
|
|
71
|
+
Each day of a bulletin carries its calendar `date` and its `expires` time (the
|
|
72
|
+
midnight, Europe/Rome, that ends the day). Look days up by date instead of by
|
|
73
|
+
position: `bulletin.day_for(date)`, `bulletin.today()` and
|
|
74
|
+
`bulletin.tomorrow()`. After midnight the bulletin published the day before
|
|
75
|
+
still answers `today()` with its second day until DPC publishes a new one.
|
|
76
|
+
|
|
77
|
+
By default, days that are already over are not downloaded
|
|
78
|
+
(`skip_expired=False` to get them all, e.g. for a past `bulletin_id`).
|
|
79
|
+
|
|
80
|
+
### Polling
|
|
81
|
+
|
|
82
|
+
The bulletin files are large (several MB per day). To poll, check the latest
|
|
83
|
+
ID and fetch the bulletin only when it changed:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from aiodpc import BulletinKind
|
|
87
|
+
|
|
88
|
+
latest = await client.latest_bulletin_id(BulletinKind.CRITICALITY)
|
|
89
|
+
if latest != criticality.id:
|
|
90
|
+
criticality = await client.get_criticality(41.9, 12.5, bulletin_id=latest)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The criticality bulletin is published every day by 16:00 (Europe/Rome) and may
|
|
94
|
+
be re-issued later the same day ("Aggiornamento"); the vigilance bulletin by
|
|
95
|
+
15:00.
|
|
96
|
+
|
|
97
|
+
### Municipality
|
|
98
|
+
|
|
99
|
+
With `municipality="Roma"`, the zones that list the municipality are
|
|
100
|
+
considered and the one with the highest level is used; `zone_name` is the zone
|
|
101
|
+
containing the coordinates. Large municipalities span several zones.
|
|
102
|
+
|
|
103
|
+
### Coverage check
|
|
104
|
+
|
|
105
|
+
`await client.is_location_covered(lat, lon)` returns whether the coordinates
|
|
106
|
+
are inside a DPC alert zone, for example to validate a configuration.
|
|
107
|
+
|
|
108
|
+
### Errors
|
|
109
|
+
|
|
110
|
+
All errors derive from `DpcError`:
|
|
111
|
+
|
|
112
|
+
- `DpcConnectionError`: network error, HTTP error status or timeout.
|
|
113
|
+
- `DpcParseError`: invalid JSON or unexpected data format.
|
|
114
|
+
- `DpcLocationError`: the location is not inside any zone of the bulletin.
|
|
115
|
+
|
|
116
|
+
Invalid arguments (coordinates, bulletin IDs) raise `ValueError`.
|
|
117
|
+
|
|
118
|
+
### Translations and codes
|
|
119
|
+
|
|
120
|
+
The texts written by DPC are kept in Italian (`Assessment.info`,
|
|
121
|
+
`Assessment.alert`, `VigilanceDay.precipitation`,
|
|
122
|
+
`Phenomenon.description`). Codes are exposed for translation and icons:
|
|
123
|
+
`AlertLevel`, `Risk` (value: the DPC code, e.g. `idraulico`),
|
|
124
|
+
`PhenomenonCategory` (e.g. `PRECIPITAZIONI`) and `Phenomenon.code` (the DPC
|
|
125
|
+
`id_fenomeno`).
|
|
126
|
+
|
|
127
|
+
### Low-level API
|
|
128
|
+
|
|
129
|
+
- `aiodpc.parsers`: pure parsing functions taking the decoded GeoJSON, to parse
|
|
130
|
+
one download for several locations or to test without network.
|
|
131
|
+
- `aiodpc.urls`: URLs of the bulletin files.
|
|
132
|
+
- `aiodpc.discovery`: the latest bulletin ID is read from the DPC map pages by
|
|
133
|
+
default (`MapPageIdSource`); implement `BulletinIdSource` to use another
|
|
134
|
+
source.
|
|
135
|
+
|
|
136
|
+
## Data source and attribution
|
|
137
|
+
|
|
138
|
+
The data is published by the Dipartimento della Protezione Civile on
|
|
139
|
+
[mappe.protezionecivile.gov.it](https://mappe.protezionecivile.gov.it/) and
|
|
140
|
+
GitHub
|
|
141
|
+
([pcm-dpc/DPC-Bollettini-Criticita-Idrogeologica-Idraulica](https://github.com/pcm-dpc/DPC-Bollettini-Criticita-Idrogeologica-Idraulica),
|
|
142
|
+
[pcm-dpc/DPC-Bollettini-Vigilanza-Meteorologica](https://github.com/pcm-dpc/DPC-Bollettini-Vigilanza-Meteorologica))
|
|
143
|
+
under the [Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/)
|
|
144
|
+
license. Applications showing the data must credit:
|
|
145
|
+
|
|
146
|
+
> Dipartimento della Protezione Civile – Presidenza del Consiglio dei Ministri
|
|
147
|
+
|
|
148
|
+
The text is available as `aiodpc.ATTRIBUTION`.
|
|
149
|
+
|
|
150
|
+
This project is not affiliated with or endorsed by DPC. The bulletins are
|
|
151
|
+
official, but this library is not: for safety decisions, always refer to
|
|
152
|
+
the official DPC and regional channels.
|
|
153
|
+
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
python -m venv .venv && . .venv/bin/activate
|
|
158
|
+
pip install --editable . --group dev # pip >= 25.1
|
|
159
|
+
ruff format --check . && ruff check .
|
|
160
|
+
mypy
|
|
161
|
+
pytest --cov
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The tests never reach DPC: a local HTTPS server answers for the DPC hosts.
|
|
165
|
+
`tests/fixtures/real` holds unmodified excerpts of real bulletins to guard the
|
|
166
|
+
data format.
|
|
167
|
+
|
|
168
|
+
## Credits
|
|
169
|
+
|
|
170
|
+
aiodpc derives from the [DPC Alert](https://github.com/caiosweet/Home-Assistant-custom-components-DPC-Alert)
|
|
171
|
+
Home Assistant integration by caiosweet (MIT license).
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT, see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "aiodpc"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Async client for the Italian Civil Protection Department (DPC) alert and weather vigilance bulletins"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "Webysther Sperandio" }]
|
|
13
|
+
requires-python = ">=3.13"
|
|
14
|
+
keywords = [
|
|
15
|
+
"allerta",
|
|
16
|
+
"alert",
|
|
17
|
+
"asyncio",
|
|
18
|
+
"civil protection",
|
|
19
|
+
"dpc",
|
|
20
|
+
"italy",
|
|
21
|
+
"protezione civile",
|
|
22
|
+
"weather",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Framework :: AsyncIO",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Natural Language :: English",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
34
|
+
"Topic :: Scientific/Engineering :: Atmospheric Science",
|
|
35
|
+
"Topic :: Software Development :: Libraries",
|
|
36
|
+
"Typing :: Typed",
|
|
37
|
+
]
|
|
38
|
+
dependencies = [
|
|
39
|
+
"aiohttp>=3.10",
|
|
40
|
+
"geojson-utils>=0.0.3,<0.1",
|
|
41
|
+
# zoneinfo needs a time zone database; Windows has none built in.
|
|
42
|
+
"tzdata; sys_platform == 'win32'",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[dependency-groups]
|
|
46
|
+
dev = [
|
|
47
|
+
"mypy>=1.15",
|
|
48
|
+
"pytest>=8.3",
|
|
49
|
+
"pytest-asyncio>=1.0",
|
|
50
|
+
"pytest-cov>=6.0",
|
|
51
|
+
"ruff==0.14.4",
|
|
52
|
+
# Certificates of the local HTTPS server standing in for DPC in the tests
|
|
53
|
+
"trustme>=1.2",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.hatch.version]
|
|
57
|
+
path = "src/aiodpc/__init__.py"
|
|
58
|
+
|
|
59
|
+
[tool.hatch.build.targets.wheel]
|
|
60
|
+
packages = ["src/aiodpc"]
|
|
61
|
+
|
|
62
|
+
[tool.hatch.build.targets.sdist]
|
|
63
|
+
include = ["src", "tests", "README.md", "LICENSE"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
target-version = "py313"
|
|
67
|
+
src = ["src", "tests"]
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint]
|
|
70
|
+
select = ["ALL"]
|
|
71
|
+
ignore = [
|
|
72
|
+
"COM812", # conflicts with the formatter
|
|
73
|
+
"D203", # incompatible with D211
|
|
74
|
+
"D213", # incompatible with D212
|
|
75
|
+
"PLR0913", # keyword-only options on public methods
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint.flake8-type-checking]
|
|
79
|
+
# Keep the imports of dataclass annotations at runtime (typing.get_type_hints).
|
|
80
|
+
runtime-evaluated-decorators = ["dataclasses.dataclass"]
|
|
81
|
+
|
|
82
|
+
[tool.ruff.lint.per-file-ignores]
|
|
83
|
+
"tests/**" = [
|
|
84
|
+
"PLR2004", # magic values in assertions
|
|
85
|
+
"S101", # assert
|
|
86
|
+
"SLF001", # private access to test internals
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.mypy]
|
|
90
|
+
python_version = "3.13"
|
|
91
|
+
strict = true
|
|
92
|
+
files = ["src", "tests"]
|
|
93
|
+
mypy_path = "src"
|
|
94
|
+
|
|
95
|
+
[[tool.mypy.overrides]]
|
|
96
|
+
# geojson-utils ships type hints but no py.typed marker.
|
|
97
|
+
module = ["geojson_utils", "geojson_utils.*"]
|
|
98
|
+
ignore_missing_imports = true
|
|
99
|
+
|
|
100
|
+
[tool.pytest.ini_options]
|
|
101
|
+
asyncio_mode = "auto"
|
|
102
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
103
|
+
testpaths = ["tests"]
|
|
104
|
+
addopts = "--strict-markers --strict-config"
|
|
105
|
+
filterwarnings = ["error"]
|
|
106
|
+
|
|
107
|
+
[tool.coverage.run]
|
|
108
|
+
source = ["aiodpc"]
|
|
109
|
+
branch = true
|
|
110
|
+
|
|
111
|
+
[tool.coverage.report]
|
|
112
|
+
show_missing = true
|
|
113
|
+
skip_covered = false
|
|
114
|
+
fail_under = 95
|
|
115
|
+
exclude_also = ["if TYPE_CHECKING:", "\\.\\.\\."]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Async client for the Italian Civil Protection Department (DPC) bulletins.
|
|
2
|
+
|
|
3
|
+
It reads the national "Bollettino di criticità idrogeologica e idraulica"
|
|
4
|
+
(alert levels per risk) and "Bollettino di vigilanza meteorologica" (weather
|
|
5
|
+
vigilance and phenomena) published by DPC as open data.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .client import DpcClient
|
|
9
|
+
from .const import (
|
|
10
|
+
ATTRIBUTION,
|
|
11
|
+
CRITICALITY_PAGE_URL,
|
|
12
|
+
DEFAULT_PHENOMENA_RADIUS_KM,
|
|
13
|
+
DEFAULT_REQUEST_TIMEOUT,
|
|
14
|
+
ROME_TZ,
|
|
15
|
+
VIGILANCE_PAGE_URL,
|
|
16
|
+
)
|
|
17
|
+
from .discovery import BulletinIdSource, MapPageIdSource, TextFetcher
|
|
18
|
+
from .exceptions import DpcConnectionError, DpcError, DpcLocationError, DpcParseError
|
|
19
|
+
from .models import (
|
|
20
|
+
AlertLevel,
|
|
21
|
+
Assessment,
|
|
22
|
+
BulletinKind,
|
|
23
|
+
CriticalityBulletin,
|
|
24
|
+
CriticalityDay,
|
|
25
|
+
Phenomenon,
|
|
26
|
+
PhenomenonCategory,
|
|
27
|
+
Risk,
|
|
28
|
+
VigilanceBulletin,
|
|
29
|
+
VigilanceDay,
|
|
30
|
+
rome_date,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__version__ = "0.1.0"
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"ATTRIBUTION",
|
|
37
|
+
"CRITICALITY_PAGE_URL",
|
|
38
|
+
"DEFAULT_PHENOMENA_RADIUS_KM",
|
|
39
|
+
"DEFAULT_REQUEST_TIMEOUT",
|
|
40
|
+
"ROME_TZ",
|
|
41
|
+
"VIGILANCE_PAGE_URL",
|
|
42
|
+
"AlertLevel",
|
|
43
|
+
"Assessment",
|
|
44
|
+
"BulletinIdSource",
|
|
45
|
+
"BulletinKind",
|
|
46
|
+
"CriticalityBulletin",
|
|
47
|
+
"CriticalityDay",
|
|
48
|
+
"DpcClient",
|
|
49
|
+
"DpcConnectionError",
|
|
50
|
+
"DpcError",
|
|
51
|
+
"DpcLocationError",
|
|
52
|
+
"DpcParseError",
|
|
53
|
+
"MapPageIdSource",
|
|
54
|
+
"Phenomenon",
|
|
55
|
+
"PhenomenonCategory",
|
|
56
|
+
"Risk",
|
|
57
|
+
"TextFetcher",
|
|
58
|
+
"VigilanceBulletin",
|
|
59
|
+
"VigilanceDay",
|
|
60
|
+
"__version__",
|
|
61
|
+
"rome_date",
|
|
62
|
+
]
|