meteo-lt-pkg 0.5.0__tar.gz → 0.5.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.
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/PKG-INFO +141 -11
- meteo_lt_pkg-0.5.0/meteo_lt_pkg.egg-info/PKG-INFO → meteo_lt_pkg-0.5.1/README.md +131 -28
- meteo_lt_pkg-0.5.1/meteo_lt/__init__.py +27 -0
- meteo_lt_pkg-0.5.1/meteo_lt/api.py +109 -0
- meteo_lt_pkg-0.5.1/meteo_lt/client.py +138 -0
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/meteo_lt/const.py +1 -4
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/meteo_lt/models.py +48 -24
- meteo_lt_pkg-0.5.1/meteo_lt/utils.py +36 -0
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/meteo_lt/warnings.py +15 -45
- meteo_lt_pkg-0.5.0/README.md → meteo_lt_pkg-0.5.1/meteo_lt_pkg.egg-info/PKG-INFO +158 -1
- meteo_lt_pkg-0.5.1/meteo_lt_pkg.egg-info/requires.txt +12 -0
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/pyproject.toml +14 -11
- meteo_lt_pkg-0.5.0/meteo_lt/__init__.py +0 -13
- meteo_lt_pkg-0.5.0/meteo_lt/api.py +0 -87
- meteo_lt_pkg-0.5.0/meteo_lt/client.py +0 -80
- meteo_lt_pkg-0.5.0/meteo_lt/utils.py +0 -34
- meteo_lt_pkg-0.5.0/meteo_lt_pkg.egg-info/requires.txt +0 -12
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/LICENSE +0 -0
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/MANIFEST.in +0 -0
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/meteo_lt_pkg.egg-info/SOURCES.txt +0 -0
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/meteo_lt_pkg.egg-info/dependency_links.txt +0 -0
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/meteo_lt_pkg.egg-info/top_level.txt +0 -0
- {meteo_lt_pkg-0.5.0 → meteo_lt_pkg-0.5.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meteo_lt-pkg
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: A library to fetch weather data from api.meteo.lt
|
|
5
5
|
Author-email: Brunas <brunonas@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/Brunas/meteo_lt-pkg
|
|
@@ -12,17 +12,17 @@ Classifier: Development Status :: 4 - Beta
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: aiohttp
|
|
15
|
+
Requires-Dist: aiohttp>=3.13
|
|
16
16
|
Provides-Extra: dev
|
|
17
|
-
Requires-Dist: pytest; extra == "dev"
|
|
18
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
19
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
20
|
-
Requires-Dist: black; extra == "dev"
|
|
21
|
-
Requires-Dist: coverage; extra == "dev"
|
|
22
|
-
Requires-Dist: flake8; extra == "dev"
|
|
23
|
-
Requires-Dist: pyflakes; extra == "dev"
|
|
24
|
-
Requires-Dist: pylint; extra == "dev"
|
|
25
|
-
Requires-Dist: build; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest>=9.0; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-cov>=7.0; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-asyncio>=1.3; extra == "dev"
|
|
20
|
+
Requires-Dist: black>=26.0; extra == "dev"
|
|
21
|
+
Requires-Dist: coverage>=7.0; extra == "dev"
|
|
22
|
+
Requires-Dist: flake8>=7.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pyflakes>=3.0; extra == "dev"
|
|
24
|
+
Requires-Dist: pylint>=4.0; extra == "dev"
|
|
25
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
26
26
|
Dynamic: license-file
|
|
27
27
|
|
|
28
28
|
# Meteo.Lt Lithuanian weather forecast package
|
|
@@ -310,6 +310,136 @@ warning = WeatherWarning(
|
|
|
310
310
|
print(f"Warning for {warning.county}: {warning.description}")
|
|
311
311
|
```
|
|
312
312
|
|
|
313
|
+
### HydroStation
|
|
314
|
+
|
|
315
|
+
Represents a hydrological observation station with water body information.
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
from meteo_lt import HydroStation
|
|
319
|
+
|
|
320
|
+
station = HydroStation(
|
|
321
|
+
code="klaipedos-juru-uosto-vms",
|
|
322
|
+
name="Klaipėdos jūrų uosto VMS",
|
|
323
|
+
water_body="Baltijos jūra",
|
|
324
|
+
coordinates=coords
|
|
325
|
+
)
|
|
326
|
+
print(f"Station: {station.name} on {station.water_body}")
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### HydroObservation
|
|
330
|
+
|
|
331
|
+
Represents a single hydrological observation with water measurements.
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
from meteo_lt import HydroObservation
|
|
335
|
+
|
|
336
|
+
observation = HydroObservation(
|
|
337
|
+
observation_datetime="2024-07-23T12:00:00+00:00",
|
|
338
|
+
water_level=481.8,
|
|
339
|
+
water_temperature=15.5,
|
|
340
|
+
water_discharge=100.0
|
|
341
|
+
)
|
|
342
|
+
print(f"Water level: {observation.water_level} cm")
|
|
343
|
+
print(f"Water temperature: {observation.water_temperature}°C")
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### HydroObservationData
|
|
347
|
+
|
|
348
|
+
Represents a collection of hydrological observations for a specific station.
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from meteo_lt import HydroObservationData
|
|
352
|
+
|
|
353
|
+
data = HydroObservationData(
|
|
354
|
+
station=station,
|
|
355
|
+
observations=[observation],
|
|
356
|
+
observations_data_range={"from": "2024-07-01", "to": "2024-07-23"}
|
|
357
|
+
)
|
|
358
|
+
print(f"Station: {data.station.name}")
|
|
359
|
+
print(f"Observations: {len(data.observations)}")
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Hydrological Data
|
|
363
|
+
|
|
364
|
+
The package provides access to hydrological data from Lithuanian water monitoring stations.
|
|
365
|
+
|
|
366
|
+
### Fetching Hydrological Stations
|
|
367
|
+
|
|
368
|
+
To get the list of all hydrological stations:
|
|
369
|
+
|
|
370
|
+
```python
|
|
371
|
+
async def fetch_hydro_stations():
|
|
372
|
+
async with MeteoLtAPI() as api:
|
|
373
|
+
stations = await api.get_hydro_stations()
|
|
374
|
+
for station in stations:
|
|
375
|
+
print(f"{station.name} ({station.code}) - Water body: {station.water_body}")
|
|
376
|
+
|
|
377
|
+
asyncio.run(fetch_hydro_stations())
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Finding the Nearest Hydrological Station
|
|
381
|
+
|
|
382
|
+
You can find the nearest hydrological station using coordinates:
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
async def find_nearest_hydro_station():
|
|
386
|
+
async with MeteoLtAPI() as api:
|
|
387
|
+
# Example coordinates for Klaipėda, Lithuania
|
|
388
|
+
nearest_station = await api.get_nearest_hydro_station(55.6872, 21.2797)
|
|
389
|
+
print(f"Nearest station: {nearest_station.name}")
|
|
390
|
+
print(f"Water body: {nearest_station.water_body}")
|
|
391
|
+
print(f"Distance: approximately {nearest_station.latitude}, {nearest_station.longitude}")
|
|
392
|
+
|
|
393
|
+
asyncio.run(find_nearest_hydro_station())
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Fetching Hydrological Observations
|
|
397
|
+
|
|
398
|
+
To get water level and temperature observations for a specific station:
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
async def fetch_hydro_observations():
|
|
402
|
+
async with MeteoLtAPI() as api:
|
|
403
|
+
# Get observations for a station
|
|
404
|
+
hydro_data = await api.get_hydro_observation_data("klaipedos-juru-uosto-vms")
|
|
405
|
+
|
|
406
|
+
print(f"Station: {hydro_data.station.name}")
|
|
407
|
+
print(f"Water body: {hydro_data.station.water_body}")
|
|
408
|
+
print(f"\nRecent observations:")
|
|
409
|
+
|
|
410
|
+
for observation in hydro_data.observations[:5]:
|
|
411
|
+
print(f" {observation.observation_datetime}")
|
|
412
|
+
print(f" Water level: {observation.water_level} cm")
|
|
413
|
+
print(f" Water temperature: {observation.water_temperature}°C")
|
|
414
|
+
if observation.water_discharge:
|
|
415
|
+
print(f" Water discharge: {observation.water_discharge} m³/s")
|
|
416
|
+
|
|
417
|
+
asyncio.run(fetch_hydro_observations())
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Getting Observations for Nearest Station
|
|
421
|
+
|
|
422
|
+
Combine location finding with observation fetching:
|
|
423
|
+
|
|
424
|
+
```python
|
|
425
|
+
async def get_nearest_station_observations():
|
|
426
|
+
async with MeteoLtAPI() as api:
|
|
427
|
+
# Find nearest hydro station to coordinates
|
|
428
|
+
nearest_station = await api.get_nearest_hydro_station(55.6872, 21.2797)
|
|
429
|
+
|
|
430
|
+
# Get observations for the nearest station
|
|
431
|
+
hydro_data = await api.get_hydro_observation_data(nearest_station.code)
|
|
432
|
+
|
|
433
|
+
print(f"Latest observations from {hydro_data.station.name}:")
|
|
434
|
+
if hydro_data.observations:
|
|
435
|
+
latest = hydro_data.observations[0]
|
|
436
|
+
print(f" Time: {latest.observation_datetime}")
|
|
437
|
+
print(f" Water level: {latest.water_level} cm")
|
|
438
|
+
print(f" Temperature: {latest.water_temperature}°C")
|
|
439
|
+
|
|
440
|
+
asyncio.run(get_nearest_station_observations())
|
|
441
|
+
```
|
|
442
|
+
|
|
313
443
|
## Contributing
|
|
314
444
|
|
|
315
445
|
Contributions are welcome! For major changes please open an issue to discuss or submit a pull request with your changes. If you want to contribute you can use devcontainers in vscode for easiest setup follow [instructions here](.devcontainer/README.md).
|
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: meteo_lt-pkg
|
|
3
|
-
Version: 0.5.0
|
|
4
|
-
Summary: A library to fetch weather data from api.meteo.lt
|
|
5
|
-
Author-email: Brunas <brunonas@gmail.com>
|
|
6
|
-
Project-URL: Homepage, https://github.com/Brunas/meteo_lt-pkg
|
|
7
|
-
Project-URL: Issues, https://github.com/Brunas/meteo_lt-pkg/issues
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Requires-Python: >=3.10
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: LICENSE
|
|
15
|
-
Requires-Dist: aiohttp
|
|
16
|
-
Provides-Extra: dev
|
|
17
|
-
Requires-Dist: pytest; extra == "dev"
|
|
18
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
19
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
20
|
-
Requires-Dist: black; extra == "dev"
|
|
21
|
-
Requires-Dist: coverage; extra == "dev"
|
|
22
|
-
Requires-Dist: flake8; extra == "dev"
|
|
23
|
-
Requires-Dist: pyflakes; extra == "dev"
|
|
24
|
-
Requires-Dist: pylint; extra == "dev"
|
|
25
|
-
Requires-Dist: build; extra == "dev"
|
|
26
|
-
Dynamic: license-file
|
|
27
|
-
|
|
28
1
|
# Meteo.Lt Lithuanian weather forecast package
|
|
29
2
|
|
|
30
3
|
[![GitHub Release][releases-shield]][releases]
|
|
@@ -310,6 +283,136 @@ warning = WeatherWarning(
|
|
|
310
283
|
print(f"Warning for {warning.county}: {warning.description}")
|
|
311
284
|
```
|
|
312
285
|
|
|
286
|
+
### HydroStation
|
|
287
|
+
|
|
288
|
+
Represents a hydrological observation station with water body information.
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from meteo_lt import HydroStation
|
|
292
|
+
|
|
293
|
+
station = HydroStation(
|
|
294
|
+
code="klaipedos-juru-uosto-vms",
|
|
295
|
+
name="Klaipėdos jūrų uosto VMS",
|
|
296
|
+
water_body="Baltijos jūra",
|
|
297
|
+
coordinates=coords
|
|
298
|
+
)
|
|
299
|
+
print(f"Station: {station.name} on {station.water_body}")
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### HydroObservation
|
|
303
|
+
|
|
304
|
+
Represents a single hydrological observation with water measurements.
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from meteo_lt import HydroObservation
|
|
308
|
+
|
|
309
|
+
observation = HydroObservation(
|
|
310
|
+
observation_datetime="2024-07-23T12:00:00+00:00",
|
|
311
|
+
water_level=481.8,
|
|
312
|
+
water_temperature=15.5,
|
|
313
|
+
water_discharge=100.0
|
|
314
|
+
)
|
|
315
|
+
print(f"Water level: {observation.water_level} cm")
|
|
316
|
+
print(f"Water temperature: {observation.water_temperature}°C")
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### HydroObservationData
|
|
320
|
+
|
|
321
|
+
Represents a collection of hydrological observations for a specific station.
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
from meteo_lt import HydroObservationData
|
|
325
|
+
|
|
326
|
+
data = HydroObservationData(
|
|
327
|
+
station=station,
|
|
328
|
+
observations=[observation],
|
|
329
|
+
observations_data_range={"from": "2024-07-01", "to": "2024-07-23"}
|
|
330
|
+
)
|
|
331
|
+
print(f"Station: {data.station.name}")
|
|
332
|
+
print(f"Observations: {len(data.observations)}")
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Hydrological Data
|
|
336
|
+
|
|
337
|
+
The package provides access to hydrological data from Lithuanian water monitoring stations.
|
|
338
|
+
|
|
339
|
+
### Fetching Hydrological Stations
|
|
340
|
+
|
|
341
|
+
To get the list of all hydrological stations:
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
async def fetch_hydro_stations():
|
|
345
|
+
async with MeteoLtAPI() as api:
|
|
346
|
+
stations = await api.get_hydro_stations()
|
|
347
|
+
for station in stations:
|
|
348
|
+
print(f"{station.name} ({station.code}) - Water body: {station.water_body}")
|
|
349
|
+
|
|
350
|
+
asyncio.run(fetch_hydro_stations())
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Finding the Nearest Hydrological Station
|
|
354
|
+
|
|
355
|
+
You can find the nearest hydrological station using coordinates:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
async def find_nearest_hydro_station():
|
|
359
|
+
async with MeteoLtAPI() as api:
|
|
360
|
+
# Example coordinates for Klaipėda, Lithuania
|
|
361
|
+
nearest_station = await api.get_nearest_hydro_station(55.6872, 21.2797)
|
|
362
|
+
print(f"Nearest station: {nearest_station.name}")
|
|
363
|
+
print(f"Water body: {nearest_station.water_body}")
|
|
364
|
+
print(f"Distance: approximately {nearest_station.latitude}, {nearest_station.longitude}")
|
|
365
|
+
|
|
366
|
+
asyncio.run(find_nearest_hydro_station())
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Fetching Hydrological Observations
|
|
370
|
+
|
|
371
|
+
To get water level and temperature observations for a specific station:
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
async def fetch_hydro_observations():
|
|
375
|
+
async with MeteoLtAPI() as api:
|
|
376
|
+
# Get observations for a station
|
|
377
|
+
hydro_data = await api.get_hydro_observation_data("klaipedos-juru-uosto-vms")
|
|
378
|
+
|
|
379
|
+
print(f"Station: {hydro_data.station.name}")
|
|
380
|
+
print(f"Water body: {hydro_data.station.water_body}")
|
|
381
|
+
print(f"\nRecent observations:")
|
|
382
|
+
|
|
383
|
+
for observation in hydro_data.observations[:5]:
|
|
384
|
+
print(f" {observation.observation_datetime}")
|
|
385
|
+
print(f" Water level: {observation.water_level} cm")
|
|
386
|
+
print(f" Water temperature: {observation.water_temperature}°C")
|
|
387
|
+
if observation.water_discharge:
|
|
388
|
+
print(f" Water discharge: {observation.water_discharge} m³/s")
|
|
389
|
+
|
|
390
|
+
asyncio.run(fetch_hydro_observations())
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Getting Observations for Nearest Station
|
|
394
|
+
|
|
395
|
+
Combine location finding with observation fetching:
|
|
396
|
+
|
|
397
|
+
```python
|
|
398
|
+
async def get_nearest_station_observations():
|
|
399
|
+
async with MeteoLtAPI() as api:
|
|
400
|
+
# Find nearest hydro station to coordinates
|
|
401
|
+
nearest_station = await api.get_nearest_hydro_station(55.6872, 21.2797)
|
|
402
|
+
|
|
403
|
+
# Get observations for the nearest station
|
|
404
|
+
hydro_data = await api.get_hydro_observation_data(nearest_station.code)
|
|
405
|
+
|
|
406
|
+
print(f"Latest observations from {hydro_data.station.name}:")
|
|
407
|
+
if hydro_data.observations:
|
|
408
|
+
latest = hydro_data.observations[0]
|
|
409
|
+
print(f" Time: {latest.observation_datetime}")
|
|
410
|
+
print(f" Water level: {latest.water_level} cm")
|
|
411
|
+
print(f" Temperature: {latest.water_temperature}°C")
|
|
412
|
+
|
|
413
|
+
asyncio.run(get_nearest_station_observations())
|
|
414
|
+
```
|
|
415
|
+
|
|
313
416
|
## Contributing
|
|
314
417
|
|
|
315
418
|
Contributions are welcome! For major changes please open an issue to discuss or submit a pull request with your changes. If you want to contribute you can use devcontainers in vscode for easiest setup follow [instructions here](.devcontainer/README.md).
|
|
@@ -321,4 +424,4 @@ Contributions are welcome! For major changes please open an issue to discuss or
|
|
|
321
424
|
[license-shield]: https://img.shields.io/github/license/Brunas/meteo_lt-pkg.svg?style=flat-square
|
|
322
425
|
[maintenance-shield]: https://img.shields.io/badge/maintainer-Brunas%20%40Brunas-blue.svg?style=flat-square
|
|
323
426
|
[releases-shield]: https://img.shields.io/github/release/Brunas/meteo_lt-pkg.svg?style=flat-square
|
|
324
|
-
[releases]: https://github.com/Brunas/meteo_lt-pkg/releases
|
|
427
|
+
[releases]: https://github.com/Brunas/meteo_lt-pkg/releases
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""init.py"""
|
|
2
|
+
|
|
3
|
+
from .api import MeteoLtAPI
|
|
4
|
+
from .models import (
|
|
5
|
+
Coordinates,
|
|
6
|
+
LocationBase,
|
|
7
|
+
Place,
|
|
8
|
+
ForecastTimestamp,
|
|
9
|
+
Forecast,
|
|
10
|
+
WeatherWarning,
|
|
11
|
+
HydroStation,
|
|
12
|
+
HydroObservation,
|
|
13
|
+
HydroObservationData,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"MeteoLtAPI",
|
|
18
|
+
"Coordinates",
|
|
19
|
+
"LocationBase",
|
|
20
|
+
"Place",
|
|
21
|
+
"ForecastTimestamp",
|
|
22
|
+
"Forecast",
|
|
23
|
+
"WeatherWarning",
|
|
24
|
+
"HydroStation",
|
|
25
|
+
"HydroObservation",
|
|
26
|
+
"HydroObservationData",
|
|
27
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Main API class script"""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from .models import (
|
|
6
|
+
Forecast,
|
|
7
|
+
Place,
|
|
8
|
+
WeatherWarning,
|
|
9
|
+
HydroStation,
|
|
10
|
+
HydroObservationData,
|
|
11
|
+
)
|
|
12
|
+
from .utils import find_nearest_location
|
|
13
|
+
from .client import MeteoLtClient
|
|
14
|
+
from .warnings import WeatherWarningsProcessor
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MeteoLtAPI:
|
|
18
|
+
"""Main API class that orchestrates external API calls and warning processing"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, session=None):
|
|
21
|
+
self.places = []
|
|
22
|
+
self.client = MeteoLtClient(session)
|
|
23
|
+
self.warnings_processor = WeatherWarningsProcessor(self.client)
|
|
24
|
+
|
|
25
|
+
async def __aenter__(self):
|
|
26
|
+
"""Async context manager entry"""
|
|
27
|
+
await self.client.__aenter__()
|
|
28
|
+
return self
|
|
29
|
+
|
|
30
|
+
async def __aexit__(
|
|
31
|
+
self,
|
|
32
|
+
exc_type: Optional[type],
|
|
33
|
+
exc_val: Optional[Exception],
|
|
34
|
+
exc_tb: Optional[object],
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Async context manager exit"""
|
|
37
|
+
await self.client.__aexit__(exc_type, exc_val, exc_tb)
|
|
38
|
+
|
|
39
|
+
async def close(self):
|
|
40
|
+
"""Close the API client and cleanup resources"""
|
|
41
|
+
await self.client.close()
|
|
42
|
+
|
|
43
|
+
async def fetch_places(self) -> None:
|
|
44
|
+
"""Gets all places from API"""
|
|
45
|
+
self.places = await self.client.fetch_places()
|
|
46
|
+
|
|
47
|
+
async def get_nearest_place(self, latitude: float, longitude: float) -> Optional[Place]:
|
|
48
|
+
"""Finds nearest place using provided coordinates"""
|
|
49
|
+
if not self.places:
|
|
50
|
+
await self.fetch_places()
|
|
51
|
+
return find_nearest_location(latitude, longitude, self.places)
|
|
52
|
+
|
|
53
|
+
async def get_forecast_with_warnings(
|
|
54
|
+
self,
|
|
55
|
+
latitude: Optional[float] = None,
|
|
56
|
+
longitude: Optional[float] = None,
|
|
57
|
+
place_code: Optional[str] = None,
|
|
58
|
+
) -> Forecast:
|
|
59
|
+
"""Get forecast with weather warnings for a location"""
|
|
60
|
+
if place_code is None:
|
|
61
|
+
if latitude is None or longitude is None:
|
|
62
|
+
raise ValueError("Either place_code or both latitude and longitude must be provided")
|
|
63
|
+
place = await self.get_nearest_place(latitude, longitude)
|
|
64
|
+
place_code = place.code
|
|
65
|
+
|
|
66
|
+
return await self.get_forecast(place_code, include_warnings=True)
|
|
67
|
+
|
|
68
|
+
async def get_forecast(self, place_code: str, include_warnings: bool = True) -> Forecast:
|
|
69
|
+
"""Retrieves forecast data from API"""
|
|
70
|
+
forecast = await self.client.fetch_forecast(place_code)
|
|
71
|
+
|
|
72
|
+
if include_warnings:
|
|
73
|
+
await self._enrich_forecast_with_warnings(forecast)
|
|
74
|
+
|
|
75
|
+
return forecast
|
|
76
|
+
|
|
77
|
+
async def get_weather_warnings(self, administrative_division: str = None) -> List[WeatherWarning]:
|
|
78
|
+
"""Fetches weather warnings from meteo.lt JSON API"""
|
|
79
|
+
return await self.warnings_processor.get_weather_warnings(administrative_division)
|
|
80
|
+
|
|
81
|
+
async def _enrich_forecast_with_warnings(self, forecast: Forecast) -> None:
|
|
82
|
+
"""Enrich forecast timestamps with relevant weather warnings"""
|
|
83
|
+
if not forecast or not forecast.place or not forecast.place.administrative_division:
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
warnings = await self.get_weather_warnings(forecast.place.administrative_division)
|
|
87
|
+
|
|
88
|
+
if warnings:
|
|
89
|
+
self.warnings_processor.enrich_forecast_with_warnings(forecast, warnings)
|
|
90
|
+
|
|
91
|
+
async def get_hydro_stations(self) -> List[HydroStation]:
|
|
92
|
+
"""Get list of all hydrological stations"""
|
|
93
|
+
return await self.client.fetch_hydro_stations()
|
|
94
|
+
|
|
95
|
+
async def get_nearest_hydro_station(self, latitude: float, longitude: float) -> Optional[HydroStation]:
|
|
96
|
+
"""Find the nearest hydrological station to given coordinates"""
|
|
97
|
+
stations = await self.get_hydro_stations()
|
|
98
|
+
if not stations:
|
|
99
|
+
return None
|
|
100
|
+
return find_nearest_location(latitude, longitude, stations)
|
|
101
|
+
|
|
102
|
+
async def get_hydro_observation_data(
|
|
103
|
+
self,
|
|
104
|
+
station_code: str,
|
|
105
|
+
observation_type: str = "measured",
|
|
106
|
+
date: str = "latest",
|
|
107
|
+
) -> HydroObservationData:
|
|
108
|
+
"""Get hydrological observation data for a station"""
|
|
109
|
+
return await self.client.fetch_hydro_observation_data(station_code, observation_type, date)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""MeteoLt API client for external API calls"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import List, Optional, Dict, Any
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
8
|
+
from .models import (
|
|
9
|
+
Place,
|
|
10
|
+
Forecast,
|
|
11
|
+
HydroStation,
|
|
12
|
+
HydroObservationData,
|
|
13
|
+
HydroObservation,
|
|
14
|
+
)
|
|
15
|
+
from .const import BASE_URL, WARNINGS_URL, TIMEOUT, ENCODING
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MeteoLtClient:
|
|
19
|
+
"""Client for external API calls to meteo.lt"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, session: Optional[aiohttp.ClientSession] = None):
|
|
22
|
+
self._session = session
|
|
23
|
+
self._owns_session = session is None
|
|
24
|
+
|
|
25
|
+
async def __aenter__(self):
|
|
26
|
+
"""Async context manager entry"""
|
|
27
|
+
if self._session is None:
|
|
28
|
+
self._session = aiohttp.ClientSession(
|
|
29
|
+
timeout=aiohttp.ClientTimeout(total=TIMEOUT),
|
|
30
|
+
raise_for_status=True,
|
|
31
|
+
)
|
|
32
|
+
return self
|
|
33
|
+
|
|
34
|
+
async def __aexit__(
|
|
35
|
+
self,
|
|
36
|
+
exc_type: Optional[type],
|
|
37
|
+
exc_val: Optional[Exception],
|
|
38
|
+
exc_tb: Optional[Any],
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Async context manager exit"""
|
|
41
|
+
await self.close()
|
|
42
|
+
|
|
43
|
+
async def close(self) -> None:
|
|
44
|
+
"""Close the client session if we own it"""
|
|
45
|
+
if self._session and self._owns_session:
|
|
46
|
+
await self._session.close()
|
|
47
|
+
self._session = None
|
|
48
|
+
|
|
49
|
+
async def _get_session(self) -> aiohttp.ClientSession:
|
|
50
|
+
"""Get or create a session"""
|
|
51
|
+
if self._session is None:
|
|
52
|
+
self._session = aiohttp.ClientSession(
|
|
53
|
+
timeout=aiohttp.ClientTimeout(total=TIMEOUT),
|
|
54
|
+
raise_for_status=True,
|
|
55
|
+
)
|
|
56
|
+
return self._session
|
|
57
|
+
|
|
58
|
+
async def fetch_places(self) -> List[Place]:
|
|
59
|
+
"""Gets all places from API"""
|
|
60
|
+
session = await self._get_session()
|
|
61
|
+
async with session.get(f"{BASE_URL}/places") as response:
|
|
62
|
+
response.encoding = ENCODING
|
|
63
|
+
response_json = await response.json()
|
|
64
|
+
return [Place.from_dict(place) for place in response_json]
|
|
65
|
+
|
|
66
|
+
async def fetch_forecast(self, place_code: str) -> Forecast:
|
|
67
|
+
"""Retrieves forecast data from API"""
|
|
68
|
+
session = await self._get_session()
|
|
69
|
+
async with session.get(f"{BASE_URL}/places/{place_code}/forecasts/long-term") as response:
|
|
70
|
+
response.encoding = ENCODING
|
|
71
|
+
response_json = await response.json()
|
|
72
|
+
return Forecast.from_dict(response_json)
|
|
73
|
+
|
|
74
|
+
async def fetch_weather_warnings(self) -> Dict[str, Any]:
|
|
75
|
+
"""Fetches raw weather warnings data from meteo.lt JSON API"""
|
|
76
|
+
session = await self._get_session()
|
|
77
|
+
|
|
78
|
+
# Get the latest warnings file
|
|
79
|
+
async with session.get(WARNINGS_URL) as response:
|
|
80
|
+
file_list = await response.json()
|
|
81
|
+
|
|
82
|
+
if not file_list:
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
# Fetch the latest warnings data
|
|
86
|
+
latest_file_url = file_list[0] # First file is the most recent
|
|
87
|
+
async with session.get(latest_file_url) as response:
|
|
88
|
+
text_data = await response.text()
|
|
89
|
+
return json.loads(text_data)
|
|
90
|
+
|
|
91
|
+
async def fetch_hydro_stations(self) -> List[HydroStation]:
|
|
92
|
+
"""Get list of all hydrological stations."""
|
|
93
|
+
session = await self._get_session()
|
|
94
|
+
async with session.get(f"{BASE_URL}/hydro-stations") as resp:
|
|
95
|
+
if resp.status == 200:
|
|
96
|
+
resp.encoding = ENCODING
|
|
97
|
+
response = await resp.json()
|
|
98
|
+
stations = []
|
|
99
|
+
for station_data in response:
|
|
100
|
+
stations.append(HydroStation.from_dict(station_data))
|
|
101
|
+
return stations
|
|
102
|
+
raise aiohttp.ClientError(f"API returned status {resp.status}")
|
|
103
|
+
|
|
104
|
+
async def fetch_hydro_station(self, station_code: str) -> HydroStation:
|
|
105
|
+
"""Get information about a specific hydrological station."""
|
|
106
|
+
session = await self._get_session()
|
|
107
|
+
async with session.get(f"{BASE_URL}/hydro-stations/{station_code}") as resp:
|
|
108
|
+
if resp.status == 200:
|
|
109
|
+
resp.encoding = ENCODING
|
|
110
|
+
response = await resp.json()
|
|
111
|
+
return HydroStation.from_dict(response)
|
|
112
|
+
raise aiohttp.ClientError(f"API returned status {resp.status}")
|
|
113
|
+
|
|
114
|
+
async def fetch_hydro_observation_data(
|
|
115
|
+
self,
|
|
116
|
+
station_code: str,
|
|
117
|
+
observation_type: str = "measured",
|
|
118
|
+
date: str = "latest",
|
|
119
|
+
) -> HydroObservationData:
|
|
120
|
+
"""Get hydrological observation data for a station."""
|
|
121
|
+
session = await self._get_session()
|
|
122
|
+
async with session.get(
|
|
123
|
+
f"{BASE_URL}/hydro-stations/{station_code}/observations/{observation_type}/{date}"
|
|
124
|
+
) as resp:
|
|
125
|
+
if resp.status == 200:
|
|
126
|
+
response = await resp.json()
|
|
127
|
+
station = HydroStation.from_dict(response.get("station"))
|
|
128
|
+
|
|
129
|
+
observations = []
|
|
130
|
+
for obs_data in response.get("observations", []):
|
|
131
|
+
observations.append(HydroObservation.from_dict(obs_data))
|
|
132
|
+
|
|
133
|
+
return HydroObservationData(
|
|
134
|
+
station=station,
|
|
135
|
+
observations_data_range=response.get("observationsDataRange"),
|
|
136
|
+
observations=observations,
|
|
137
|
+
)
|
|
138
|
+
raise aiohttp.ClientError(f"API returned status {resp.status}")
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
"""const.py"""
|
|
2
2
|
|
|
3
3
|
BASE_URL = "https://api.meteo.lt/v1"
|
|
4
|
-
WARNINGS_URL =
|
|
5
|
-
"https://www.meteo.lt/app/mu-plugins/Meteo/Components/"
|
|
6
|
-
"WeatherWarningsNew/list_JSON.php"
|
|
7
|
-
)
|
|
4
|
+
WARNINGS_URL = "https://www.meteo.lt/app/mu-plugins/Meteo/Components/" "WeatherWarningsNew/list_JSON.php"
|
|
8
5
|
TIMEOUT = 30
|
|
9
6
|
ENCODING = "utf-8"
|
|
10
7
|
|