refactor: rename project to gallery
This commit is contained in:
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
@@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
from enum import Enum
|
||||
from typing import NamedTuple, Optional
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class TagType(str, Enum):
|
||||
@@ -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)
|
||||
@@ -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:
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -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>
|
||||
@@ -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"),
|
||||
0
gallery/painting/gismeteo/__init__.py
Normal file
0
gallery/painting/gismeteo/__init__.py
Normal 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)
|
||||
@@ -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)
|
||||
5
gallery/painting/gismeteo/mock/__init__.py
Normal file
5
gallery/painting/gismeteo/mock/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from pathlib import Path
|
||||
|
||||
from gallery.sketch.mock import MockData
|
||||
|
||||
GISMETEO_MOCK_DATA = MockData(Path(__file__).parent / "data")
|
||||
@@ -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
15
gallery/sketch/mock.py
Normal 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
|
||||
0
gallery/sketch/schedule/__init_.py
Normal file
0
gallery/sketch/schedule/__init_.py
Normal file
26
gallery/sketch/schedule/model.py
Normal file
26
gallery/sketch/schedule/model.py
Normal 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]
|
||||
0
gallery/sketch/weather/__init__.py
Normal file
0
gallery/sketch/weather/__init__.py
Normal 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
|
||||
|
||||
12
gallery/sketch/weather/mock/__init__.py
Normal file
12
gallery/sketch/weather/mock/__init__.py
Normal 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")
|
||||
@@ -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:
|
||||
@@ -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"),
|
||||
)
|
||||
@@ -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()
|
||||
@@ -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"
|
||||
|
||||
@@ -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
5
scripts/lint
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
cd "$(dirname $(dirname "$0"))" || exit
|
||||
|
||||
poetry run pylint gallery
|
||||
@@ -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
|
||||
|
||||
@@ -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
5
scripts/test
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
cd "$(dirname $(dirname "$0"))" || exit
|
||||
|
||||
poetry run pytest tests
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user