feat: add redis cache
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
from os import environ
|
||||
|
||||
from aiocache import caches
|
||||
|
||||
REDIS_HOST = environ.get("REDIS_HOST", "0.0.0.0")
|
||||
REDIS_PORT = int(environ.get("REDIS_PORT", 6379))
|
||||
REDIS_DB = int(environ.get("REDIS_DB", 1))
|
||||
|
||||
caches.set_config(
|
||||
{
|
||||
"default": {
|
||||
"cache": "aiocache.SimpleMemoryCache",
|
||||
"serializer": {"class": "aiocache.serializers.StringSerializer"},
|
||||
},
|
||||
"redis": {
|
||||
"cache": "aiocache.RedisCache",
|
||||
"endpoint": REDIS_HOST,
|
||||
"port": REDIS_PORT,
|
||||
"db": REDIS_DB,
|
||||
"serializer": {"class": "aiocache.serializers.PickleSerializer"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -6,8 +6,12 @@ import uvicorn
|
||||
from gallery.easel import build_app
|
||||
from gallery.painting.gismeteo.api import GismeteoApi
|
||||
from gallery.painting.matchtv.api import MatchTvApi
|
||||
from gallery.sketch.schedule.cached import CachedScheduleApi
|
||||
from gallery.sketch.weather.cached import CachedWeatherApi
|
||||
|
||||
app = build_app(GismeteoApi(), MatchTvApi())
|
||||
weather_api = CachedWeatherApi(GismeteoApi())
|
||||
schedule_api = CachedScheduleApi(MatchTvApi())
|
||||
app = build_app(weather_api, schedule_api)
|
||||
|
||||
|
||||
def run():
|
||||
@@ -18,3 +22,7 @@ def run():
|
||||
log_config=str(Path(__file__).parent / "logging.yaml"),
|
||||
reload="DEBUG" in environ,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
||||
@@ -2,7 +2,6 @@ import datetime
|
||||
import logging
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from aiocache import cached
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from gallery.sketch.source import ApiSource
|
||||
@@ -17,6 +16,7 @@ logger = logging.getLogger("gismeteo")
|
||||
|
||||
|
||||
class GismeteoApi(WeatherApi):
|
||||
PROVIDER = "gismeteo"
|
||||
SOURCE = ApiSource(
|
||||
"https://www.gismeteo.ru",
|
||||
cookies={
|
||||
@@ -32,7 +32,6 @@ class GismeteoApi(WeatherApi):
|
||||
)
|
||||
},
|
||||
)
|
||||
CACHE_TTL = 10 * 60
|
||||
|
||||
def _parse_oneday(self, date: datetime.date, data: str) -> WeatherResponse:
|
||||
result: List[Dict[str, Any]] = []
|
||||
@@ -76,12 +75,10 @@ class GismeteoApi(WeatherApi):
|
||||
LocationId.ZMIYEVKA,
|
||||
]
|
||||
|
||||
@cached(ttl=CACHE_TTL)
|
||||
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:
|
||||
data = await self.SOURCE.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:
|
||||
data = await self.SOURCE.request(f"weather-{location_id}/{days}-days")
|
||||
return self._parse_manydays(data)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from aiocache import cached
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from gallery.sketch.schedule.api import ScheduleApi
|
||||
@@ -13,8 +12,8 @@ logger = logging.getLogger("matchtv")
|
||||
|
||||
|
||||
class MatchTvApi(ScheduleApi):
|
||||
PROVIDER = "matchtv"
|
||||
SOURCE = ApiSource("https://matchtv.ru")
|
||||
CACHE_TTL = 30 * 60
|
||||
|
||||
async def get_channels(self) -> list[str]:
|
||||
return [
|
||||
@@ -27,7 +26,6 @@ class MatchTvApi(ScheduleApi):
|
||||
ChannelId.MATCH_STRANA,
|
||||
]
|
||||
|
||||
@cached(ttl=CACHE_TTL)
|
||||
async def get_channel_schedule(
|
||||
self, channel_id: str, date: datetime.date
|
||||
) -> Schedule:
|
||||
|
||||
0
gallery/sketch/__init__.py
Normal file
0
gallery/sketch/__init__.py
Normal file
6
gallery/sketch/api.py
Normal file
6
gallery/sketch/api.py
Normal file
@@ -0,0 +1,6 @@
|
||||
class Api:
|
||||
PROVIDER: str
|
||||
|
||||
@property
|
||||
def provider(self) -> str:
|
||||
return self.PROVIDER
|
||||
20
gallery/sketch/cached.py
Normal file
20
gallery/sketch/cached.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from gallery.util import TimeUnit
|
||||
|
||||
from .api import Api
|
||||
|
||||
API = TypeVar("API", bound=Api)
|
||||
|
||||
|
||||
class CachedApi(Api, Generic[API]):
|
||||
CACHE_TTL: int = TimeUnit.HOUR
|
||||
CACHE_ALIAS: str = "redis"
|
||||
CACHE_KEY: str
|
||||
|
||||
def __init__(self, api: API):
|
||||
self._api = api
|
||||
|
||||
@property
|
||||
def provider(self) -> str:
|
||||
return self._api.provider
|
||||
@@ -1,9 +1,10 @@
|
||||
import datetime
|
||||
|
||||
from ..api import Api
|
||||
from .model import Schedule
|
||||
|
||||
|
||||
class ScheduleApi:
|
||||
class ScheduleApi(Api):
|
||||
async def get_channels(self) -> list[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
32
gallery/sketch/schedule/cached.py
Normal file
32
gallery/sketch/schedule/cached.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import datetime
|
||||
|
||||
from aiocache import cached
|
||||
|
||||
from gallery.sketch.cached import CachedApi
|
||||
|
||||
from .api import ScheduleApi
|
||||
from .model import Schedule
|
||||
|
||||
|
||||
class CachedScheduleApi(ScheduleApi, CachedApi[ScheduleApi]):
|
||||
CACHE_KEY = "schedule"
|
||||
|
||||
@cached(
|
||||
key_builder=lambda fun, self: f"api.{self.CACHE_KEY}.{self.provider}.channels",
|
||||
alias=CachedApi.CACHE_ALIAS,
|
||||
ttl=CachedApi.CACHE_TTL,
|
||||
)
|
||||
async def get_channels(self) -> list[str]:
|
||||
return await self._api.get_channels()
|
||||
|
||||
@cached(
|
||||
key_builder=lambda fun, self, channel_id, date: (
|
||||
f"api.{self.CACHE_KEY}.{self.provider}.channel.{channel_id}.{date}"
|
||||
),
|
||||
alias=CachedApi.CACHE_ALIAS,
|
||||
ttl=CachedApi.CACHE_TTL,
|
||||
)
|
||||
async def get_channel_schedule(
|
||||
self, channel_id: str, date: datetime.date
|
||||
) -> Schedule:
|
||||
return await self._api.get_channel_schedule(channel_id, date)
|
||||
@@ -1,9 +1,11 @@
|
||||
import datetime
|
||||
|
||||
from ..api import Api
|
||||
from .model import WeatherResponse
|
||||
|
||||
|
||||
class WeatherApi:
|
||||
class WeatherApi(Api):
|
||||
|
||||
async def get_locations(self) -> list[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
40
gallery/sketch/weather/cached.py
Normal file
40
gallery/sketch/weather/cached.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import datetime
|
||||
|
||||
from aiocache import cached
|
||||
|
||||
from gallery.sketch.cached import CachedApi
|
||||
|
||||
from .api import WeatherApi
|
||||
from .model import WeatherResponse
|
||||
|
||||
|
||||
class CachedWeatherApi(WeatherApi, CachedApi[WeatherApi]):
|
||||
CACHE_KEY = "weather"
|
||||
|
||||
@cached(
|
||||
key_builder=lambda fun, self: f"api.{self.CACHE_KEY}.{self.provider}.locations",
|
||||
alias=CachedApi.CACHE_ALIAS,
|
||||
ttl=CachedApi.CACHE_TTL,
|
||||
)
|
||||
async def get_locations(self) -> list[str]:
|
||||
return await self._api.get_locations()
|
||||
|
||||
@cached(
|
||||
key_builder=lambda fun, self, location_id, date: (
|
||||
f"api.{self.CACHE_KEY}.{self.provider}.day.{location_id}.{date}"
|
||||
),
|
||||
alias=CachedApi.CACHE_ALIAS,
|
||||
ttl=CachedApi.CACHE_TTL,
|
||||
)
|
||||
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:
|
||||
return await self._api.get_day(location_id, date)
|
||||
|
||||
@cached(
|
||||
key_builder=lambda fun, self, location_id, date: (
|
||||
f"api.{self.CACHE_KEY}.{self.provider}.day.{location_id}.{date}"
|
||||
),
|
||||
alias=CachedApi.CACHE_ALIAS,
|
||||
ttl=CachedApi.CACHE_TTL,
|
||||
)
|
||||
async def get_days(self, location_id: str, days: int) -> WeatherResponse:
|
||||
return await self._api.get_days(location_id, days)
|
||||
5
gallery/util.py
Normal file
5
gallery/util.py
Normal file
@@ -0,0 +1,5 @@
|
||||
class TimeUnit:
|
||||
SECOND = 1
|
||||
MINUTE = 60 * SECOND
|
||||
HOUR = 60 * MINUTE
|
||||
DAY = 24 * HOUR
|
||||
Reference in New Issue
Block a user