style: format code

This commit is contained in:
2026-06-12 00:16:15 +03:00
parent 91e2c9d123
commit f368e6717c
28 changed files with 121 additions and 166 deletions

View File

@@ -34,6 +34,8 @@
"request": "launch", "request": "launch",
"module": "uvicorn", "module": "uvicorn",
"args": ["gallery.main:app", "--reload", "--log-config", "gallery/logging.yaml"], "args": ["gallery.main:app", "--reload", "--log-config", "gallery/logging.yaml"],
"justMyCode": true,
"consoleTitle": "gallery:app",
}, },
{ {
"name": "gallery:static", "name": "gallery:static",
@@ -41,6 +43,7 @@
"request": "launch", "request": "launch",
"type": "node-terminal", "type": "node-terminal",
"command": "npm run dev", "command": "npm run dev",
"consoleTitle": "gallery:static",
}, },
], ],
}, },

View File

@@ -13,8 +13,6 @@ def mount(app: FastAPI):
return await schedule_api.get_channels() return await schedule_api.get_channels()
@app.get("/api/schedule/{channel}/{date}", tags=["API"]) @app.get("/api/schedule/{channel}/{date}", tags=["API"])
async def get_api_schedule_channel_schedule( async def get_api_schedule_channel_schedule(request: AppRequest, channel: str, date: datetime.date) -> Schedule:
request: AppRequest, channel: str, date: datetime.date
) -> Schedule:
schedule_api = request.app.state.api.schedule schedule_api = request.app.state.api.schedule
return await schedule_api.get_channel_schedule(ChannelId(channel), date) return await schedule_api.get_channel_schedule(ChannelId(channel), date)

View File

@@ -8,22 +8,16 @@ from gallery.sketch.weather.model import Location, WeatherResponse
def mount(app: FastAPI): def mount(app: FastAPI):
@app.get("/api/weather/locations", tags=["API"]) @app.get("/api/weather/locations", tags=["API"])
async def get_api_weather_locations( async def get_api_weather_locations(request: AppRequest, query: str) -> list[Location]:
request: AppRequest, query: str
) -> list[Location]:
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
return await weather_api.find_locations(query) return await weather_api.find_locations(query)
@app.get("/api/weather/{location}/day/{date}", tags=["API"]) @app.get("/api/weather/{location}/day/{date}", tags=["API"])
async def get_api_weather_day( async def get_api_weather_day(request: AppRequest, location: str, date: datetime.date) -> WeatherResponse:
request: AppRequest, location: str, date: datetime.date
) -> WeatherResponse:
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
return await weather_api.get_day(location, date) return await weather_api.get_day(location, date)
@app.get("/api/weather/{location}/days/{days}", tags=["API"]) @app.get("/api/weather/{location}/days/{days}", tags=["API"])
async def get_api_weather_days( async def get_api_weather_days(request: AppRequest, location: str, days: int) -> WeatherResponse:
request: AppRequest, location: str, days: int
) -> WeatherResponse:
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
return await weather_api.get_days(location, days) return await weather_api.get_days(location, days)

View File

@@ -3,11 +3,8 @@ from typing import NamedTuple
from fastapi import APIRouter, Request from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from gallery.version import __version__ from ..common.utils.template import build_templates
from ..translation import _
class Section(NamedTuple): class Section(NamedTuple):
@@ -25,8 +22,7 @@ base_dir = Path(__file__).parent
router = APIRouter() router = APIRouter()
templates = Jinja2Templates(directory=base_dir / "templates") templates = build_templates()
templates.env.globals.update({"_": _})
@router.get("/", response_class=HTMLResponse) @router.get("/", response_class=HTMLResponse)
@@ -35,7 +31,6 @@ async def get_section_list(request: Request):
request=request, request=request,
name="root_index.html", name="root_index.html",
context={ context={
"version": __version__,
"sections": SECTIONS, "sections": SECTIONS,
}, },
) )

View File

@@ -112,7 +112,7 @@
Created by shmyga · © 2026 Created by shmyga · © 2026
</footer> </footer>
</div> </div>
<script> {# <script>
(function () { (function () {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const widget = params.get('widget') || window.location.hostname.startsWith('weather'); const widget = params.get('widget') || window.location.hostname.startsWith('weather');
@@ -120,7 +120,7 @@
document.body.classList.add('widget'); document.body.classList.add('widget');
} }
}()); }());
</script> </script> #}
</body> </body>
</html> </html>

