feat(weather): wehaer value sky model instead of text cloudness

This commit is contained in:
2024-07-26 18:25:15 +03:00
parent f711b2d77b
commit 48a6cce569
6 changed files with 135 additions and 47 deletions

View File

@@ -16,6 +16,7 @@ RUN apt update && \
dpkg-reconfigure --frontend=noninteractive locales dpkg-reconfigure --frontend=noninteractive locales
ENV LANG=ru_RU.UTF-8 ENV LANG=ru_RU.UTF-8
ENV LC_ALL=ru_RU.UTF-8 ENV LC_ALL=ru_RU.UTF-8
ENV TZ="Europe/Moscow"
COPY --from=builder /app ./ COPY --from=builder /app ./
COPY gismeteo gismeteo/ COPY gismeteo gismeteo/
COPY weather weather/ COPY weather weather/

View File

@@ -10,7 +10,7 @@ class LocationValue(NamedTuple):
@classmethod @classmethod
def parse(cls, source: str) -> "LocationValue": def parse(cls, source: str) -> "LocationValue":
location, name = source.split("-") name, location = source.split("-")
return cls(int(location), name) return cls(int(location), name)

View File

@@ -5,6 +5,8 @@ from typing import Dict, Iterable, List, Optional
import dateparser import dateparser
from bs4 import Tag from bs4 import Tag
from weather.model import Cloudness, Precipitation, Sky, WindDirection
from .core import BaseWidgetParser, RowParser from .core import BaseWidgetParser, RowParser
ONE_DAY_PARSER = BaseWidgetParser(".widget.widget-oneday .widget-items") ONE_DAY_PARSER = BaseWidgetParser(".widget.widget-oneday .widget-items")
@@ -40,12 +42,45 @@ class DateParser(RowParser[datetime.datetime]):
yield time yield time
class CloudnessParser(RowParser[str]): class SkyParser(RowParser[Sky]):
KEY = "cloudness" KEY = "sky"
def parse_row(self, tag: Tag) -> Iterable[str]: 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"): for item in tag.select(".widget-row[data-row=icon-tooltip] > .row-item"):
yield item.attrs["data-tooltip"] 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[int]): class TemperatureParser(RowParser[int]):
@@ -77,14 +112,27 @@ class WindGustParser(RowParser[int]):
yield int(value.attrs["value"]) if value else 0 yield int(value.attrs["value"]) if value else 0
class WindDirectionParser(RowParser[str]): class WindDirectionParser(RowParser[WindDirection]):
KEY = "wind_direction" KEY = "wind_direction"
def parse_row(self, tag: Tag) -> Iterable[str]: 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( for item in tag.select(
".widget-row[data-row=wind-direction] > .row-item > .direction" ".widget-row[data-row=wind-direction] > .row-item > .direction"
): ):
yield item.text wind_direction_str = item.text.lower()
yield self.WIND_DIRECTION_MAP[wind_direction_str]
class WindPrecipitationParser(RowParser[float]): class WindPrecipitationParser(RowParser[float]):
@@ -117,7 +165,7 @@ class HumidityParser(RowParser[int]):
ROW_PARSERS: List[RowParser] = [ ROW_PARSERS: List[RowParser] = [
DateParser(), DateParser(),
CloudnessParser(), SkyParser(),
TemperatureParser(), TemperatureParser(),
WindSpeedParser(), WindSpeedParser(),
WindGustParser(), WindGustParser(),

View File

@@ -1,21 +1,60 @@
import datetime import datetime
from enum import Enum
from pydantic import BaseModel from pydantic import BaseModel
class WeatherValue(BaseModel): class Model(BaseModel):
class Config:
use_enum_values = True
class Cloudness(str, Enum):
CLEAR = "clear"
PARTLY_CLOUDY = "party_cloudy"
CLOUDY = "cloudy"
MAINLY_CLOUDY = "mainly_cloudy"
class Precipitation(str, Enum):
NO = "no"
SMALL_RAIN = "small_rain"
RAIN = "rain"
SHOWER = "shower"
class Sky(Model):
cloudness: Cloudness
precipitation: Precipitation
thunder: bool
fog: bool
class WindDirection(str, Enum):
CALM = "calm"
N = "N"
NO = "NO"
O = "O"
SO = "SO"
S = "S"
SW = "SW"
W = "W"
NW = "NW"
class WeatherValue(Model):
date: datetime.datetime date: datetime.datetime
cloudness: str sky: Sky
temperature: int temperature: int
wind_speed: int wind_speed: int
wind_gust: int wind_gust: int
wind_direction: str wind_direction: WindDirection
precipitation: float precipitation: float
pressure: int pressure: int
humidity: int humidity: int
class WeatherResponse(BaseModel): class WeatherResponse(Model):
location: str location: str
date: datetime.date date: datetime.date
period: str period: str

View File

@@ -1,38 +1,39 @@
def wind_direction_icon(wind_direction: str) -> str: from weather.model import Cloudness, Precipitation, Sky, WindDirection
def wind_direction_icon(wind_direction: WindDirection) -> str:
return { return {
"С": "🡫", WindDirection.N: "🡫",
"СВ": "🡯", WindDirection.NO: "🡯",
"В": "🡨", WindDirection.O: "🡨",
"ЮВ": "🡬", WindDirection.SO: "🡬",
"Ю": "🡡", WindDirection.S: "🡡",
"ЮЗ": "🡭", WindDirection.SW: "🡭",
"З": "🡪", WindDirection.W: "🡪",
"СЗ": "🡦", WindDirection.NW: "🡦",
"штиль": "", WindDirection.CALM: "",
}.get(wind_direction, wind_direction) }.get(wind_direction, wind_direction)
def cloudness_icon(cloudness: str) -> list[str]: def cloudness_icon(sky: Sky) -> list[str]:
icons = [] main_icon = ""
values = {item.strip().lower() for item in cloudness.split(",")} if sky.thunder:
if "дымка" in values: if sky.cloudness == Cloudness.CLEAR:
main_icon = "🌩️"
if sky.precipitation == Precipitation.NO:
main_icon = ""
else:
main_icon = "⛈️"
elif sky.precipitation == Precipitation.NO:
main_icon = {
Cloudness.CLEAR: "☀️",
Cloudness.PARTLY_CLOUDY: "🌤️",
Cloudness.CLOUDY: "",
Cloudness.MAINLY_CLOUDY: "☁️",
}[sky.cloudness]
else:
main_icon = "🌧️"
icons = [main_icon]
if sky.fog:
icons.append("🌫️") icons.append("🌫️")
values.remove("дымка")
icons = [
{
frozenset(["ясно"]): "☀️",
frozenset(["малооблачно", "без осадков"]): "🌤️",
frozenset(["облачно", "без осадков"]): "",
frozenset(["малооблачно", "небольшой дождь"]): "🌦️",
frozenset(["пасмурно", "без осадков"]): "☁️",
frozenset(["облачно", "небольшой дождь"]): "🌧️",
frozenset(["облачно", "дождь"]): "🌧️",
frozenset(["облачно", "небольшой дождь", "гроза"]): "⛈️",
frozenset(["малооблачно", "дождь"]): "🌦️",
frozenset(["пасмурно", "небольшой дождь"]): "🌧️",
frozenset(["облачно", "дождь", "гроза"]): "⛈️",
frozenset(["пасмурно", "дождь"]): "🌧️",
frozenset(["пасмурно", "дождь", "гроза"]): "⛈️",
}.get(frozenset(values), "|".join(values))
] + icons
return icons return icons

View File

@@ -44,7 +44,7 @@
<tr> <tr>
{% for value in response.values %} {% for value in response.values %}
<td class="cloudness"> <td class="cloudness">
{% for icon in value.cloudness | cloudness_icon %} {% for icon in value.sky | cloudness_icon %}
<div class="icon">{{icon}}</div> <div class="icon">{{icon}}</div>
{% endfor %} {% endfor %}
</td> </td>
@@ -76,7 +76,6 @@
{% for value in response.values %} {% for value in response.values %}
<td class="wind"> <td class="wind">
<span class="icon">{{value.wind_direction | wind_direction_icon}}</span> <span class="icon">{{value.wind_direction | wind_direction_icon}}</span>
<span class="direction">{{value.wind_direction}}</span>
</td> </td>
{% endfor %} {% endfor %}
</tr> </tr>