Files
gallery/gallery/painting/yandexweather/parser.py
T

188 lines
5.1 KiB
Python

import datetime
from typing import Any
from bs4 import Tag
from gallery.sketch.parse.core import ParseError
from gallery.sketch.parse.date import DATE_PARSER
from gallery.sketch.parse.html import (
HTMLParser,
TableParser,
TableValueParser,
TagParser,
)
from gallery.sketch.parse.json import JSONParser
from gallery.sketch.weather.model import (
Cloudness,
Location,
Precipitation,
Sky,
WeatherResponse,
WeatherValue,
WindDirection,
)
from gallery.sketch.weather.parse.wind import WIND_DIRECTION_PARSER
from . import PROVIDER
class LocationNameParser(TagParser[str]):
selector = "[class^=Suggest_placeholder]"
def extract(self, data: Tag) -> str:
return data.text.strip()
class DateParser(TagParser[datetime.date]):
selector = "[class^=AppForecastDayHeader_dayTitle]"
def extract(self, data: Tag) -> datetime.date:
return DATE_PARSER.parse(data.text.split(",")[-1].strip())
class TimeParser(TableValueParser[datetime.datetime]):
selector = "[style$='-part']"
model_key = "date"
HOUR_MAP = {
"Утром": 6,
"Днём": 12,
"Вечером": 18,
"Ночью": 0,
}
def extract(self, data: Tag) -> datetime.datetime:
hour = self.HOUR_MAP[data.text.strip()]
return datetime.datetime.min.replace(hour=hour)
class TemperatureParser(TableValueParser[list[int]]):
selector = "[style$='-temp']"
model_key = "temperature"
def extract(self, data: Tag) -> list[int]:
return [int(data.text.strip().rstrip("°"))]
class SkyParser(TableValueParser[Sky]):
selector = "[style$='-text']"
model_key = "sky"
CLOUDNESS_MAP = {
"ясно": Cloudness.CLEAR,
"облачно с прояснениями": Cloudness.CLOUDY,
"малооблачно": Cloudness.CLOUDY,
"пасмурно": Cloudness.MAINLY_CLOUDY,
}
PRECIPITATION_MAP = {
"небольшой дождь": Precipitation.SMALL_RAIN,
"дождь": Precipitation.RAIN,
"ливень": Precipitation.SHOWER,
}
def extract(self, data: Tag) -> Sky:
sky = data.text.strip()
cloudness = Cloudness.CLEAR
precipitation = Precipitation.NO
thunder = False
fog = False
if sky in self.CLOUDNESS_MAP:
cloudness = self.CLOUDNESS_MAP[sky]
elif sky in self.PRECIPITATION_MAP:
precipitation = self.PRECIPITATION_MAP[sky]
else:
raise ParseError("Unknown sky value", sky)
return Sky(
cloudness=cloudness,
precipitation=precipitation,
thunder=thunder,
fog=fog,
)
class WindSpeedParser(TableValueParser[int]):
selector = "[style$='-wind']:not([class*=AppForecastDayPart_wind_formatted])"
model_key = "wind.speed"
def extract(self, data: Tag) -> int:
return int(data.text.strip())
class WindDirectionParser(TableValueParser[WindDirection | None]):
selector = "[style$='-dir']"
model_key = "wind.direction"
def extract(self, data: Tag) -> WindDirection | None:
return WIND_DIRECTION_PARSER.parse(data.text.strip())
class HumidityParser(TableValueParser[int]):
selector = "[style$='-hum']"
model_key = "humidity"
def extract(self, data: Tag) -> int:
return int(data.text.strip().rstrip("%"))
class PressureParser(TableValueParser[list[int]]):
selector = "[style$='-press']"
model_key = "pressure"
def extract(self, data: Tag) -> list[int]:
return [int(data.text.strip().rstrip("%"))]
class YandexTableParser(TableParser[WeatherValue]):
selector = ""
value_parsers = [
TimeParser(),
TemperatureParser(),
SkyParser(),
WindSpeedParser(),
WindDirectionParser(),
HumidityParser(),
PressureParser(),
]
def create_model(self, data: dict) -> WeatherValue:
return WeatherValue.model_validate(data)
class YandexForecastParser(HTMLParser[list[WeatherResponse]]):
table_parser = YandexTableParser()
date_parser = DateParser()
location_parser = LocationNameParser()
def extract(self, data: Tag) -> list[WeatherResponse]:
location = self.location_parser.parse(data)
articles = data.select("article[class^=AppForecastDay]")
return [
WeatherResponse(
location=location,
date=self.date_parser.parse(item),
period="day",
values=self.table_parser.parse(item),
)
for item in articles
]
class YandexLocationParser(JSONParser[list[Location]]):
def extract(self, data: Any) -> list[Location]:
return [
Location(
id=str(item["geoId"]),
name=item["title"],
provider=PROVIDER,
lat=item["coords"]["lat"],
lon=item["coords"]["lon"],
country="ru",
country_code="ru",
district=item["description"],
subdistrict="",
)
for item in data
]