feat(openweather): add location name to forecast response
This commit is contained in:
@@ -12,6 +12,7 @@ services:
|
|||||||
build: .
|
build: .
|
||||||
environment:
|
environment:
|
||||||
- REDIS_HOST=redis
|
- REDIS_HOST=redis
|
||||||
|
- OPENWEATHER_KEY=$OPENWEATHER_KEY
|
||||||
- DEBUG=1
|
- DEBUG=1
|
||||||
ports:
|
ports:
|
||||||
- 8000:80
|
- 8000:80
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ services:
|
|||||||
image: ${DOCKER_ROOT}/gallery
|
image: ${DOCKER_ROOT}/gallery
|
||||||
environment:
|
environment:
|
||||||
- REDIS_HOST=redis
|
- REDIS_HOST=redis
|
||||||
|
- OPENWEATHER_KEY=$OPENWEATHER_KEY
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ from gallery.sketch.weather.model import Location, WeatherResponse, WeatherValue
|
|||||||
from gallery.sketch.weather.util import merge_weather_values
|
from gallery.sketch.weather.util import merge_weather_values
|
||||||
from gallery.util import TimeUnit
|
from gallery.util import TimeUnit
|
||||||
|
|
||||||
from .openweather import Forecast, OpenWeather
|
from .openweather import Forecast
|
||||||
|
from .openweather import Location as OpenWeatherLocation
|
||||||
|
from .openweather import OpenWeather
|
||||||
from .parser import FORECAST_ITEM_PARSER
|
from .parser import FORECAST_ITEM_PARSER
|
||||||
|
|
||||||
logger = logging.getLogger("openweather")
|
logger = logging.getLogger("openweather")
|
||||||
@@ -24,6 +26,14 @@ class OpenWeatherApi(WeatherApi):
|
|||||||
def _parse_location(cls, location_id: str) -> tuple[float, float]:
|
def _parse_location(cls, location_id: str) -> tuple[float, float]:
|
||||||
return tuple(map(float, location_id.split(":", maxsplit=2)))
|
return tuple(map(float, location_id.split(":", maxsplit=2)))
|
||||||
|
|
||||||
|
@cached(
|
||||||
|
key_builder=lambda fun, self, location_id: f"api.weather.{self.provider}.source.{location_id}.location",
|
||||||
|
alias="redis",
|
||||||
|
ttl=TimeUnit.DAY,
|
||||||
|
)
|
||||||
|
async def _get_location(self, location_id: str) -> OpenWeatherLocation:
|
||||||
|
return await self.SOURCE.get_location(*self._parse_location(location_id))
|
||||||
|
|
||||||
@cached(
|
@cached(
|
||||||
key_builder=lambda fun, self, location_id: f"api.weather.{self.provider}.source.{location_id}.forecast",
|
key_builder=lambda fun, self, location_id: f"api.weather.{self.provider}.source.{location_id}.forecast",
|
||||||
alias="redis",
|
alias="redis",
|
||||||
@@ -33,7 +43,7 @@ class OpenWeatherApi(WeatherApi):
|
|||||||
return await self.SOURCE.get_forecast(*self._parse_location(location_id))
|
return await self.SOURCE.get_forecast(*self._parse_location(location_id))
|
||||||
|
|
||||||
async def find_locations(self, query: str) -> list[Location]:
|
async def find_locations(self, query: str) -> list[Location]:
|
||||||
result = await self.SOURCE.get_locations(query)
|
result = await self.SOURCE.find_locations(query)
|
||||||
return [
|
return [
|
||||||
Location(
|
Location(
|
||||||
id=f"{item.lat}:{item.lon}",
|
id=f"{item.lat}:{item.lon}",
|
||||||
@@ -50,6 +60,7 @@ class OpenWeatherApi(WeatherApi):
|
|||||||
]
|
]
|
||||||
|
|
||||||
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:
|
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:
|
||||||
|
location: OpenWeatherLocation = await self._get_location(location_id)
|
||||||
data: Forecast = await self._get_location_forecast(location_id)
|
data: Forecast = await self._get_location_forecast(location_id)
|
||||||
values = []
|
values = []
|
||||||
for item in data.list:
|
for item in data.list:
|
||||||
@@ -57,13 +68,14 @@ class OpenWeatherApi(WeatherApi):
|
|||||||
if value.date.date() == date:
|
if value.date.date() == date:
|
||||||
values.append(value)
|
values.append(value)
|
||||||
return WeatherResponse(
|
return WeatherResponse(
|
||||||
location=location_id,
|
location=location.name,
|
||||||
date=date,
|
date=date,
|
||||||
period="day",
|
period="day",
|
||||||
values=values,
|
values=values,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_days(self, location_id: str, days: int) -> WeatherResponse:
|
async def get_days(self, location_id: str, days: int) -> WeatherResponse:
|
||||||
|
location: OpenWeatherLocation = await self._get_location(location_id)
|
||||||
data: Forecast = await self._get_location_forecast(location_id)
|
data: Forecast = await self._get_location_forecast(location_id)
|
||||||
values_by_date: dict[datetime.datetime, list[WeatherValue]] = defaultdict(list)
|
values_by_date: dict[datetime.datetime, list[WeatherValue]] = defaultdict(list)
|
||||||
for item in data.list:
|
for item in data.list:
|
||||||
@@ -72,7 +84,7 @@ class OpenWeatherApi(WeatherApi):
|
|||||||
values_by_date[item_date].append(value)
|
values_by_date[item_date].append(value)
|
||||||
values = [merge_weather_values(date, values) for date, values in values_by_date.items()]
|
values = [merge_weather_values(date, values) for date, values in values_by_date.items()]
|
||||||
return WeatherResponse(
|
return WeatherResponse(
|
||||||
location=location_id,
|
location=location.name,
|
||||||
date=datetime.date.today(),
|
date=datetime.date.today(),
|
||||||
period="days",
|
period="days",
|
||||||
values=list(sorted(values, key=lambda item: item.date)),
|
values=list(sorted(values, key=lambda item: item.date)),
|
||||||
|
|||||||
@@ -89,8 +89,15 @@ class OpenWeather:
|
|||||||
response_data = json.loads(response)
|
response_data = json.loads(response)
|
||||||
return Forecast.model_validate(response_data)
|
return Forecast.model_validate(response_data)
|
||||||
|
|
||||||
async def get_locations(self, query: str, limit: int = 5) -> list[Location]:
|
async def find_locations(self, query: str, limit: int = 5) -> list[Location]:
|
||||||
endpoint = f"geo/1.0/direct?q={query}&limit={limit}&appid={self._api_key}"
|
endpoint = f"geo/1.0/direct?q={query}&limit={limit}&appid={self._api_key}"
|
||||||
response = await self._source.request(endpoint)
|
response = await self._source.request(endpoint)
|
||||||
response_data = json.loads(response)
|
response_data = json.loads(response)
|
||||||
return [Location.model_validate(item) for item in response_data]
|
return [Location.model_validate(item) for item in response_data]
|
||||||
|
|
||||||
|
async def get_location(self, lat: float, lon: float) -> Location:
|
||||||
|
limit = 1
|
||||||
|
endpoint = f"geo/1.0/reverse?lat={lat}&lon={lon}&limit={limit}&appid={self._api_key}"
|
||||||
|
response = await self._source.request(endpoint)
|
||||||
|
response_data = json.loads(response)
|
||||||
|
return Location.model_validate(response_data[0])
|
||||||
|
|||||||
Reference in New Issue
Block a user