feat: add cache and logging
This commit is contained in:
@@ -21,4 +21,4 @@ COPY --from=builder /app ./
|
|||||||
COPY gismeteo gismeteo/
|
COPY gismeteo gismeteo/
|
||||||
COPY weather weather/
|
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"]
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from aiocache import cached
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
from weather.api import WeatherApi
|
from weather.api import WeatherApi
|
||||||
@@ -11,15 +13,27 @@ from . import datehelp
|
|||||||
from .location import LOCATION_BUNDLE
|
from .location import LOCATION_BUNDLE
|
||||||
from .parser import DAYS_PARSER, LOCATION_PARSER, ONE_DAY_PARSER, ROW_PARSERS
|
from .parser import DAYS_PARSER, LOCATION_PARSER, ONE_DAY_PARSER, ROW_PARSERS
|
||||||
|
|
||||||
|
logger = logging.getLogger("gismeteo")
|
||||||
|
|
||||||
|
|
||||||
class GismeteoApi(WeatherApi):
|
class GismeteoApi(WeatherApi):
|
||||||
BASE_URL = "https://www.gismeteo.ru"
|
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"
|
USER_AGENT = (
|
||||||
COOKIE = "cf_clearance=U28mYVC0ENu88vorlL_CWmWOoevvXp0vb4xCqfqYC9s-1722273367-1.0.1.1-IDV73azTHY0V.NAnmEvok3zf5HHEkvF098pmya7IiqRRB5nk3FhbLCb0AeWm_kpTFqi1niFk2mYN_ramGTSl0A"
|
"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:
|
async def _request(self, endpoint: str) -> str:
|
||||||
url = f"{self.BASE_URL}/{endpoint}"
|
url = f"{self.BASE_URL}/{endpoint}"
|
||||||
|
logger.info(url)
|
||||||
async with aiohttp.ClientSession(
|
async with aiohttp.ClientSession(
|
||||||
headers={
|
headers={
|
||||||
"User-Agent": self.USER_AGENT,
|
"User-Agent": self.USER_AGENT,
|
||||||
@@ -58,7 +72,6 @@ class GismeteoApi(WeatherApi):
|
|||||||
while len(result) < index + 1:
|
while len(result) < index + 1:
|
||||||
result.append({})
|
result.append({})
|
||||||
result[index][parser.KEY] = value
|
result[index][parser.KEY] = value
|
||||||
print(">", result)
|
|
||||||
values = [WeatherValue(**item) for item in result]
|
values = [WeatherValue(**item) for item in result]
|
||||||
return WeatherResponse(
|
return WeatherResponse(
|
||||||
location=location or "n/a",
|
location=location or "n/a",
|
||||||
@@ -67,11 +80,13 @@ class GismeteoApi(WeatherApi):
|
|||||||
values=values,
|
values=values,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@cached(ttl=CACHE_TTL)
|
||||||
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:
|
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:
|
||||||
location = LOCATION_BUNDLE.parse(location_id)
|
location = LOCATION_BUNDLE.parse(location_id)
|
||||||
data = await self._request(f"weather-{location}/{datehelp.dump(date)}")
|
data = await self._request(f"weather-{location}/{datehelp.dump(date)}")
|
||||||
return self._parse_oneday(date, data)
|
return self._parse_oneday(date, data)
|
||||||
|
|
||||||
|
@cached(ttl=CACHE_TTL)
|
||||||
async def get_days(self, location_id: str, days: int) -> WeatherResponse:
|
async def get_days(self, location_id: str, days: int) -> WeatherResponse:
|
||||||
location = LOCATION_BUNDLE.parse(location_id)
|
location = LOCATION_BUNDLE.parse(location_id)
|
||||||
data = await self._request(f"weather-{location}/{days}-days")
|
data = await self._request(f"weather-{location}/{days}-days")
|
||||||
|
|||||||
18
poetry.lock
generated
18
poetry.lock
generated
@@ -1,5 +1,21 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
# 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]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
version = "3.9.5"
|
version = "3.9.5"
|
||||||
@@ -1795,4 +1811,4 @@ multidict = ">=4.0"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "bc04729da7680c2e078b4abd6e402186b54a938ef5cee388b0b2c462f6c167f1"
|
content-hash = "61e9ac2e623a1f5f543705c252f34f8e15b58e164b0a8c14330b12d814a3c778"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ aiohttp = "^3.9.5"
|
|||||||
beautifulsoup4 = "^4.12.3"
|
beautifulsoup4 = "^4.12.3"
|
||||||
dateparser = "^1.2.0"
|
dateparser = "^1.2.0"
|
||||||
pydantic = "^2.8.2"
|
pydantic = "^2.8.2"
|
||||||
|
aiocache = "^0.12.2"
|
||||||
|
|
||||||
[tool.poetry.group.app.dependencies]
|
[tool.poetry.group.app.dependencies]
|
||||||
fastapi = "^0.111.1"
|
fastapi = "^0.111.1"
|
||||||
|
|||||||
@@ -23,7 +23,12 @@
|
|||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"module": "uvicorn",
|
"module": "uvicorn",
|
||||||
"args": ["weather.main:app", "--reload"]
|
"args": [
|
||||||
|
"weather.main:app",
|
||||||
|
"--reload",
|
||||||
|
"--log-config",
|
||||||
|
"weather/logging.yaml"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
34
weather/logging.yaml
Normal file
34
weather/logging.yaml
Normal file
@@ -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
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
from os import environ
|
from os import environ
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
@@ -9,4 +10,10 @@ app = build_app(GismeteoApi())
|
|||||||
|
|
||||||
|
|
||||||
def run():
|
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,
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user