diff --git a/Dockerfile b/Dockerfile index 1ebfd12..f21b1b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,4 +21,4 @@ COPY --from=builder /app ./ COPY gismeteo gismeteo/ COPY weather weather/ -CMD ["uvicorn", "weather.main:app", "--host", "0.0.0.0", "--port", "80"] +CMD ["uvicorn", "weather.main:app", "--host", "0.0.0.0", "--port", "80", "--log-config", "weather/logging.yaml"] diff --git a/gismeteo/api.py b/gismeteo/api.py index f81ad79..8c9de6a 100644 --- a/gismeteo/api.py +++ b/gismeteo/api.py @@ -1,7 +1,9 @@ import datetime +import logging from typing import Any, Dict, List import aiohttp +from aiocache import cached from bs4 import BeautifulSoup from weather.api import WeatherApi @@ -11,15 +13,27 @@ from . import datehelp from .location import LOCATION_BUNDLE from .parser import DAYS_PARSER, LOCATION_PARSER, ONE_DAY_PARSER, ROW_PARSERS +logger = logging.getLogger("gismeteo") + class GismeteoApi(WeatherApi): BASE_URL = "https://www.gismeteo.ru" + CACHE_TTL = 10 * 60 - USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" - COOKIE = "cf_clearance=U28mYVC0ENu88vorlL_CWmWOoevvXp0vb4xCqfqYC9s-1722273367-1.0.1.1-IDV73azTHY0V.NAnmEvok3zf5HHEkvF098pmya7IiqRRB5nk3FhbLCb0AeWm_kpTFqi1niFk2mYN_ramGTSl0A" + USER_AGENT = ( + "Mozilla/5.0 (X11; Linux x86_64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/126.0.0.0 Safari/537.36" + ) + COOKIE = ( + "cf_clearance=U28mYVC0ENu88vorlL_CWmWOoevvXp0vb4xCqfqYC9s-" + "1722273367-1.0.1.1-" + "IDV73azTHY0V.NAnmEvok3zf5HHEkvF098pmya7IiqRRB5nk3FhbLCb0AeWm_kpTFqi1niFk2mYN_ramGTSl0A" + ) async def _request(self, endpoint: str) -> str: url = f"{self.BASE_URL}/{endpoint}" + logger.info(url) async with aiohttp.ClientSession( headers={ "User-Agent": self.USER_AGENT, @@ -58,7 +72,6 @@ class GismeteoApi(WeatherApi): while len(result) < index + 1: result.append({}) result[index][parser.KEY] = value - print(">", result) values = [WeatherValue(**item) for item in result] return WeatherResponse( location=location or "n/a", @@ -67,11 +80,13 @@ class GismeteoApi(WeatherApi): values=values, ) + @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)}") 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") diff --git a/poetry.lock b/poetry.lock index f1e7fa4..e79d893 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,21 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "aiocache" +version = "0.12.2" +description = "multi backend asyncio cache" +optional = false +python-versions = "*" +files = [ + {file = "aiocache-0.12.2-py2.py3-none-any.whl", hash = "sha256:9b6fa30634ab0bfc3ecc44928a91ff07c6ea16d27d55469636b296ebc6eb5918"}, + {file = "aiocache-0.12.2.tar.gz", hash = "sha256:b41c9a145b050a5dcbae1599f847db6dd445193b1f3bd172d8e0fe0cb9e96684"}, +] + +[package.extras] +memcached = ["aiomcache (>=0.5.2)"] +msgpack = ["msgpack (>=0.5.5)"] +redis = ["redis (>=4.2.0)"] + [[package]] name = "aiohttp" version = "3.9.5" @@ -1795,4 +1811,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "bc04729da7680c2e078b4abd6e402186b54a938ef5cee388b0b2c462f6c167f1" +content-hash = "61e9ac2e623a1f5f543705c252f34f8e15b58e164b0a8c14330b12d814a3c778" diff --git a/pyproject.toml b/pyproject.toml index 2ce537b..e4e6883 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ aiohttp = "^3.9.5" beautifulsoup4 = "^4.12.3" dateparser = "^1.2.0" pydantic = "^2.8.2" +aiocache = "^0.12.2" [tool.poetry.group.app.dependencies] fastapi = "^0.111.1" diff --git a/weather.code-workspace b/weather.code-workspace index a73e1d3..fde26b9 100644 --- a/weather.code-workspace +++ b/weather.code-workspace @@ -23,7 +23,12 @@ "type": "debugpy", "request": "launch", "module": "uvicorn", - "args": ["weather.main:app", "--reload"] + "args": [ + "weather.main:app", + "--reload", + "--log-config", + "weather/logging.yaml" + ] } ] } diff --git a/weather/logging.yaml b/weather/logging.yaml new file mode 100644 index 0000000..6f1bf1e --- /dev/null +++ b/weather/logging.yaml @@ -0,0 +1,34 @@ +version: 1 +disable_existing_loggers: False +formatters: + default: + # "()": uvicorn.logging.DefaultFormatter + format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + access: + # "()": uvicorn.logging.AccessFormatter + format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" +handlers: + default: + formatter: default + class: logging.StreamHandler + stream: ext://sys.stderr + access: + formatter: access + class: logging.StreamHandler + stream: ext://sys.stdout +loggers: + uvicorn.error: + level: INFO + handlers: + - default + propagate: no + uvicorn.access: + level: INFO + handlers: + - access + propagate: no +root: + level: INFO + handlers: + - default + propagate: no diff --git a/weather/main.py b/weather/main.py index 8902a72..f398f2f 100644 --- a/weather/main.py +++ b/weather/main.py @@ -1,4 +1,5 @@ from os import environ +from pathlib import Path import uvicorn @@ -9,4 +10,10 @@ app = build_app(GismeteoApi()) def run(): - uvicorn.run("weather.main:app", host="0.0.0.0", port=8000, reload="DEBUG" in environ) + uvicorn.run( + "weather.main:app", + host="0.0.0.0", + port=8000, + log_config=str(Path(__file__).parent / "logging.yaml"), + reload="DEBUG" in environ, + )