refactor: rename project to gallery

This commit is contained in:
2024-08-11 20:46:04 +03:00
parent 3785b8ce5d
commit 72fd378c40
48 changed files with 117 additions and 92 deletions

View File

@@ -0,0 +1 @@
# Gallery

View File

@@ -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"
]
}
]

View File

@@ -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

View File

@@ -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):

View File

@@ -1,6 +1,6 @@
import datetime
from enum import Enum
from typing import NamedTuple, Optional
from typing import NamedTuple
class TagType(str, Enum):

View File

@@ -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)

View File

@@ -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:

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -18,7 +18,7 @@
<body class="app-container">
<ul>
{% for location in locations %}
<li><a href="weather/{{location.name}}-{{location.location_id}}">{{location.name}}</a></li>
<li><a href="weather/{{location}}">{{location}}</a></li>
{% endfor %}
</ul>
</body>

View File

@@ -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"),

View File

View File

@@ -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)

View File

@@ -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)

View File

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

View File

@@ -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

15
gallery/sketch/mock.py Normal file
View File

@@ -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

View File

View File

@@ -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]

View File

View File

@@ -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

View File

@@ -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")

View File

@@ -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:

View File

@@ -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"),
)

View File

@@ -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()

View File

@@ -1,10 +1,10 @@
[tool.poetry]
name = "weather"
name = "gallery"
version = "0.1.0"
description = ""
authors = ["shmyga <shmyga.z@gmail.com>"]
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"

View File

@@ -2,4 +2,4 @@
set -e
cd "$(dirname $(dirname "$0"))" || exit
docker build -t shmyga/weather .
docker build -t shmyga/gallery .

5
scripts/lint Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
poetry run pylint gallery

View File

@@ -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

View File

@@ -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

5
scripts/test Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
poetry run pytest tests

View File

@@ -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