feat(weather): wehaer value sky model instead of text cloudness
This commit is contained in:
@@ -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/
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user