style: format code
This commit is contained in:
@@ -4,6 +4,7 @@ from bs4 import Tag
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class WidgetParser:
|
||||
def parse_widget(self, tag: Tag) -> Tag:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -36,9 +36,7 @@ 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"
|
||||
)
|
||||
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"])
|
||||
@@ -108,21 +106,15 @@ 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")
|
||||
]
|
||||
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"
|
||||
):
|
||||
for item in tag.select(".widget-row-wind > .row-item > .wind-speed > speed-value"):
|
||||
yield int(item.attrs["value"])
|
||||
|
||||
|
||||
@@ -152,22 +144,16 @@ class WindDirectionParser(RowParser[WindDirection]):
|
||||
}
|
||||
|
||||
def parse_row(self, tag: Tag) -> Iterable[float]:
|
||||
for item in tag.select(
|
||||
".widget-row-wind > .row-item > .wind-speed > .wind-direction"
|
||||
):
|
||||
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
|
||||
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"
|
||||
):
|
||||
for item in tag.select(".widget-row[data-row=precipitation-bars] > .row-item > .item-unit"):
|
||||
yield float(item.text.replace(",", "."))
|
||||
|
||||
|
||||
@@ -175,9 +161,7 @@ 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"
|
||||
):
|
||||
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")]
|
||||
|
||||
|
||||
|
||||
@@ -25,32 +25,20 @@ class MatchTvApi(ScheduleApi):
|
||||
ChannelId.MATCH_STRANA,
|
||||
]
|
||||
|
||||
async def get_channel_schedule(
|
||||
self, channel_id: ChannelId, date: datetime.date
|
||||
) -> Schedule:
|
||||
async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
|
||||
endpoint = f"tvguide/{channel_id}?date={date:%Y%m%d}"
|
||||
data = await self.SOURCE.request(endpoint)
|
||||
soup = BeautifulSoup(data, features="html.parser")
|
||||
values = []
|
||||
channel_name = (
|
||||
soup.select_one(".p-tv-guide-header__title")
|
||||
.text.replace("Телепрограмма ", "")
|
||||
.strip()
|
||||
)
|
||||
current_day = datetime.datetime.combine(
|
||||
date.today(), datetime.datetime.min.time()
|
||||
)
|
||||
channel_name = soup.select_one(".p-tv-guide-header__title").text.replace("Телепрограмма ", "").strip()
|
||||
current_day = datetime.datetime.combine(date.today(), datetime.datetime.min.time())
|
||||
end = current_day + datetime.timedelta(days=1, hours=6)
|
||||
prev_value: ScheduleValue | None = None
|
||||
for item in soup.select(
|
||||
".p-tv-guide-schedule-channel-carcass__transmissions .p-tv-guide-schedule-channel-transmission"
|
||||
):
|
||||
title = item.select_one(
|
||||
".p-tv-guide-schedule-channel-transmission__title"
|
||||
).text.strip()
|
||||
time_str = item.select_one(
|
||||
".p-tv-guide-schedule-channel-transmission__time-block"
|
||||
).text.strip()
|
||||
title = item.select_one(".p-tv-guide-schedule-channel-transmission__title").text.strip()
|
||||
time_str = item.select_one(".p-tv-guide-schedule-channel-transmission__time-block").text.strip()
|
||||
hours, minutes = map(int, time_str.split(":"))
|
||||
item_date = current_day.replace(hour=hours, minute=minutes)
|
||||
if prev_value is not None and item_date.hour < prev_value.start.hour:
|
||||
@@ -62,6 +50,4 @@ class MatchTvApi(ScheduleApi):
|
||||
if prev_value is not None:
|
||||
prev_value.end = item_date
|
||||
prev_value = value
|
||||
return Schedule(
|
||||
channel=Channel(id=channel_id, name=channel_name), date=date, values=values
|
||||
)
|
||||
return Schedule(channel=Channel(id=channel_id, name=channel_name), date=date, values=values)
|
||||
|
||||
@@ -55,10 +55,7 @@ class OpenWeatherApi(WeatherApi):
|
||||
value = FORECAST_ITEM_PARSER.parse(item)
|
||||
item_date = value.date.replace(hour=0, minute=0)
|
||||
values_by_date[item_date].append(value)
|
||||
values = [
|
||||
merge_weather_values(date, values)
|
||||
for date, values in values_by_date.items()
|
||||
]
|
||||
values = [merge_weather_values(date, values) for date, values in values_by_date.items()]
|
||||
return WeatherResponse(
|
||||
location=location_id,
|
||||
date=datetime.date.today(),
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
from gallery.sketch.mock import MockData
|
||||
|
||||
OPENWEATHER_MOCK_DATA = MockData(Path(__file__).parent / "data")
|
||||
@@ -75,9 +75,7 @@ class OpenWeather:
|
||||
self._source = ApiSource(self.BASE_URL)
|
||||
|
||||
async def get_forecast(self, lat: float, lon: float) -> Forecast:
|
||||
endpoint = (
|
||||
f"data/2.5/forecast?lat={lat}&lon={lon}&appid={self._api_key}&units=metric"
|
||||
)
|
||||
endpoint = f"data/2.5/forecast?lat={lat}&lon={lon}&appid={self._api_key}&units=metric"
|
||||
response = await self._source.request(endpoint)
|
||||
response_data = json.loads(response)
|
||||
return Forecast(**response_data)
|
||||
|
||||
@@ -24,11 +24,7 @@ class ForecastItemParser:
|
||||
|
||||
def parse(self, item: ForecastItem) -> WeatherValue:
|
||||
item_date = datetime.datetime.fromtimestamp(item.dt, datetime.UTC)
|
||||
item_date = (
|
||||
item_date.replace(tzinfo=datetime.timezone.utc)
|
||||
.astimezone(tz=None)
|
||||
.replace(tzinfo=None)
|
||||
)
|
||||
item_date = item_date.replace(tzinfo=datetime.timezone.utc).astimezone(tz=None).replace(tzinfo=None)
|
||||
value = build_weather_value(item_date)
|
||||
# TODO parse temperature interval flag
|
||||
value.temperature = [round(item.main.temp)]
|
||||
@@ -38,12 +34,8 @@ class ForecastItemParser:
|
||||
value.wind_speed = round(item.wind.speed)
|
||||
value.wind_gust = round(item.wind.gust)
|
||||
value.wind_direction = item.wind.deg
|
||||
value.sky.cloudness = self.CLOUDNESS_MAP.get(
|
||||
item.weather[0].description, Cloudness.CLEAR
|
||||
)
|
||||
value.sky.precipitation = self.PRECIPITATION_MAP.get(
|
||||
item.weather[0].description, Precipitation.NO
|
||||
)
|
||||
value.sky.cloudness = self.CLOUDNESS_MAP.get(item.weather[0].description, Cloudness.CLEAR)
|
||||
value.sky.precipitation = self.PRECIPITATION_MAP.get(item.weather[0].description, Precipitation.NO)
|
||||
if item.rain:
|
||||
value.precipitation = round(item.rain.interval_3h, 1)
|
||||
return value
|
||||
|
||||
@@ -60,9 +60,7 @@ class YandexTvApi(ScheduleApi):
|
||||
async def get_channels(self) -> list[ChannelId]:
|
||||
return list(CHANNELS_MAP.keys())
|
||||
|
||||
async def get_channel_schedule(
|
||||
self, channel_id: ChannelId, date: datetime.date
|
||||
) -> Schedule:
|
||||
async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
|
||||
endpoint = f"channel/{CHANNELS_MAP[channel_id]}?date={date:%Y-%m-%d}"
|
||||
data = await self.SOURCE.request(endpoint)
|
||||
soup = BeautifulSoup(data, features="html.parser")
|
||||
@@ -70,9 +68,7 @@ class YandexTvApi(ScheduleApi):
|
||||
raise RuntimeError("Captcha")
|
||||
values = []
|
||||
channel_name = soup.select_one(".channel-header__text").text.strip()
|
||||
current_day = datetime.datetime.combine(
|
||||
date.today(), datetime.datetime.min.time()
|
||||
)
|
||||
current_day = datetime.datetime.combine(date.today(), datetime.datetime.min.time())
|
||||
end = current_day + datetime.timedelta(days=1, hours=6)
|
||||
prev_value: ScheduleValue | None = None
|
||||
for item in soup.select(".channel-schedule .channel-schedule__event"):
|
||||
@@ -89,6 +85,4 @@ class YandexTvApi(ScheduleApi):
|
||||
if prev_value is not None:
|
||||
prev_value.end = item_date
|
||||
prev_value = value
|
||||
return Schedule(
|
||||
channel=Channel(id=channel_id, name=channel_name), date=date, values=values
|
||||
)
|
||||
return Schedule(channel=Channel(id=channel_id, name=channel_name), date=date, values=values)
|
||||
|
||||
Reference in New Issue
Block a user