View File

@@ -0,0 +1,25 @@
from pathlib import Path
from babel.dates import format_date
from fastapi.templating import Jinja2Templates
from gallery.version import __version__
from ...translation import _
def build_templates(templates_dir: Path | None = None, filters: dict | None = None) -> Jinja2Templates:
directory = [Path(__file__).parent.parent / "templates"]
if templates_dir:
directory.append(templates_dir)
templates = Jinja2Templates(directory=directory)
templates.env.globals.update(
{
"_": _,
"version": __version__,
"format_date": format_date,
}
)
if filters:
templates.env.filters.update(filters)
return templates

View File

@@ -1,34 +1,22 @@
import datetime import datetime
from pathlib import Path from pathlib import Path
from babel.dates import format_date
from fastapi import APIRouter from fastapi import APIRouter
from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from gallery.easel.core import AppRequest from gallery.easel.core import AppRequest
from gallery.sketch.schedule.catalog import BUNDLE from gallery.sketch.schedule.catalog import BUNDLE
from gallery.version import __version__
from ..common.util import TagType, TagUtil from ..common.utils.tag import TagType, TagUtil
from ..translation import _ from ..common.utils.template import build_templates
from .filters import timedelta_format from .filters import timedelta_format
base_dir = Path(__file__).parent templates = build_templates(
templates = Jinja2Templates( Path(__file__).parent / "templates",
directory=[
base_dir.parent / "common/templates",
base_dir / "templates",
]
)
templates.env.globals.update(
{ {
"_": _, "timedelta_format": timedelta_format,
"version": __version__, },
"format_date": format_date,
}
) )
templates.env.filters["timedelta_format"] = timedelta_format
router = APIRouter() router = APIRouter()
@@ -42,7 +30,6 @@ async def get_schedule_list(request: AppRequest):
request=request, request=request,
name="index.html", name="index.html",
context={ context={
"version": __version__,
"channels": channels_data, "channels": channels_data,
}, },
) )
@@ -57,7 +44,6 @@ async def get_schedule_tag(request: AppRequest, tag: str, live: bool = False):
request=request, request=request,
name="schedule.html", name="schedule.html",
context={ context={
"version": __version__,
"tag_util": TagUtil, "tag_util": TagUtil,
"datetime": datetime, "datetime": datetime,
"response": results[0], "response": results[0],
@@ -84,7 +70,6 @@ async def get_channel_tag(request: AppRequest, channel: str, tag: str):
request=request, request=request,
name="channel.html", name="channel.html",
context={ context={
"version": __version__,
"tag_util": TagUtil, "tag_util": TagUtil,
"datetime": datetime, "datetime": datetime,
"response": response, "response": response,

View File

@@ -5,9 +5,7 @@ from fastapi import Cookie, Header, Request
from gallery.util import root_path from gallery.util import root_path
_translation: ContextVar[gettext.GNUTranslations | gettext.NullTranslations] = ( _translation: ContextVar[gettext.GNUTranslations | gettext.NullTranslations] = ContextVar("translation")
ContextVar("translation")
)
async def set_language( async def set_language(
@@ -19,9 +17,7 @@ async def set_language(
lang = language or accept_language.split(",")[0].split("-")[0] lang = language or accept_language.split(",")[0].split("-")[0]
try: try:
t = gettext.translation( t = gettext.translation("messages", localedir=root_path / "locales", languages=[lang])
"messages", localedir=root_path / "locales", languages=[lang]
)
except FileNotFoundError: except FileNotFoundError:
t = gettext.NullTranslations() t = gettext.NullTranslations()

View File

@@ -3,33 +3,21 @@ from pathlib import Path
from fastapi import APIRouter from fastapi import APIRouter
from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from babel.dates import format_date
from gallery.easel.core import AppRequest from gallery.easel.core import AppRequest
from gallery.sketch.weather.model import WeatherResponse from gallery.sketch.weather.model import WeatherResponse
from gallery.version import __version__
from ..common.util import TagType, TagUtil from ..common.utils.tag import TagType, TagUtil
from ..translation import _ from ..common.utils.template import build_templates
from .filters import cloudness_icon, wind_direction_icon from .filters import cloudness_icon, wind_direction_icon
templates = build_templates(
base_dir = Path(__file__).parent Path(__file__).parent / "templates",
templates = Jinja2Templates(
directory=[
base_dir.parent / "common/templates",
base_dir / "templates",
]
)
templates.env.globals.update(
{ {
"_": _, "wind_direction_icon": wind_direction_icon,
"version": __version__, "cloudness_icon": cloudness_icon,
"format_date": format_date, },
}
) )
templates.env.filters["wind_direction_icon"] = wind_direction_icon
templates.env.filters["cloudness_icon"] = cloudness_icon
def build_weather_response(request: AppRequest, response: WeatherResponse): def build_weather_response(request: AppRequest, response: WeatherResponse):

View File

@@ -4,6 +4,7 @@ from bs4 import Tag
T = TypeVar("T") T = TypeVar("T")
class WidgetParser: class WidgetParser:
def parse_widget(self, tag: Tag) -> Tag: def parse_widget(self, tag: Tag) -> Tag:
raise NotImplementedError raise NotImplementedError

View File

@@ -36,9 +36,7 @@ class DateParser(RowParser[datetime.datetime]):
KEY = "date" KEY = "date"
def parse_row(self, tag: Tag) -> Iterable[datetime.datetime]: def parse_row(self, tag: Tag) -> Iterable[datetime.datetime]:
datetime_date_tag = tag.select_one( datetime_date_tag = tag.select_one(".widget-row.widget-row-datetime-date > .row-item")
".widget-row.widget-row-datetime-date > .row-item"
)
if datetime_date_tag: if datetime_date_tag:
date_str = datetime_date_tag.find(text=True, recursive=False).text date_str = datetime_date_tag.find(text=True, recursive=False).text
date = dateparser.parse(date_str, languages=["ru"]) date = dateparser.parse(date_str, languages=["ru"])
@@ -108,21 +106,15 @@ class TemperatureParser(RowParser[list[int]]):
KEY = "temperature" KEY = "temperature"
def parse_row(self, tag: Tag) -> Iterable[list[int]]: def parse_row(self, tag: Tag) -> Iterable[list[int]]:
for item in tag.select( for item in tag.select(".widget-row-chart[data-row=temperature-air] > .chart > .values > .value"):
".widget-row-chart[data-row=temperature-air] > .chart > .values > .value" yield [int(value.attrs["value"]) for value in item.select("temperature-value")]
):
yield [
int(value.attrs["value"]) for value in item.select("temperature-value")
]
class WindSpeedParser(RowParser[int]): class WindSpeedParser(RowParser[int]):
KEY = "wind_speed" KEY = "wind_speed"
def parse_row(self, tag: Tag) -> Iterable[int]: def parse_row(self, tag: Tag) -> Iterable[int]:
for item in tag.select( for item in tag.select(".widget-row-wind > .row-item > .wind-speed > speed-value"):
".widget-row-wind > .row-item > .wind-speed > speed-value"
):
yield int(item.attrs["value"]) yield int(item.attrs["value"])
@@ -152,22 +144,16 @@ class WindDirectionParser(RowParser[WindDirection]):
} }
def parse_row(self, tag: Tag) -> Iterable[float]: def parse_row(self, tag: Tag) -> Iterable[float]:
for item in tag.select( for item in tag.select(".widget-row-wind > .row-item > .wind-speed > .wind-direction"):
".widget-row-wind > .row-item > .wind-speed > .wind-direction"
):
wind_direction_str = item.text.lower().strip() wind_direction_str = item.text.lower().strip()
yield WindDirectionDeg.from_direction( yield WindDirectionDeg.from_direction(self.WIND_DIRECTION_MAP[wind_direction_str]).value
self.WIND_DIRECTION_MAP[wind_direction_str]
).value
class PrecipitationParser(RowParser[float]): class PrecipitationParser(RowParser[float]):
KEY = "precipitation" KEY = "precipitation"
def parse_row(self, tag: Tag) -> Iterable[float]: def parse_row(self, tag: Tag) -> Iterable[float]:
for item in tag.select( for item in tag.select(".widget-row[data-row=precipitation-bars] > .row-item > .item-unit"):
".widget-row[data-row=precipitation-bars] > .row-item > .item-unit"
):
yield float(item.text.replace(",", ".")) yield float(item.text.replace(",", "."))
@@ -175,9 +161,7 @@ class PressureParser(RowParser[list[int]]):
KEY = "pressure" KEY = "pressure"
def parse_row(self, tag: Tag) -> Iterable[list[int]]: def parse_row(self, tag: Tag) -> Iterable[list[int]]:
for item in tag.select( for item in tag.select(".widget-row-chart[data-row=pressure] > .chart > .values > .value"):
".widget-row-chart[data-row=pressure] > .chart > .values > .value"
):
yield [int(value.attrs["value"]) for value in item.select("pressure-value")] yield [int(value.attrs["value"]) for value in item.select("pressure-value")]

View File

@@ -25,32 +25,20 @@ class MatchTvApi(ScheduleApi):
ChannelId.MATCH_STRANA, ChannelId.MATCH_STRANA,
] ]
async def get_channel_schedule( async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
self, channel_id: ChannelId, date: datetime.date
) -> Schedule:
endpoint = f"tvguide/{channel_id}?date={date:%Y%m%d}" endpoint = f"tvguide/{channel_id}?date={date:%Y%m%d}"
data = await self.SOURCE.request(endpoint) data = await self.SOURCE.request(endpoint)
soup = BeautifulSoup(data, features="html.parser") soup = BeautifulSoup(data, features="html.parser")
values = [] values = []
channel_name = ( channel_name = soup.select_one(".p-tv-guide-header__title").text.replace("Телепрограмма ", "").strip()
soup.select_one(".p-tv-guide-header__title") current_day = datetime.datetime.combine(date.today(), datetime.datetime.min.time())
.text.replace("Телепрограмма ", "")
.strip()
)
current_day = datetime.datetime.combine(
date.today(), datetime.datetime.min.time()
)
end = current_day + datetime.timedelta(days=1, hours=6) end = current_day + datetime.timedelta(days=1, hours=6)
prev_value: ScheduleValue | None = None prev_value: ScheduleValue | None = None
for item in soup.select( for item in soup.select(
".p-tv-guide-schedule-channel-carcass__transmissions .p-tv-guide-schedule-channel-transmission" ".p-tv-guide-schedule-channel-carcass__transmissions .p-tv-guide-schedule-channel-transmission"
): ):
title = item.select_one( title = item.select_one(".p-tv-guide-schedule-channel-transmission__title").text.strip()
".p-tv-guide-schedule-channel-transmission__title" time_str = item.select_one(".p-tv-guide-schedule-channel-transmission__time-block").text.strip()
).text.strip()
time_str = item.select_one(
".p-tv-guide-schedule-channel-transmission__time-block"
).text.strip()
hours, minutes = map(int, time_str.split(":")) hours, minutes = map(int, time_str.split(":"))
item_date = current_day.replace(hour=hours, minute=minutes) item_date = current_day.replace(hour=hours, minute=minutes)
if prev_value is not None and item_date.hour < prev_value.start.hour: 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: if prev_value is not None:
prev_value.end = item_date prev_value.end = item_date
prev_value = value prev_value = value
return Schedule( return Schedule(channel=Channel(id=channel_id, name=channel_name), date=date, values=values)
channel=Channel(id=channel_id, name=channel_name), date=date, values=values
)

View File

@@ -55,10 +55,7 @@ class OpenWeatherApi(WeatherApi):
value = FORECAST_ITEM_PARSER.parse(item) value = FORECAST_ITEM_PARSER.parse(item)
item_date = value.date.replace(hour=0, minute=0) item_date = value.date.replace(hour=0, minute=0)
values_by_date[item_date].append(value) values_by_date[item_date].append(value)
values = [ values = [merge_weather_values(date, values) for date, values in values_by_date.items()]
merge_weather_values(date, values)
for date, values in values_by_date.items()
]
return WeatherResponse( return WeatherResponse(
location=location_id, location=location_id,
date=datetime.date.today(), date=datetime.date.today(),

View File

@@ -1,5 +0,0 @@
from pathlib import Path
from gallery.sketch.mock import MockData
OPENWEATHER_MOCK_DATA = MockData(Path(__file__).parent / "data")

View File

@@ -75,9 +75,7 @@ class OpenWeather:
self._source = ApiSource(self.BASE_URL) self._source = ApiSource(self.BASE_URL)
async def get_forecast(self, lat: float, lon: float) -> Forecast: async def get_forecast(self, lat: float, lon: float) -> Forecast:
endpoint = ( endpoint = f"data/2.5/forecast?lat={lat}&lon={lon}&appid={self._api_key}&units=metric"
f"data/2.5/forecast?lat={lat}&lon={lon}&appid={self._api_key}&units=metric"
)
response = await self._source.request(endpoint) response = await self._source.request(endpoint)
response_data = json.loads(response) response_data = json.loads(response)
return Forecast(**response_data) return Forecast(**response_data)

View File

@@ -24,11 +24,7 @@ class ForecastItemParser:
def parse(self, item: ForecastItem) -> WeatherValue: def parse(self, item: ForecastItem) -> WeatherValue:
item_date = datetime.datetime.fromtimestamp(item.dt, datetime.UTC) item_date = datetime.datetime.fromtimestamp(item.dt, datetime.UTC)
item_date = ( item_date = item_date.replace(tzinfo=datetime.timezone.utc).astimezone(tz=None).replace(tzinfo=None)
item_date.replace(tzinfo=datetime.timezone.utc)
.astimezone(tz=None)
.replace(tzinfo=None)
)
value = build_weather_value(item_date) value = build_weather_value(item_date)
# TODO parse temperature interval flag # TODO parse temperature interval flag
value.temperature = [round(item.main.temp)] value.temperature = [round(item.main.temp)]
@@ -38,12 +34,8 @@ class ForecastItemParser:
value.wind_speed = round(item.wind.speed) value.wind_speed = round(item.wind.speed)
value.wind_gust = round(item.wind.gust) value.wind_gust = round(item.wind.gust)
value.wind_direction = item.wind.deg value.wind_direction = item.wind.deg
value.sky.cloudness = self.CLOUDNESS_MAP.get( value.sky.cloudness = self.CLOUDNESS_MAP.get(item.weather[0].description, Cloudness.CLEAR)
item.weather[0].description, Cloudness.CLEAR value.sky.precipitation = self.PRECIPITATION_MAP.get(item.weather[0].description, Precipitation.NO)
)
value.sky.precipitation = self.PRECIPITATION_MAP.get(
item.weather[0].description, Precipitation.NO
)
if item.rain: if item.rain:
value.precipitation = round(item.rain.interval_3h, 1) value.precipitation = round(item.rain.interval_3h, 1)
return value return value

View File

@@ -60,9 +60,7 @@ class YandexTvApi(ScheduleApi):
async def get_channels(self) -> list[ChannelId]: async def get_channels(self) -> list[ChannelId]:
return list(CHANNELS_MAP.keys()) return list(CHANNELS_MAP.keys())
async def get_channel_schedule( async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
self, channel_id: ChannelId, date: datetime.date
) -> Schedule:
endpoint = f"channel/{CHANNELS_MAP[channel_id]}?date={date:%Y-%m-%d}" endpoint = f"channel/{CHANNELS_MAP[channel_id]}?date={date:%Y-%m-%d}"
data = await self.SOURCE.request(endpoint) data = await self.SOURCE.request(endpoint)
soup = BeautifulSoup(data, features="html.parser") soup = BeautifulSoup(data, features="html.parser")
@@ -70,9 +68,7 @@ class YandexTvApi(ScheduleApi):
raise RuntimeError("Captcha") raise RuntimeError("Captcha")
values = [] values = []
channel_name = soup.select_one(".channel-header__text").text.strip() channel_name = soup.select_one(".channel-header__text").text.strip()
current_day = datetime.datetime.combine( current_day = datetime.datetime.combine(date.today(), datetime.datetime.min.time())
date.today(), datetime.datetime.min.time()
)
end = current_day + datetime.timedelta(days=1, hours=6) end = current_day + datetime.timedelta(days=1, hours=6)
prev_value: ScheduleValue | None = None prev_value: ScheduleValue | None = None
for item in soup.select(".channel-schedule .channel-schedule__event"): for item in soup.select(".channel-schedule .channel-schedule__event"):
@@ -89,6 +85,4 @@ class YandexTvApi(ScheduleApi):
if prev_value is not None: if prev_value is not None:
prev_value.end = item_date prev_value.end = item_date
prev_value = value prev_value = value
return Schedule( return Schedule(channel=Channel(id=channel_id, name=channel_name), date=date, values=values)
channel=Channel(id=channel_id, name=channel_name), date=date, values=values
)

View File

@@ -11,18 +11,14 @@ class ScheduleApi(Api):
async def get_channels(self) -> list[ChannelId]: async def get_channels(self) -> list[ChannelId]:
raise NotImplementedError raise NotImplementedError
async def get_channel_schedule( async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
self, channel_id: ChannelId, date: datetime.date
) -> Schedule:
raise NotImplementedError raise NotImplementedError
async def get_all_schedules(self, date: datetime.date) -> list[Schedule]: async def get_all_schedules(self, date: datetime.date) -> list[Schedule]:
channels = await self.get_channels() channels = await self.get_channels()
results = [] results = []
for channel in channels: for channel in channels:
results.append( results.append(await self.get_channel_schedule(channel_id=channel, date=date))
await self.get_channel_schedule(channel_id=channel, date=date)
)
if self.INTERVAL > 0: if self.INTERVAL > 0:
await asyncio.sleep(self.INTERVAL) await asyncio.sleep(self.INTERVAL)
return results return results

View File

@@ -27,15 +27,11 @@ class CachedScheduleApi(ScheduleApi, CachedApi[ScheduleApi]):
), ),
**CACHE_PRESET._asdict(), **CACHE_PRESET._asdict(),
) )
async def get_channel_schedule( async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
self, channel_id: ChannelId, date: datetime.date
) -> Schedule:
return await self._api.get_channel_schedule(channel_id, date) return await self._api.get_channel_schedule(channel_id, date)
@cached( @cached(
key_builder=lambda fun, self, date: ( key_builder=lambda fun, self, date: (f"api.{self.CACHE_KEY}.{self.provider}.all.{date}"),
f"api.{self.CACHE_KEY}.{self.provider}.all.{date}"
),
**CACHE_PRESET._asdict(), **CACHE_PRESET._asdict(),
) )
async def get_all_schedules(self, date: datetime.date) -> list[Schedule]: async def get_all_schedules(self, date: datetime.date) -> list[Schedule]:

View File

@@ -7,9 +7,7 @@ logger = logging.getLogger("source")
class ApiSource: class ApiSource:
DEFAULT_USER_AGENT = ( DEFAULT_USER_AGENT = (
"Mozilla/5.0 (X11; Linux x86_64) " "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36"
) )
DEFAULT_TIMEOUT = 30.0 DEFAULT_TIMEOUT = 30.0

View File

@@ -23,9 +23,7 @@ def build_weather_value(date: datetime.datetime) -> WeatherValue:
) )
def merge_weather_values( def merge_weather_values(date: datetime.datetime, values: list[WeatherValue]) -> WeatherValue:
date: datetime.datetime, values: list[WeatherValue]
) -> WeatherValue:
result = build_weather_value(date) result = build_weather_value(date)
temperatures = [] temperatures = []
pressures = [] pressures = []

