207 lines
6.6 KiB
Python
207 lines
6.6 KiB
Python
import datetime
|
||
import re
|
||
from typing import Iterable
|
||
|
||
import dateparser
|
||
from bs4 import Tag
|
||
|
||
from gallery.sketch.weather.model import (
|
||
Cloudness,
|
||
Precipitation,
|
||
Sky,
|
||
WindDirection,
|
||
WindDirectionDeg,
|
||
)
|
||
|
||
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.HEAVY_RAIN,
|
||
"дождь": Precipitation.RAIN,
|
||
"ливень": Precipitation.SHOWER,
|
||
"снег": Precipitation.SNOW,
|
||
"небольшой снег": Precipitation.SNOW,
|
||
"сильный снег": Precipitation.HEAVY_SNOW,
|
||
"мокрый снег": Precipitation.SNOW,
|
||
"снег с дождём": Precipitation.SNOW,
|
||
"сильный снег с дождём": Precipitation.HEAVY_SNOW,
|
||
"небольшой снег с дождём": Precipitation.SNOW,
|
||
"небольшой мокрый снег": Precipitation.SNOW,
|
||
}
|
||
|
||
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-wind > .row-item > .wind-speed > 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-wind > .row-item > .wind-gust"):
|
||
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.CALM,
|
||
"с": WindDirection.N,
|
||
"св": WindDirection.NE,
|
||
"в": WindDirection.E,
|
||
"юв": WindDirection.SE,
|
||
"ю": WindDirection.S,
|
||
"юз": WindDirection.SW,
|
||
"з": WindDirection.W,
|
||
"сз": WindDirection.NW,
|
||
}
|
||
|
||
def parse_row(self, tag: Tag) -> Iterable[float]:
|
||
for item in tag.select(
|
||
".widget-row-wind > .row-item > .wind-speed > .wind-direction"
|
||
):
|
||
wind_direction_str = item.text.lower().strip()
|
||
yield WindDirectionDeg.from_direction(
|
||
self.WIND_DIRECTION_MAP[wind_direction_str]
|
||
).value
|
||
|
||
|
||
class PrecipitationParser(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, .widget-row[data-row=humidity-avg] > .row-item"
|
||
):
|
||
yield int(item.text)
|
||
|
||
|
||
ROW_PARSERS: list[RowParser] = [
|
||
DateParser(),
|
||
SkyParser(),
|
||
TemperatureParser(),
|
||
WindSpeedParser(),
|
||
WindGustParser(),
|
||
WindDirectionParser(),
|
||
PrecipitationParser(),
|
||
PressureParser(),
|
||
HumidityParser(),
|
||
]
|
||
|
||
ROW_PARSERS_MAP: dict[str, RowParser] = {parser.KEY: parser for parser in ROW_PARSERS}
|