diff --git a/README.md b/README.md index e69de29..b021765 100644 --- a/README.md +++ b/README.md @@ -0,0 +1 @@ +# Gallery diff --git a/weather.code-workspace b/gallery.code-workspace similarity index 86% rename from weather.code-workspace rename to gallery.code-workspace index fde26b9..ffd578d 100644 --- a/weather.code-workspace +++ b/gallery.code-workspace @@ -19,15 +19,15 @@ "version": "0.2.1", "configurations": [ { - "name": "app", + "name": "gallery:app", "type": "debugpy", "request": "launch", "module": "uvicorn", "args": [ - "weather.main:app", + "gallery.main:app", "--reload", "--log-config", - "weather/logging.yaml" + "gallery/logging.yaml" ] } ] diff --git a/gismeteo/__init__.py b/gallery/__init__.py similarity index 100% rename from gismeteo/__init__.py rename to gallery/__init__.py diff --git a/weather/app/__init__.py b/gallery/easel/__init__.py similarity index 88% rename from weather/app/__init__.py rename to gallery/easel/__init__.py index debba70..28e5faf 100644 --- a/weather/app/__init__.py +++ b/gallery/easel/__init__.py @@ -2,7 +2,7 @@ import locale from fastapi import FastAPI -from weather.api import WeatherApi +from gallery.sketch.weather.api import WeatherApi from .route import api, doc, view diff --git a/weather/__init__.py b/gallery/easel/route/__init__.py similarity index 100% rename from weather/__init__.py rename to gallery/easel/route/__init__.py diff --git a/weather/app/route/api.py b/gallery/easel/route/api.py similarity index 86% rename from weather/app/route/api.py rename to gallery/easel/route/api.py index 969b65f..b241cda 100644 --- a/weather/app/route/api.py +++ b/gallery/easel/route/api.py @@ -2,8 +2,8 @@ import datetime from fastapi import FastAPI, Request -from weather.api import WeatherApi -from weather.model import WeatherResponse +from gallery.sketch.weather.api import WeatherApi +from gallery.sketch.weather.model import WeatherResponse def mount(app: FastAPI): diff --git a/weather/app/route/doc/__init__.py b/gallery/easel/route/doc/__init__.py similarity index 100% rename from weather/app/route/doc/__init__.py rename to gallery/easel/route/doc/__init__.py diff --git a/weather/app/route/doc/static/redoc.standalone.js b/gallery/easel/route/doc/static/redoc.standalone.js similarity index 100% rename from weather/app/route/doc/static/redoc.standalone.js rename to gallery/easel/route/doc/static/redoc.standalone.js diff --git a/weather/app/route/doc/static/swagger-ui-bundle.js b/gallery/easel/route/doc/static/swagger-ui-bundle.js similarity index 100% rename from weather/app/route/doc/static/swagger-ui-bundle.js rename to gallery/easel/route/doc/static/swagger-ui-bundle.js diff --git a/weather/app/route/doc/static/swagger-ui.css b/gallery/easel/route/doc/static/swagger-ui.css similarity index 100% rename from weather/app/route/doc/static/swagger-ui.css rename to gallery/easel/route/doc/static/swagger-ui.css diff --git a/weather/app/route/util.py b/gallery/easel/route/util.py similarity index 97% rename from weather/app/route/util.py rename to gallery/easel/route/util.py index f3308c1..cb149b7 100644 --- a/weather/app/route/util.py +++ b/gallery/easel/route/util.py @@ -1,6 +1,6 @@ import datetime from enum import Enum -from typing import NamedTuple, Optional +from typing import NamedTuple class TagType(str, Enum): diff --git a/weather/app/route/view/__init__.py b/gallery/easel/route/view/__init__.py similarity index 87% rename from weather/app/route/view/__init__.py rename to gallery/easel/route/view/__init__.py index aa7f262..6d5b117 100644 --- a/weather/app/route/view/__init__.py +++ b/gallery/easel/route/view/__init__.py @@ -6,12 +6,11 @@ from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates -from gismeteo.location import LOCATION_BUNDLE -from gismeteo.mock import MOCK_DATA -from weather.api import WeatherApi -from weather.app.route.util import TagType, TagUtil -from weather.model import WeatherResponse +from gallery.sketch.weather.api import WeatherApi +from gallery.sketch.weather.mock import WEATHER_MOCK_DATA +from gallery.sketch.weather.model import WeatherResponse +from ..util import TagType, TagUtil from .filters import cloudness_icon, wind_direction_icon @@ -39,11 +38,13 @@ def mount(app: FastAPI): @app.get("/weather", response_class=HTMLResponse) async def get_weather_list(request: Request): + weather_api: WeatherApi = request.app.state.weather_api + locations = await weather_api.get_locations() return templates.TemplateResponse( request=request, name="index.html", context={ - "locations": LOCATION_BUNDLE._values, + "locations": locations, }, ) @@ -53,12 +54,12 @@ def mount(app: FastAPI): @app.get("/weather/{location}/day/mock", response_class=HTMLResponse) async def get_weather_day_mock(request: Request): - response = MOCK_DATA.get_response("day") + response = WEATHER_MOCK_DATA.get_response("day") return build_weather_response(request, response) @app.get("/weather/{location}/days/mock", response_class=HTMLResponse) async def get_weather_days_mock(request: Request): - response = MOCK_DATA.get_response("days") + response = WEATHER_MOCK_DATA.get_response("days") return build_weather_response(request, response) @app.get("/weather/{location}/day/{date}", response_class=HTMLResponse) diff --git a/weather/app/route/view/filters.py b/gallery/easel/route/view/filters.py similarity index 92% rename from weather/app/route/view/filters.py rename to gallery/easel/route/view/filters.py index ebdd0f1..c24bda5 100644 --- a/weather/app/route/view/filters.py +++ b/gallery/easel/route/view/filters.py @@ -1,4 +1,4 @@ -from weather.model import Cloudness, Precipitation, Sky, WindDirection +from gallery.sketch.weather.model import Cloudness, Precipitation, Sky, WindDirection def wind_direction_icon(wind_direction: WindDirection) -> str: diff --git a/weather/app/route/view/static/favicon.ico b/gallery/easel/route/view/static/favicon.ico similarity index 100% rename from weather/app/route/view/static/favicon.ico rename to gallery/easel/route/view/static/favicon.ico diff --git a/weather/app/route/view/static/index.js b/gallery/easel/route/view/static/index.js similarity index 100% rename from weather/app/route/view/static/index.js rename to gallery/easel/route/view/static/index.js diff --git a/weather/app/route/view/static/style.css b/gallery/easel/route/view/static/style.css similarity index 100% rename from weather/app/route/view/static/style.css rename to gallery/easel/route/view/static/style.css diff --git a/weather/app/route/view/templates/index.html b/gallery/easel/route/view/templates/index.html similarity index 84% rename from weather/app/route/view/templates/index.html rename to gallery/easel/route/view/templates/index.html index 5740528..ef84ef0 100644 --- a/weather/app/route/view/templates/index.html +++ b/gallery/easel/route/view/templates/index.html @@ -18,7 +18,7 @@ diff --git a/weather/app/route/view/templates/weather.html b/gallery/easel/route/view/templates/weather.html similarity index 100% rename from weather/app/route/view/templates/weather.html rename to gallery/easel/route/view/templates/weather.html diff --git a/weather/logging.yaml b/gallery/logging.yaml similarity index 100% rename from weather/logging.yaml rename to gallery/logging.yaml diff --git a/weather/main.py b/gallery/main.py similarity index 69% rename from weather/main.py rename to gallery/main.py index f398f2f..c745037 100644 --- a/weather/main.py +++ b/gallery/main.py @@ -3,15 +3,15 @@ from pathlib import Path import uvicorn -from gismeteo.api import GismeteoApi -from weather.app import build_app +from gallery.easel import build_app +from gallery.painting.gismeteo.api import GismeteoApi app = build_app(GismeteoApi()) def run(): uvicorn.run( - "weather.main:app", + "gallery.main:app", host="0.0.0.0", port=8000, log_config=str(Path(__file__).parent / "logging.yaml"), diff --git a/weather/app/route/__init__.py b/gallery/painting/__init__.py similarity index 100% rename from weather/app/route/__init__.py rename to gallery/painting/__init__.py diff --git a/gallery/painting/gismeteo/__init__.py b/gallery/painting/gismeteo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gismeteo/api.py b/gallery/painting/gismeteo/api.py similarity index 88% rename from gismeteo/api.py rename to gallery/painting/gismeteo/api.py index 8c9de6a..73280f8 100644 --- a/gismeteo/api.py +++ b/gallery/painting/gismeteo/api.py @@ -6,11 +6,10 @@ import aiohttp from aiocache import cached from bs4 import BeautifulSoup -from weather.api import WeatherApi -from weather.model import WeatherResponse, WeatherValue +from gallery.sketch.weather.api import WeatherApi +from gallery.sketch.weather.model import WeatherResponse, WeatherValue from . import datehelp -from .location import LOCATION_BUNDLE from .parser import DAYS_PARSER, LOCATION_PARSER, ONE_DAY_PARSER, ROW_PARSERS logger = logging.getLogger("gismeteo") @@ -80,14 +79,18 @@ class GismeteoApi(WeatherApi): values=values, ) + async def get_locations(self) -> list[str]: + return [ + "orel-4432", + "zmiyevka-184640", + ] + @cached(ttl=CACHE_TTL) async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse: - location = LOCATION_BUNDLE.parse(location_id) - data = await self._request(f"weather-{location}/{datehelp.dump(date)}") + data = await self._request(f"weather-{location_id}/{datehelp.dump(date)}") return self._parse_oneday(date, data) @cached(ttl=CACHE_TTL) async def get_days(self, location_id: str, days: int) -> WeatherResponse: - location = LOCATION_BUNDLE.parse(location_id) - data = await self._request(f"weather-{location}/{days}-days") + data = await self._request(f"weather-{location_id}/{days}-days") return self._parse_manydays(data) diff --git a/gismeteo/core.py b/gallery/painting/gismeteo/core.py similarity index 100% rename from gismeteo/core.py rename to gallery/painting/gismeteo/core.py diff --git a/gismeteo/datehelp.py b/gallery/painting/gismeteo/datehelp.py similarity index 95% rename from gismeteo/datehelp.py rename to gallery/painting/gismeteo/datehelp.py index ed05d1b..196a604 100644 --- a/gismeteo/datehelp.py +++ b/gallery/painting/gismeteo/datehelp.py @@ -14,7 +14,7 @@ class Day(str, Enum): def parse(value: str) -> datetime.date: - if value == "today" or value == "mock": + if value in ["today", "mock"]: return datetime.date.today() elif value == "tomorrow": return datetime.date.today() + datetime.timedelta(days=1) diff --git a/gallery/painting/gismeteo/mock/__init__.py b/gallery/painting/gismeteo/mock/__init__.py new file mode 100644 index 0000000..72e3ba2 --- /dev/null +++ b/gallery/painting/gismeteo/mock/__init__.py @@ -0,0 +1,5 @@ +from pathlib import Path + +from gallery.sketch.mock import MockData + +GISMETEO_MOCK_DATA = MockData(Path(__file__).parent / "data") diff --git a/gismeteo/mock/data/10-days.html b/gallery/painting/gismeteo/mock/data/10-days.html similarity index 100% rename from gismeteo/mock/data/10-days.html rename to gallery/painting/gismeteo/mock/data/10-days.html diff --git a/gismeteo/mock/data/today.html b/gallery/painting/gismeteo/mock/data/today.html similarity index 100% rename from gismeteo/mock/data/today.html rename to gallery/painting/gismeteo/mock/data/today.html diff --git a/gismeteo/parser.py b/gallery/painting/gismeteo/parser.py similarity index 98% rename from gismeteo/parser.py rename to gallery/painting/gismeteo/parser.py index 33e5a93..1e3f794 100644 --- a/gismeteo/parser.py +++ b/gallery/painting/gismeteo/parser.py @@ -5,7 +5,7 @@ from typing import Iterable import dateparser from bs4 import Tag -from weather.model import Cloudness, Precipitation, Sky, WindDirection +from gallery.sketch.weather.model import Cloudness, Precipitation, Sky, WindDirection from .core import BaseWidgetParser, RowParser diff --git a/gallery/sketch/mock.py b/gallery/sketch/mock.py new file mode 100644 index 0000000..c0aba23 --- /dev/null +++ b/gallery/sketch/mock.py @@ -0,0 +1,15 @@ +import json +from pathlib import Path + + +class MockData: + + def __init__(self, data_dir) -> None: + self._data_dir = data_dir + + def get_html(self, key: str) -> str: + return (self._data_dir / f"{key}.html").read_text() + + def get_json(self, key: str) -> dict: + data = json.loads((Path(__file__).parent / f"data/{key}.json").read_text()) + return data diff --git a/gallery/sketch/schedule/__init_.py b/gallery/sketch/schedule/__init_.py new file mode 100644 index 0000000..e69de29 diff --git a/gallery/sketch/schedule/model.py b/gallery/sketch/schedule/model.py new file mode 100644 index 0000000..4ac4306 --- /dev/null +++ b/gallery/sketch/schedule/model.py @@ -0,0 +1,26 @@ +import datetime + +from pydantic import BaseModel + + +class Model(BaseModel): + class Config: + use_enum_values = True + + +class Channel(Model): + id: str + name: str + + +class ScheduleItem(Model): + start: datetime.datetime + end: datetime.datetime + label: str + category: str | None + + +class Schedule(Model): + channel: Channel + date: datetime.date + items: list[ScheduleItem] diff --git a/gallery/sketch/weather/__init__.py b/gallery/sketch/weather/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/weather/api.py b/gallery/sketch/weather/api.py similarity index 78% rename from weather/api.py rename to gallery/sketch/weather/api.py index cf9f808..1838b5d 100644 --- a/weather/api.py +++ b/gallery/sketch/weather/api.py @@ -4,6 +4,9 @@ from .model import WeatherResponse class WeatherApi: + async def get_locations(self) -> list[str]: + raise NotImplementedError + async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse: raise NotImplementedError diff --git a/gallery/sketch/weather/mock/__init__.py b/gallery/sketch/weather/mock/__init__.py new file mode 100644 index 0000000..adf4734 --- /dev/null +++ b/gallery/sketch/weather/mock/__init__.py @@ -0,0 +1,12 @@ +from pathlib import Path + +from gallery.sketch.mock import MockData +from gallery.sketch.weather.model import WeatherResponse + + +class WeatherMockData(MockData): + def get_response(self, key: str) -> WeatherResponse: + return WeatherResponse(**self.get_json(key)) + + +WEATHER_MOCK_DATA = WeatherMockData(Path(__file__).parent / "data") diff --git a/gismeteo/mock/data/day.json b/gallery/sketch/weather/mock/data/day.json similarity index 100% rename from gismeteo/mock/data/day.json rename to gallery/sketch/weather/mock/data/day.json diff --git a/gismeteo/mock/data/days.json b/gallery/sketch/weather/mock/data/days.json similarity index 100% rename from gismeteo/mock/data/days.json rename to gallery/sketch/weather/mock/data/days.json diff --git a/weather/model.py b/gallery/sketch/weather/model.py similarity index 100% rename from weather/model.py rename to gallery/sketch/weather/model.py diff --git a/weather/util.py b/gallery/sketch/weather/util.py similarity index 84% rename from weather/util.py rename to gallery/sketch/weather/util.py index f4d9896..767b668 100644 --- a/weather/util.py +++ b/gallery/sketch/weather/util.py @@ -1,6 +1,6 @@ import datetime -from weather.model import Cloudness, Precipitation, Sky, WeatherValue, WindDirection +from .model import Cloudness, Precipitation, Sky, WeatherValue, WindDirection def build_weather_value(date: datetime.datetime) -> WeatherValue: diff --git a/gismeteo/location.py b/gismeteo/location.py deleted file mode 100644 index f314160..0000000 --- a/gismeteo/location.py +++ /dev/null @@ -1,34 +0,0 @@ -from typing import NamedTuple - - -class LocationValue(NamedTuple): - location_id: int - name: str - - def __str__(self) -> str: - return f"{self.name}-{self. location_id}" - - @classmethod - def parse(cls, source: str) -> "LocationValue": - name, location = source.split("-") - return cls(int(location), name) - - -class LocationBundle: - def __init__(self, *values: LocationValue) -> None: - self._values = values - self._values_by_name = {value.name: value for value in self._values} - self._values_by_id = {value.location_id: value for value in self._values} - - def parse(self, value: str) -> LocationValue: - if str.isdigit(value): - return self._values_by_id[int(value)] - if value in self._values_by_name: - return self._values_by_name[value] - return LocationValue.parse(value) - - -LOCATION_BUNDLE = LocationBundle( - LocationValue(4432, "orel"), - LocationValue(184640, "zmiyevka"), -) diff --git a/gismeteo/mock/__init__.py b/gismeteo/mock/__init__.py deleted file mode 100644 index 65fdb84..0000000 --- a/gismeteo/mock/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -import json -from pathlib import Path - -from gismeteo.api import WeatherResponse - - -class MockData: - - def get_html(self, key: str) -> str: - return (Path(__file__).parent / f"data/{key}.html").read_text() - - def get_response(self, key: str) -> WeatherResponse: - data = json.loads((Path(__file__).parent / f"data/{key}.json").read_text()) - return WeatherResponse(**data) - - -MOCK_DATA = MockData() diff --git a/pyproject.toml b/pyproject.toml index e4e6883..9702c22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [tool.poetry] -name = "weather" +name = "gallery" version = "0.1.0" description = "" authors = ["shmyga "] readme = "README.md" -packages = [{ include = "weather" }, { include = "gismeteo" }] +packages = [{ include = "gallery" }] [tool.poetry.dependencies] python = "^3.12" @@ -32,7 +32,7 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] -app = 'weather.main:run' +gallery = "gallery.main:run" [tool.pytest.ini_options] addopts = "-p no:warnings" diff --git a/scripts/build b/scripts/build index 68a9e40..cb1748a 100755 --- a/scripts/build +++ b/scripts/build @@ -2,4 +2,4 @@ set -e cd "$(dirname $(dirname "$0"))" || exit -docker build -t shmyga/weather . +docker build -t shmyga/gallery . diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 0000000..7bb1550 --- /dev/null +++ b/scripts/lint @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -e +cd "$(dirname $(dirname "$0"))" || exit + +poetry run pylint gallery diff --git a/scripts/publish b/scripts/publish index e1023ca..12298a5 100755 --- a/scripts/publish +++ b/scripts/publish @@ -2,7 +2,7 @@ set -e cd "$(dirname $(dirname "$0"))" || exit -IMAGE_NAME=shmyga/weather +IMAGE_NAME=shmyga/gallery docker tag $IMAGE_NAME instreamatic.com:8083/$IMAGE_NAME docker push instreamatic.com:8083/$IMAGE_NAME diff --git a/scripts/run b/scripts/run index e9a5dea..9f45915 100755 --- a/scripts/run +++ b/scripts/run @@ -2,4 +2,4 @@ set -e cd "$(dirname $(dirname "$0"))" || exit -docker run --rm -p 8000:80 shmyga/weather +docker run --rm -p 8000:80 shmyga/gallery diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..621b46d --- /dev/null +++ b/scripts/test @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -e +cd "$(dirname $(dirname "$0"))" || exit + +poetry run pytest tests diff --git a/tests/test_gismeteo_api.py b/tests/test_gismeteo_api.py index 0e4740a..3ead3ca 100644 --- a/tests/test_gismeteo_api.py +++ b/tests/test_gismeteo_api.py @@ -2,8 +2,8 @@ import datetime import pytest -from gismeteo.api import GismeteoApi -from gismeteo.mock import MOCK_DATA +from gallery.painting.gismeteo.api import GismeteoApi +from gallery.painting.gismeteo.mock import GISMETEO_MOCK_DATA @pytest.fixture(name="gismeteo_api", scope="module") @@ -11,17 +11,17 @@ def gismeteo_api_fixture() -> GismeteoApi: api = GismeteoApi() async def _request(endpoint: str) -> str: - return MOCK_DATA.get_html(endpoint.split("/")[-1]) + return GISMETEO_MOCK_DATA.get_html(endpoint.split("/")[-1]) api._request = _request return api async def test_day(gismeteo_api: GismeteoApi): - result = await gismeteo_api.get_day("zmiyevka", datetime.date.today()) + result = await gismeteo_api.get_day("test", datetime.date.today()) assert len(result.values) == 8 async def test_days(gismeteo_api: GismeteoApi): - result = await gismeteo_api.get_days("zmiyevka", 10) + result = await gismeteo_api.get_days("test", 10) assert len(result.values) == 10