View File

@@ -1 +1,6 @@
__version__ = "0.2.2" __version__ = "0.2.2"
import tomllib
from pathlib import Path
__version__ = tomllib.loads((Path(__file__).parent.parent / "pyproject.toml").read_text())["tool"]["poetry"]["version"]

View File

@@ -35,9 +35,13 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts] [tool.poetry.scripts]
gallery = "gallery.main:run" gallery = "gallery.main:run"
[tool.black]
line-length = 120
[tool.isort]
profile = "black"
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "-p no:warnings" addopts = "-p no:warnings"
asyncio_mode = "auto" asyncio_mode = "auto"
testpaths = ["tests"] testpaths = ["tests"]
[tool.poetry_bumpversion.file."gallery/version.py"]

8
scripts/format Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
TARGET="gallery"
poetry run isort $TARGET
poetry run black $TARGET -q

View File

@@ -2,4 +2,8 @@
set -e set -e
cd "$(dirname $(dirname "$0"))" || exit cd "$(dirname $(dirname "$0"))" || exit
poetry run pylint gallery TARGET="gallery"
poetry run pylint $TARGET
poetry run isort $TARGET --check-only
poetry run black $TARGET -q --check --diff

15
scripts/setup Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
PYTHON_VERSION=3.12
poetry env use ${PYTHON_VERSION}
poetry install
cd static || exit
if [[ -f $HOME/.nvm/nvm.sh ]]; then
source "$HOME/.nvm/nvm.sh"
nvm use
fi
npm ci