import datetime import re from typing import Iterable import dateparser from bs4 import Tag from weather.model import Cloudness, Precipitation, Sky, WindDirection from .core import BaseWidgetParser, RowParser ONE_DAY_PARSER = BaseWidgetParser(".widget.widget-oneday .widget-items") DAYS_PARSER = BaseWidgetParser(".widget.widget-days .widget-items") class LocationParser: PATTERN = re.compile('{"ru":{"city":{"name":"(.*?)"') def parse_location(self, data: str) -> str | None: match = self.PATTERN.search(data) if match: return match.group(1) return None LOCATION_PARSER = LocationParser() class DateParser(RowParser[datetime.datetime]): KEY = "date" def parse_row(self, tag: Tag) -> Iterable[datetime.datetime]: datetime_date_tag = tag.select_one( ".widget-row.widget-row-datetime-date > .row-item" ) if datetime_date_tag: date_str = datetime_date_tag.find(text=True, recursive=False).text date = dateparser.parse(date_str, languages=["ru"]) for item in tag.select(".widget-row.widget-row-datetime-time > .row-item"): time_str = item.text time = dateparser.parse(time_str, languages=["ru"]) time = time.replace(year=date.year, month=date.month, day=date.day) yield time else: for item in tag.select(".widget-row.widget-row-date > .row-item"): date_str = item.text date = dateparser.parse(date_str, languages=["ru"]) yield date class SkyParser(RowParser[Sky]): KEY = "sky" CLOUDNESS_MAP: dict[str, Cloudness] = { "ясно": Cloudness.CLEAR, "малооблачно": Cloudness.PARTLY_CLOUDY, "облачно": Cloudness.CLOUDY, "пасмурно": Cloudness.MAINLY_CLOUDY, } PRECIPITATION_MAP: dict[str, Precipitation] = { "без осадков": Precipitation.NO, "небольшой дождь": Precipitation.SMALL_RAIN, "дождь": Precipitation.RAIN, "ливень": Precipitation.SHOWER, } def parse_row(self, tag: Tag) -> Iterable[Sky]: for item in tag.select(".widget-row[data-row=icon-tooltip] > .row-item"): sky_str = item.attrs["data-tooltip"] values = {item.strip().lower() for item in sky_str.split(",")} cloudness = Cloudness.CLEAR precipitation = Precipitation.NO thunder = "гроза" in values fog = "дымка" in values for k, v in self.CLOUDNESS_MAP.items(): if k in values: cloudness = v break for k, v in self.PRECIPITATION_MAP.items(): if k in values: precipitation = v break yield Sky( cloudness=cloudness, precipitation=precipitation, thunder=thunder, fog=fog, ) class TemperatureParser(RowParser[list[int]]): KEY = "temperature" def parse_row(self, tag: Tag) -> Iterable[list[int]]: for item in tag.select( ".widget-row-chart[data-row=temperature-air] > .chart > .values > .value" ): yield [ int(value.attrs["value"]) for value in item.select("temperature-value") ] class WindSpeedParser(RowParser[int]): KEY = "wind_speed" def parse_row(self, tag: Tag) -> Iterable[int]: for item in tag.select( ".widget-row[data-row=wind-speed] > .row-item > speed-value" ): yield int(item.attrs["value"]) class WindGustParser(RowParser[int]): KEY = "wind_gust" def parse_row(self, tag: Tag) -> Iterable[int]: for item in tag.select(".widget-row[data-row=wind-gust] > .row-item"): value = item.select_one("speed-value") yield int(value.attrs["value"]) if value else 0 class WindDirectionParser(RowParser[WindDirection]): KEY = "wind_direction" WIND_DIRECTION_MAP: dict[str, WindDirection] = { "штиль": WindDirection.CALM, "с": WindDirection.N, "св": WindDirection.NO, "в": WindDirection.O, "юв": WindDirection.SO, "ю": WindDirection.S, "юз": WindDirection.SW, "з": WindDirection.W, "сз": WindDirection.NW, } def parse_row(self, tag: Tag) -> Iterable[WindDirection]: for item in tag.select( ".widget-row[data-row=wind-direction] > .row-item > .direction" ): wind_direction_str = item.text.lower() yield self.WIND_DIRECTION_MAP[wind_direction_str] class WindPrecipitationParser(RowParser[float]): KEY = "precipitation" def parse_row(self, tag: Tag) -> Iterable[float]: for item in tag.select( ".widget-row[data-row=precipitation-bars] > .row-item > .item-unit" ): yield float(item.text.replace(",", ".")) class PressureParser(RowParser[list[int]]): KEY = "pressure" def parse_row(self, tag: Tag) -> Iterable[list[int]]: for item in tag.select( ".widget-row-chart[data-row=pressure] > .chart > .values > .value" ): yield [int(value.attrs["value"]) for value in item.select("pressure-value")] class HumidityParser(RowParser[int]): KEY = "humidity" def parse_row(self, tag: Tag) -> Iterable[int]: for item in tag.select(".widget-row[data-row=humidity] > .row-item"): yield int(item.text) ROW_PARSERS: list[RowParser] = [ DateParser(), SkyParser(), TemperatureParser(), WindSpeedParser(), WindGustParser(), WindDirectionParser(), WindPrecipitationParser(), PressureParser(), HumidityParser(), ] ROW_PARSERS_MAP: dict[str, RowParser] = {parser.KEY: parser for parser in ROW_PARSERS}