From 8fd750401ee7c4886f760102b26ac14b9daab8fd Mon Sep 17 00:00:00 2001 From: "herve.le-bars" Date: Fri, 25 Oct 2024 11:01:26 +0200 Subject: [PATCH 1/6] feat: redis caching service to container factory --- backend/bloom/container.py | 8 ++++++++ backend/bloom/routers/v1/vessels.py | 20 +++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/backend/bloom/container.py b/backend/bloom/container.py index 87b5c359..c0af3a03 100644 --- a/backend/bloom/container.py +++ b/backend/bloom/container.py @@ -13,6 +13,7 @@ from bloom.services.metrics import MetricsService from bloom.usecase.GenerateAlerts import GenerateAlerts from dependency_injector import containers, providers +import redis class UseCases(containers.DeclarativeContainer): @@ -23,6 +24,13 @@ class UseCases(containers.DeclarativeContainer): db_url=db_url, ) + service_cache = providers.Factory( + redis.Redis, + host=settings.redis_host, + port=settings.redis_port, + db=0 + ) + vessel_repository = providers.Factory( VesselRepository, session_factory=db.provided.session, diff --git a/backend/bloom/routers/v1/vessels.py b/backend/bloom/routers/v1/vessels.py index bf396c65..fe087a00 100644 --- a/backend/bloom/routers/v1/vessels.py +++ b/backend/bloom/routers/v1/vessels.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException,Request from redis import Redis from bloom.config import settings from bloom.container import UseCases @@ -24,26 +24,28 @@ rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0, password=settings.redis_password) @router.get("/vessels") -async def list_vessels(nocache:bool=False,key: str = Depends(X_API_KEY_HEADER)): +async def list_vessels(request: Request, + nocache:bool=False, + key: str = Depends(X_API_KEY_HEADER)): + use_cases = UseCases() check_apikey(key) - endpoint=f"/vessels" - cache= rd.get(endpoint) + cache_key=f"{request.url.path}" + cache= use_cases.service_cache().get(cache_key) start = time.time() if cache and not nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") + logger.debug(f"{cache_key} cached ({settings.redis_cache_expiration})s") payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") + logger.debug(f"{cache_key} elapsed Time: {time.time()-start}") return payload else: - use_cases = UseCases() vessel_repository = use_cases.vessel_repository() db = use_cases.db() with db.session() as session: json_data = [json.loads(v.model_dump_json() if v else "{}") for v in vessel_repository.get_vessels_list(session)] - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) + rd.set(cache_key, json.dumps(json_data)) + rd.expire(cache_key,settings.redis_cache_expiration) return json_data @router.get("/vessels/{vessel_id}") From 7632ca6b4fc365142c2d2e2eccffcb538fd878d8 Mon Sep 17 00:00:00 2001 From: "herve.le-bars" Date: Fri, 8 Nov 2024 14:41:27 +0100 Subject: [PATCH 2/6] ++ # Conflicts: # backend/bloom/routers/v1/vessels.py --- backend/bloom/dependencies.py | 15 +++++++++++++-- backend/bloom/routers/v1/vessels.py | 16 ++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/backend/bloom/dependencies.py b/backend/bloom/dependencies.py index 3014e1b4..e8b51354 100644 --- a/backend/bloom/dependencies.py +++ b/backend/bloom/dependencies.py @@ -1,13 +1,19 @@ -from fastapi import Request, HTTPException +from fastapi import Request, HTTPException, Depends from bloom.config import settings from fastapi.security import APIKeyHeader from pydantic import BaseModel, ConfigDict, Field,conint from pydantic.generics import GenericModel from datetime import datetime, timedelta -from typing import Generic,TypeVar, List +from typing import Generic,TypeVar, List, Annotated from enum import Enum +from bloom.container import UseCases +import redis + +UserCasesDep= Annotated[UseCases, Depends(UseCases)] +CacheServiceDep= Annotated[redis.Redis, Depends(UseCases.service_cache)] + ## Reference for pagination design ## https://jayhawk24.hashnode.dev/how-to-implement-pagination-in-fastapi-feat-sqlalchemy X_API_KEY_HEADER=APIKeyHeader(name="x-key") @@ -15,6 +21,9 @@ class CachedRequest(BaseModel): nocache:bool=False + + + def check_apikey(key:str): if key != settings.api_key : raise HTTPException(status_code=401, detail="Unauthorized") @@ -42,6 +51,7 @@ class PaginatedRequest(BaseModel): order_by: OrderByRequest = OrderByEnum.ascending + class PageParams(BaseModel): """ Request query params for paginated API. """ offset: conint(ge=0) = 0 @@ -57,6 +67,7 @@ class PagedResponseSchema(GenericModel,Generic[T]): previous: str|None results: List[T] + def paginate(request: Request, page_params: PageParams, query, ResponseSchema: BaseModel) -> PagedResponseSchema[T]: """Paginate the query.""" diff --git a/backend/bloom/routers/v1/vessels.py b/backend/bloom/routers/v1/vessels.py index fe087a00..41749027 100644 --- a/backend/bloom/routers/v1/vessels.py +++ b/backend/bloom/routers/v1/vessels.py @@ -21,16 +21,20 @@ X_API_KEY_HEADER,check_apikey) router = APIRouter() -rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0, password=settings.redis_password) +#rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0) + +UserCasesDep= Annotated[UseCases, Depends(UseCases)] +CacheServiceDep= Annotated[redis.Redis, Depends(UseCases.service_cache)] @router.get("/vessels") async def list_vessels(request: Request, nocache:bool=False, - key: str = Depends(X_API_KEY_HEADER)): - use_cases = UseCases() + key: str = Depends(X_API_KEY_HEADER), + use_cases = UserCasesDep, + cache_service=CacheServiceDep): check_apikey(key) cache_key=f"{request.url.path}" - cache= use_cases.service_cache().get(cache_key) + cache= cache_service.get(cache_key) start = time.time() if cache and not nocache: logger.debug(f"{cache_key} cached ({settings.redis_cache_expiration})s") @@ -44,8 +48,8 @@ async def list_vessels(request: Request, json_data = [json.loads(v.model_dump_json() if v else "{}") for v in vessel_repository.get_vessels_list(session)] - rd.set(cache_key, json.dumps(json_data)) - rd.expire(cache_key,settings.redis_cache_expiration) + cache_service.set(cache_key, json.dumps(json_data)) + cache_service.expire(cache_key,settings.redis_cache_expiration) return json_data @router.get("/vessels/{vessel_id}") From 9a270e8f043f3503a15e2212c28b15619eab7e45 Mon Sep 17 00:00:00 2001 From: "herve.le-bars" Date: Mon, 11 Nov 2024 22:07:24 +0100 Subject: [PATCH 3/6] feat: create @cache decorator to manage Redis cache via UseCases container --- backend/bloom/container.py | 3 +- backend/bloom/dependencies.py | 115 ++++------- backend/bloom/domain/metrics.py | 12 +- .../repositories/repository_excursion.py | 4 +- .../infra/repositories/repository_segment.py | 10 +- backend/bloom/routers/requests.py | 58 ++++++ backend/bloom/routers/v1/cache.py | 5 +- backend/bloom/routers/v1/metrics.py | 105 +++------- backend/bloom/routers/v1/ports.py | 46 ++--- backend/bloom/routers/v1/vessels.py | 190 ++++++++---------- backend/bloom/routers/v1/zones.py | 115 ++++------- backend/bloom/services/metrics.py | 21 +- 12 files changed, 276 insertions(+), 408 deletions(-) create mode 100644 backend/bloom/routers/requests.py diff --git a/backend/bloom/container.py b/backend/bloom/container.py index c0af3a03..6afefbb8 100644 --- a/backend/bloom/container.py +++ b/backend/bloom/container.py @@ -24,10 +24,11 @@ class UseCases(containers.DeclarativeContainer): db_url=db_url, ) - service_cache = providers.Factory( + cache_service = providers.Factory( redis.Redis, host=settings.redis_host, port=settings.redis_port, + password=settings.redis_password, db=0 ) diff --git a/backend/bloom/dependencies.py b/backend/bloom/dependencies.py index e8b51354..8341f778 100644 --- a/backend/bloom/dependencies.py +++ b/backend/bloom/dependencies.py @@ -1,95 +1,52 @@ from fastapi import Request, HTTPException, Depends from bloom.config import settings from fastapi.security import APIKeyHeader -from pydantic import BaseModel, ConfigDict, Field,conint -from pydantic.generics import GenericModel -from datetime import datetime, timedelta -from typing import Generic,TypeVar, List, Annotated -from enum import Enum +from pydantic import BaseModel +from functools import wraps +import time +import json +from bloom.logger import logger from bloom.container import UseCases -import redis - - - -UserCasesDep= Annotated[UseCases, Depends(UseCases)] -CacheServiceDep= Annotated[redis.Redis, Depends(UseCases.service_cache)] ## Reference for pagination design ## https://jayhawk24.hashnode.dev/how-to-implement-pagination-in-fastapi-feat-sqlalchemy X_API_KEY_HEADER=APIKeyHeader(name="x-key") -class CachedRequest(BaseModel): - nocache:bool=False - +## FastAPI endpoint decorator to manage Redis caching +# Needs to add request:Request and nocache:bool parameters to all endpoints +# using @cache decorator +# Example: +# @router.get('/my/endpoint') +# @cache +# def my_endpoint_function(request: Request, # needed by @cache +# ... +# nocache:bool = False, # needed by @cache +# ): +# ... +def cache(func): + @wraps(func) + async def wrapper(*args, **kwargs): + start = time.time() + request=kwargs['request'] + cache_service=UseCases().cache_service() + nocache=True if request.query_params.get('nocache').lower() == 'true' else False + cache_key=f"{request.url.path}/{request.query_params}" + incache=False + #incache= cache_service.get(cache_key) + if incache and not nocache: + logger.debug(f"{cache_key} cached ({settings.redis_cache_expiration})s") + payload=json.loads(incache) + else: + payload=await func(*args, **kwargs) + #cache_service.set(cache_key, json.dumps(payload)) + #cache_service.expire(cache_key,settings.redis_cache_expiration) + logger.debug(f"{cache_key} elapsed Time: {time.time()-start}") + return payload + return wrapper def check_apikey(key:str): if key != settings.api_key : raise HTTPException(status_code=401, detail="Unauthorized") return True - -def check_cache(request:Request): - cache= rd.get(request.url.path) - - -class DatetimeRangeRequest(BaseModel): - start_at: datetime = Field(default=datetime.now()-timedelta(days=7)) - end_at: datetime = datetime.now() - - -class OrderByEnum(str, Enum): - ascending = "ASC" - descending = "DESC" - -class OrderByRequest(BaseModel): - order: OrderByEnum = OrderByEnum.ascending - -class PaginatedRequest(BaseModel): - offset: int|None = 0 - limit: int|None = 100 - order_by: OrderByRequest = OrderByEnum.ascending - - - -class PageParams(BaseModel): - """ Request query params for paginated API. """ - offset: conint(ge=0) = 0 - limit: conint(ge=1, le=100000) = 100 - -T = TypeVar("T") - -class PagedResponseSchema(GenericModel,Generic[T]): - total: int - limit: int - offset: int - next: str|None - previous: str|None - results: List[T] - - -def paginate(request: Request, page_params: PageParams, query, ResponseSchema: BaseModel) -> PagedResponseSchema[T]: - """Paginate the query.""" - - print(f"{request.url.scheme}://{request.client}/{request.url.path}") - paginated_query = query.offset((page_params.offset) * page_params.limit).limit(page_params.limit).all() - - return PagedResponseSchema( - total=query.count(), - offset=page_params.offset, - limit=page_params.limit, - next="", - previous="", - results=[ResponseSchema.from_orm(item) for item in paginated_query], - ) - -class TotalTimeActivityTypeEnum(str, Enum): - total_time_at_sea: str = "Total Time at Sea" - total_time_in_amp: str = "Total Time in AMP" - total_time_in_territorial_waters: str = "Total Time in Territorial Waters" - total_time_in_zones_with_no_fishing_rights: str = "Total Time in zones with no fishing rights" - total_time_fishing: str = "Total Time Fishing" - total_time_fishing_in_amp: str = "Total Time Fishing in AMP" - total_time_fishing_in_territorial_waters: str = "Total Time Fishing in Territorial Waters" - total_time_fishing_in_zones_with_no_fishing_rights: str = "Total Time Fishing in zones with no fishing rights" - total_time_fishing_in_extincting_amp: str = "Total Time in Extincting AMP" \ No newline at end of file diff --git a/backend/bloom/domain/metrics.py b/backend/bloom/domain/metrics.py index fa9501f2..890840c3 100644 --- a/backend/bloom/domain/metrics.py +++ b/backend/bloom/domain/metrics.py @@ -4,7 +4,17 @@ from datetime import datetime, timedelta from enum import Enum from bloom.domain.vessel import Vessel -from bloom.dependencies import TotalTimeActivityTypeEnum + +class TotalTimeActivityTypeEnum(str, Enum): + total_time_at_sea: str = "Total Time at Sea" + total_time_in_amp: str = "Total Time in AMP" + total_time_in_territorial_waters: str = "Total Time in Territorial Waters" + total_time_in_zones_with_no_fishing_rights: str = "Total Time in zones with no fishing rights" + total_time_fishing: str = "Total Time Fishing" + total_time_fishing_in_amp: str = "Total Time Fishing in AMP" + total_time_fishing_in_territorial_waters: str = "Total Time Fishing in Territorial Waters" + total_time_fishing_in_zones_with_no_fishing_rights: str = "Total Time Fishing in zones with no fishing rights" + total_time_fishing_in_extincting_amp: str = "Total Time in Extincting AMP" class ResponseMetricsVesselInActivitySchema(BaseModel): model_config = ConfigDict(from_attributes=True) diff --git a/backend/bloom/infra/repositories/repository_excursion.py b/backend/bloom/infra/repositories/repository_excursion.py index f2801fc1..dba05a78 100644 --- a/backend/bloom/infra/repositories/repository_excursion.py +++ b/backend/bloom/infra/repositories/repository_excursion.py @@ -37,7 +37,7 @@ def get_param_from_last_excursion(self, session: Session, vessel_id: int) -> Uni def get_excursions_by_vessel_id(self, session: Session, vessel_id: int) -> List[Excursion]: """Recheche l'excursion en cours d'un bateau, c'est-à-dire l'excursion qui n'a pas de date d'arrivée""" stmt = select(sql_model.Excursion).where(sql_model.Excursion.vessel_id == vessel_id) - result = session.execute(stmt).scalars() + result = session.execute(stmt).scalars().all() if not result: return [] return [ExcursionRepository.map_to_domain(r) for r in result] @@ -46,7 +46,7 @@ def get_vessel_excursion_by_id(self, session: Session, vessel_id: int, excursion """Recheche l'excursion en cours d'un bateau, c'est-à-dire l'excursion qui n'a pas de date d'arrivée""" stmt = select(sql_model.Excursion).where((sql_model.Excursion.vessel_id == vessel_id) & (sql_model.Excursion.id == excursion_id)) - result = session.execute(stmt).scalar() + result = session.execute(stmt).scalar_one_or_none() if not result: return None return ExcursionRepository.map_to_domain(result) diff --git a/backend/bloom/infra/repositories/repository_segment.py b/backend/bloom/infra/repositories/repository_segment.py index 10ad8c1a..3cd20bca 100644 --- a/backend/bloom/infra/repositories/repository_segment.py +++ b/backend/bloom/infra/repositories/repository_segment.py @@ -112,9 +112,9 @@ def list_vessel_excursion_segments(self,session,vessel_id:int,excursions_id: int sql_model.Segment.excursion_id == sql_model.Excursion.id ).where( sql_model.Segment.excursion_id == excursions_id, sql_model.Excursion.vessel_id == vessel_id) - result = session.execute(stmt) - if result is not None : - return [ SegmentRepository.map_to_domain(record) for record in result.scalars()] + result = session.execute(stmt).scalars().all() + if result: + return [ SegmentRepository.map_to_domain(record) for record in result] else: return [] @@ -127,7 +127,7 @@ def get_vessel_excursion_segment_by_id(self,session,vessel_id:int,excursions_id: ).where( sql_model.Segment.excursion_id == excursions_id, sql_model.Excursion.vessel_id == vessel_id, sql_model.Segment.id == segment_id) - result = session.execute(stmt) + result = session.execute(stmt).scalar_one_or_none() if result is not None : return [ SegmentRepository.map_to_domain(record) for record in result.scalars()][0] else: @@ -154,7 +154,7 @@ def get_last_vessel_id_segments(self, session: Session) -> pd.DataFrame: ).filter( sql_model.Segment.last_vessel_segment == True ) - q = session.execute(stmt) + q = session.execute(stmt).scalars().all() if not q: return None df = pd.DataFrame(q, columns=["vessel_id", "excursion_id", "end_position", "timestamp_end", 'heading_at_end', diff --git a/backend/bloom/routers/requests.py b/backend/bloom/routers/requests.py new file mode 100644 index 00000000..4fe4b896 --- /dev/null +++ b/backend/bloom/routers/requests.py @@ -0,0 +1,58 @@ +from pydantic import BaseModel, Field,conint +from fastapi import Request +from datetime import datetime, timedelta +from enum import Enum +from typing import Generic,TypeVar, List + + +class CachedRequest(BaseModel): + nocache:bool=False + + +class OrderByEnum(str, Enum): + ascending = "ASC" + descending = "DESC" + +class DatetimeRangeRequest(BaseModel): + start_at: datetime = Field(default=datetime.now()-timedelta(days=7)) + end_at: datetime = datetime.now() + +class OrderByRequest(BaseModel): + order: OrderByEnum = OrderByEnum.ascending + +class PaginatedRequest(BaseModel): + offset: int|None = 0 + limit: int|None = 100 + order_by: OrderByRequest = OrderByEnum.ascending + + +class PageParams(BaseModel): + """ Request query params for paginated API. """ + offset: conint(ge=0) = 0 + limit: conint(ge=1, le=100000) = 100 + +T = TypeVar("T") + +class PagedResponseSchema(BaseModel,Generic[T]): + total: int + limit: int + offset: int + next: str|None + previous: str|None + results: List[T] + + +def paginate(request: Request, page_params: PageParams, query, ResponseSchema: BaseModel) -> PagedResponseSchema[T]: + """Paginate the query.""" + + print(f"{request.url.scheme}://{request.client}/{request.url.path}") + paginated_query = query.offset((page_params.offset) * page_params.limit).limit(page_params.limit).all() + + return PagedResponseSchema( + total=query.count(), + offset=page_params.offset, + limit=page_params.limit, + next="", + previous="", + results=[ResponseSchema.from_orm(item) for item in paginated_query], + ) \ No newline at end of file diff --git a/backend/bloom/routers/v1/cache.py b/backend/bloom/routers/v1/cache.py index 7de9a808..653d8b0c 100644 --- a/backend/bloom/routers/v1/cache.py +++ b/backend/bloom/routers/v1/cache.py @@ -2,14 +2,13 @@ from bloom.config import settings import redis from bloom.config import settings -from bloom.logger import logger from bloom.dependencies import (X_API_KEY_HEADER,check_apikey) +from bloom.container import UseCases router = APIRouter() -rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0, password=settings.redis_password) @router.get("/cache/all/flush") async def cache_all_flush(request:Request,key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - rd.flushall() + UseCases().cache_service().flushall() return {"code":0} \ No newline at end of file diff --git a/backend/bloom/routers/v1/metrics.py b/backend/bloom/routers/v1/metrics.py index a84723ce..6b61e52f 100644 --- a/backend/bloom/routers/v1/metrics.py +++ b/backend/bloom/routers/v1/metrics.py @@ -17,10 +17,8 @@ ResponseMetricsZoneVisitedSchema, ResponseMetricsZoneVisitingTimeByVesselSchema, ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema) -from bloom.dependencies import ( DatetimeRangeRequest, - PaginatedRequest,OrderByRequest,OrderByEnum, - paginate,PagedResponseSchema,PageParams, - X_API_KEY_HEADER, check_apikey,CachedRequest) +from bloom.routers.requests import DatetimeRangeRequest,OrderByRequest,PageParams,CachedRequest +from bloom.dependencies import ( X_API_KEY_HEADER, check_apikey,cache) from bloom.domain.metrics import TotalTimeActivityTypeRequest @@ -29,6 +27,7 @@ @router.get("/metrics/vessels-in-activity", response_model=list[ResponseMetricsVesselInActivitySchema]) +@cache def read_metrics_vessels_in_activity_total(request: Request, datetime_range: DatetimeRangeRequest = Depends(), pagination: PageParams = Depends(), @@ -37,27 +36,16 @@ def read_metrics_vessels_in_activity_total(request: Request, key: str = Depends(X_API_KEY_HEADER), ): check_apikey(key) - cache_key=f"{request.url.path}?{request.query_params}" - cache_payload= rd.get(cache_key) - start = time.time() - payload = [] - if cache_payload and not caching.nocache: - logger.debug(f"{cache_key} cached ({settings.redis_cache_expiration})s") - payload=loads(cache_payload) - else: - use_cases = UseCases() - MetricsService=use_cases.metrics_service() - payload=MetricsService.getVesselsInActivity(datetime_range=datetime_range, - pagination=pagination, - order=order) - serialized=dumps(payload) - rd.set(cache_key, serialized) - rd.expire(cache_key,settings.redis_cache_expiration) - logger.debug(f"{cache_key} elapsed Time: {time.time()-start}") + use_cases = UseCases() + MetricsService=use_cases.metrics_service() + payload=MetricsService.getVesselsInActivity(datetime_range=datetime_range, + pagination=pagination, + order=order) return payload @router.get("/metrics/zone-visited", response_model=list[ResponseMetricsZoneVisitedSchema]) +@cache def read_zone_visited_total(request: Request, datetime_range: DatetimeRangeRequest = Depends(), pagination: PageParams = Depends(), @@ -65,27 +53,16 @@ def read_zone_visited_total(request: Request, caching: CachedRequest = Depends(), key: str = Depends(X_API_KEY_HEADER),): check_apikey(key) - cache_key=f"{request.url.path}?{request.query_params}" - cache_payload= rd.get(cache_key) - start = time.time() - payload=[] - if cache_payload and not caching.nocache: - logger.debug(f"{cache_key} cached ({settings.redis_cache_expiration})s") - payload=loads(cache_payload) - else: - use_cases = UseCases() - MetricsService=use_cases.metrics_service() - payload=MetricsService.getZoneVisited(datetime_range=datetime_range, - pagination=pagination, - order=order) - serialized=dumps(payload) - rd.set(cache_key, serialized) - rd.expire(cache_key,settings.redis_cache_expiration) - logger.debug(f"{cache_key} elapsed Time: {time.time()-start}") + use_cases = UseCases() + MetricsService=use_cases.metrics_service() + payload=MetricsService.getZoneVisited(datetime_range=datetime_range, + pagination=pagination, + order=order) return payload @router.get("/metrics/zones/{zone_id}/visiting-time-by-vessel", response_model=list[ResponseMetricsZoneVisitingTimeByVesselSchema]) +@cache def read_metrics_zone_visiting_time_by_vessel(request: Request, zone_id: int, datetime_range: DatetimeRangeRequest = Depends(), @@ -94,30 +71,19 @@ def read_metrics_zone_visiting_time_by_vessel(request: Request, caching: CachedRequest = Depends(), key: str = Depends(X_API_KEY_HEADER),): check_apikey(key) - cache_key=f"{request.url.path}?{request.query_params}" - cache_payload= rd.get(cache_key) - start = time.time() - payload=[] - if cache_payload and not caching.nocache: - logger.debug(f"{cache_key} cached ({settings.redis_cache_expiration})s") - payload=loads(cache_payload) - else: - use_cases = UseCases() - MetricsService=use_cases.metrics_service() - payload=MetricsService.getZoneVisitingTimeByVessel( - zone_id=zone_id, - datetime_range=datetime_range, - pagination=pagination, - order=order) - serialized=dumps(payload) - rd.set(cache_key, serialized) - rd.expire(cache_key,settings.redis_cache_expiration) - logger.debug(f"{cache_key} elapsed Time: {time.time()-start}") + use_cases = UseCases() + MetricsService=use_cases.metrics_service() + payload=MetricsService.getZoneVisitingTimeByVessel( + zone_id=zone_id, + datetime_range=datetime_range, + pagination=pagination, + order=order) return payload @router.get("/metrics/vessels/{vessel_id}/activity/{activity_type}", response_model=ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema) +@cache def read_metrics_vessels_visits_by_activity_type(request: Request, vessel_id: int, activity_type: TotalTimeActivityTypeRequest = Depends(), @@ -125,23 +91,10 @@ def read_metrics_vessels_visits_by_activity_type(request: Request, caching: CachedRequest = Depends(), key: str = Depends(X_API_KEY_HEADER),): check_apikey(key) - cache_key=f"{request.url.path}?{request.query_params}" - cache_payload= rd.get(cache_key) - start = time.time() - payload=[] - if cache_payload and not caching.nocache: - logger.debug(f"{cache_key} cached ({settings.redis_cache_expiration})s") - payload=loads(cache_payload) - else: - use_cases = UseCases() - MetricsService=use_cases.metrics_service() - payload=MetricsService.getVesselVisitsByActivityType( - vessel_id=vessel_id, - activity_type=activity_type, - datetime_range=datetime_range) - serialized=dumps(payload) - rd.set(cache_key, serialized) - rd.expire(cache_key,settings.redis_cache_expiration) - - logger.debug(f"{cache_key} elapsed Time: {time.time()-start}") + use_cases = UseCases() + MetricsService=use_cases.metrics_service() + payload=MetricsService.getVesselVisitsByActivityType( + vessel_id=vessel_id, + activity_type=activity_type, + datetime_range=datetime_range) return payload \ No newline at end of file diff --git a/backend/bloom/routers/v1/ports.py b/backend/bloom/routers/v1/ports.py index be0fe278..95c45f71 100644 --- a/backend/bloom/routers/v1/ports.py +++ b/backend/bloom/routers/v1/ports.py @@ -1,56 +1,34 @@ -from fastapi import APIRouter, Depends, HTTPException, Request +from fastapi import APIRouter, Depends, Request from redis import Redis from bloom.config import settings from bloom.container import UseCases -from pydantic import BaseModel, Field -from typing_extensions import Annotated, Literal, Optional -from datetime import datetime, timedelta -import time -import redis import json -from sqlalchemy import select, func, and_, or_ -from bloom.infra.database import sql_model -from bloom.infra.repositories.repository_segment import SegmentRepository from bloom.config import settings from bloom.container import UseCases -from bloom.domain.vessel import Vessel from bloom.logger import logger -from bloom.dependencies import ( DatetimeRangeRequest, - PaginatedRequest,OrderByRequest,OrderByEnum, - paginate,PagedResponseSchema,PageParams, - X_API_KEY_HEADER,check_apikey,CachedRequest) +from bloom.routers.requests import CachedRequest +from bloom.dependencies import ( X_API_KEY_HEADER,check_apikey,cache) from bloom.config import settings router = APIRouter() -rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0, password=settings.redis_password) @router.get("/ports") +@cache async def list_ports( request:Request, caching: CachedRequest = Depends(), key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - endpoint=f"/ports" - cache= rd.get(endpoint) - start = time.time() - if cache and not caching.nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return payload - else: - use_cases = UseCases() - port_repository = use_cases.port_repository() - db = use_cases.db() - with db.session() as session: - json_data = [json.loads(p.model_dump_json() if p else "{}") - for p in port_repository.get_all_ports(session)] - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return json_data + use_cases = UseCases() + port_repository = use_cases.port_repository() + db = use_cases.db() + with db.session() as session: + json_data = [json.loads(p.model_dump_json() if p else "{}") + for p in port_repository.get_all_ports(session)] + return json_data @router.get("/ports/{port_id}") +@cache async def get_port(port_id:int, key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) diff --git a/backend/bloom/routers/v1/vessels.py b/backend/bloom/routers/v1/vessels.py index 41749027..e5d762a0 100644 --- a/backend/bloom/routers/v1/vessels.py +++ b/backend/bloom/routers/v1/vessels.py @@ -1,153 +1,123 @@ -from fastapi import APIRouter, Depends, HTTPException,Request +from fastapi import APIRouter, Depends, Request from redis import Redis from bloom.config import settings from bloom.container import UseCases -from pydantic import BaseModel, Field -from typing_extensions import Annotated, Literal, Optional -from datetime import datetime, timedelta -import time -import redis +from typing import Any import json -from sqlalchemy import select, func, and_, or_ -from bloom.infra.database import sql_model -from bloom.infra.repositories.repository_segment import SegmentRepository from bloom.config import settings from bloom.container import UseCases -from bloom.domain.vessel import Vessel from bloom.logger import logger -from bloom.dependencies import ( DatetimeRangeRequest, - PaginatedRequest,OrderByRequest,OrderByEnum, - paginate,PagedResponseSchema,PageParams, - X_API_KEY_HEADER,check_apikey) - +from bloom.routers.requests import DatetimeRangeRequest,OrderByRequest,PageParams +from bloom.dependencies import (X_API_KEY_HEADER,check_apikey,cache) router = APIRouter() -#rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0) -UserCasesDep= Annotated[UseCases, Depends(UseCases)] -CacheServiceDep= Annotated[redis.Redis, Depends(UseCases.service_cache)] @router.get("/vessels") -async def list_vessels(request: Request, - nocache:bool=False, - key: str = Depends(X_API_KEY_HEADER), - use_cases = UserCasesDep, - cache_service=CacheServiceDep): +@cache +async def list_vessels(request: Request, # used by @cache + nocache:bool=False, # used by @cache + key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - cache_key=f"{request.url.path}" - cache= cache_service.get(cache_key) - start = time.time() - if cache and not nocache: - logger.debug(f"{cache_key} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{cache_key} elapsed Time: {time.time()-start}") - return payload - else: - vessel_repository = use_cases.vessel_repository() - db = use_cases.db() - with db.session() as session: - - json_data = [json.loads(v.model_dump_json() if v else "{}") + use_cases = UseCases() + vessel_repository = use_cases.vessel_repository() + db = use_cases.db() + with db.session() as session: + return [json.loads(v.model_dump_json() if v else "{}") for v in vessel_repository.get_vessels_list(session)] - cache_service.set(cache_key, json.dumps(json_data)) - cache_service.expire(cache_key,settings.redis_cache_expiration) - return json_data @router.get("/vessels/{vessel_id}") -async def get_vessel(vessel_id: int,key: str = Depends(X_API_KEY_HEADER)): +@cache +async def get_vessel(request: Request, # used by @cache + vessel_id: int, + nocache:bool=False, # used by @cache + key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) use_cases = UseCases() vessel_repository = use_cases.vessel_repository() db = use_cases.db() + json_data={} with db.session() as session: - return vessel_repository.get_vessel_by_id(session,vessel_id) + data=vessel_repository.get_vessel_by_id(session,vessel_id) + return json.loads(vessel_repository.map_to_domain(data).model_dump_json()) if data else {} @router.get("/vessels/all/positions/last") -async def list_all_vessel_last_position(nocache:bool=False,key: str = Depends(X_API_KEY_HEADER)): +@cache +async def list_all_vessel_last_position(request: Request, # used by @cache + nocache:bool=False, # used by @cache + key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - endpoint=f"/vessels/all/positions/last" - cache= rd.get(endpoint) - start = time.time() - if cache and not nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return payload - else: - use_cases = UseCases() - segment_repository = use_cases.segment_repository() - db = use_cases.db() - with db.session() as session: - json_data = [json.loads(p.model_dump_json() if p else "{}") - for p in segment_repository.get_all_vessels_last_position(session)] - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return json_data + use_cases = UseCases() + use_cases = UseCases() + segment_repository = use_cases.segment_repository() + db = use_cases.db() + json_data={} + with db.session() as session: + json_data = [json.loads(p.model_dump_json() if p else "{}") + for p in segment_repository.get_all_vessels_last_position(session)] + return json_data @router.get("/vessels/{vessel_id}/positions/last") -async def get_vessel_last_position(vessel_id: int, nocache:bool=False,key: str = Depends(X_API_KEY_HEADER)): +@cache +async def get_vessel_last_position(request: Request, # used by @cache + vessel_id: int, + nocache:bool=False, # used by @cache + key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - endpoint=f"/vessels/{vessel_id}/positions/last" - cache= rd.get(endpoint) - start = time.time() - if cache and not nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return payload - else: - use_cases = UseCases() - segment_repository = use_cases.segment_repository() - db = use_cases.db() - with db.session() as session: - result=segment_repository.get_vessel_last_position(session,vessel_id) - json_data = json.loads(result.model_dump_json() if result else "{}") - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return json_data + use_cases = UseCases() + use_cases = UseCases() + segment_repository = use_cases.segment_repository() + db = use_cases.db() + json_data={} + with db.session() as session: + result=segment_repository.get_vessel_last_position(session,vessel_id) + json_data = json.loads(result.model_dump_json() if result else "{}") + return json_data @router.get("/vessels/{vessel_id}/excursions") -async def list_vessel_excursions(vessel_id: int, nocache:bool=False, +@cache +async def list_vessel_excursions(request: Request, # used by @cache + vessel_id: int, + nocache:bool=False, # used by @cache datetime_range: DatetimeRangeRequest = Depends(), pagination: PageParams = Depends(), order: OrderByRequest = Depends(), key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - endpoint=f"/vessels/{vessel_id}/excursions" - cache= rd.get(endpoint) - start = time.time() - if cache and not nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return payload - else: - use_cases = UseCases() - excursion_repository = use_cases.excursion_repository() - db = use_cases.db() - with db.session() as session: - json_data = [json.loads(p.model_dump_json() if p else "{}") - for p in excursion_repository.get_excursions_by_vessel_id(session,vessel_id)] - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return json_data + use_cases = UseCases() + use_cases = UseCases() + excursion_repository = use_cases.excursion_repository() + db = use_cases.db() + json_data={} + with db.session() as session: + json_data = [json.loads(p.model_dump_json() if p else "{}") + for p in excursion_repository.get_excursions_by_vessel_id(session,vessel_id)] + return json_data @router.get("/vessels/{vessel_id}/excursions/{excursions_id}") -async def get_vessel_excursion(vessel_id: int,excursions_id: int,key: str = Depends(X_API_KEY_HEADER)): +@cache +async def get_vessel_excursion(request: Request, # used by @cache + vessel_id: int, + excursions_id: int, + nocache:bool=False, # used by @cache + key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) use_cases = UseCases() excursion_repository = use_cases.excursion_repository() db = use_cases.db() + json_data={} with db.session() as session: - return excursion_repository.get_vessel_excursion_by_id(session,vessel_id,excursions_id) + result = excursion_repository.get_vessel_excursion_by_id(session,vessel_id,excursions_id) + json_data = json.loads(result.model_dump_json() if result else "{}") + return json_data @router.get("/vessels/{vessel_id}/excursions/{excursions_id}/segments") -async def list_vessel_excursion_segments(vessel_id: int, +@cache +async def list_vessel_excursion_segments(request: Request, # used by @cache + vessel_id: int, excursions_id: int, + nocache:bool=False, # used by @cache key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) use_cases = UseCases() @@ -157,13 +127,19 @@ async def list_vessel_excursion_segments(vessel_id: int, return segment_repository.list_vessel_excursion_segments(session,vessel_id,excursions_id) @router.get("/vessels/{vessel_id}/excursions/{excursions_id}/segments/{segment_id}") -async def get_vessel_excursion_segment(vessel_id: int, +@cache +async def get_vessel_excursion_segment(request: Request, # used by @cache + vessel_id: int, excursions_id: int, segment_id:int, + nocache:bool=False, # used by @cache key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) use_cases = UseCases() segment_repository = use_cases.segment_repository() db = use_cases.db() + json_data={} with db.session() as session: - return segment_repository.get_vessel_excursion_segment_by_id(session,vessel_id,excursions_id,segment_id) \ No newline at end of file + result = segment_repository.get_vessel_excursion_segment_by_id(session,vessel_id,excursions_id,segment_id) + json_data = json.loads(result.model_dump_json() if result else "{}") + return json_data \ No newline at end of file diff --git a/backend/bloom/routers/v1/zones.py b/backend/bloom/routers/v1/zones.py index c3ef2b67..9591c1f4 100644 --- a/backend/bloom/routers/v1/zones.py +++ b/backend/bloom/routers/v1/zones.py @@ -15,107 +15,60 @@ from bloom.container import UseCases from bloom.domain.vessel import Vessel from bloom.logger import logger -from bloom.dependencies import ( DatetimeRangeRequest, - PaginatedRequest,OrderByRequest,OrderByEnum, - paginate,PagedResponseSchema,PageParams, - X_API_KEY_HEADER,check_apikey) +from bloom.dependencies import (X_API_KEY_HEADER,check_apikey,cache) router = APIRouter() -rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0, password=settings.redis_password) @router.get("/zones") +@cache async def list_zones(request:Request,nocache:bool=False,key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - endpoint=f"/zones" - cache= rd.get(endpoint) - start = time.time() - if cache and not nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return payload - else: - use_cases = UseCases() - zone_repository = use_cases.zone_repository() - db = use_cases.db() - with db.session() as session: - json_data = [json.loads(z.model_dump_json() if z else "{}") - for z in zone_repository.get_all_zones(session)] - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return json_data + use_cases = UseCases() + zone_repository = use_cases.zone_repository() + db = use_cases.db() + with db.session() as session: + json_data = [json.loads(z.model_dump_json() if z else "{}") + for z in zone_repository.get_all_zones(session)] + return json_data @router.get("/zones/all/categories") +@cache async def list_zone_categories(request:Request,nocache:bool=False,key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - endpoint=f"/zones/all/categories" - cache= rd.get(endpoint) - start = time.time() - if cache and not nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return payload - else: - use_cases = UseCases() - zone_repository = use_cases.zone_repository() - db = use_cases.db() - with db.session() as session: - json_data = [json.loads(z.model_dump_json() if z else "{}") - for z in zone_repository.get_all_zone_categories(session)] - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return json_data + use_cases = UseCases() + zone_repository = use_cases.zone_repository() + db = use_cases.db() + with db.session() as session: + json_data = [json.loads(z.model_dump_json() if z else "{}") + for z in zone_repository.get_all_zone_categories(session)] + return json_data @router.get("/zones/by-category/{category}/by-sub-category/{sub}") +@cache async def get_zone_all_by_category(category:str="all",sub:str=None,nocache:bool=False,key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - endpoint=f"/zones/by-category/{category}/by-sub-category/{sub}" - cache= rd.get(endpoint) - start = time.time() - if cache and not nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return payload - else: - use_cases = UseCases() - zone_repository = use_cases.zone_repository() - db = use_cases.db() - with db.session() as session: - json_data = [json.loads(z.model_dump_json() if z else "{}") - for z in zone_repository.get_all_zones_by_category(session,category if category != 'all' else None,sub)] - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return json_data + use_cases = UseCases() + zone_repository = use_cases.zone_repository() + db = use_cases.db() + with db.session() as session: + json_data = [json.loads(z.model_dump_json() if z else "{}") + for z in zone_repository.get_all_zones_by_category(session,category if category != 'all' else None,sub)] + return json_data @router.get("/zones/by-category/{category}") +@cache async def get_zone_all_by_category(category:str="all",nocache:bool=False,key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) - endpoint=f"/zones/by-category/{category}" - cache= rd.get(endpoint) - start = time.time() - if cache and not nocache: - logger.debug(f"{endpoint} cached ({settings.redis_cache_expiration})s") - payload=json.loads(cache) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return payload - else: - use_cases = UseCases() - zone_repository = use_cases.zone_repository() - db = use_cases.db() - with db.session() as session: - json_data = [json.loads(z.model_dump_json() if z else "{}") - for z in zone_repository.get_all_zones_by_category(session,category if category != 'all' else None)] - rd.set(endpoint, json.dumps(json_data)) - rd.expire(endpoint,settings.redis_cache_expiration) - logger.debug(f"{endpoint} elapsed Time: {time.time()-start}") - return json_data + use_cases = UseCases() + zone_repository = use_cases.zone_repository() + db = use_cases.db() + with db.session() as session: + json_data = [json.loads(z.model_dump_json() if z else "{}") + for z in zone_repository.get_all_zones_by_category(session,category if category != 'all' else None)] + return json_data @router.get("/zones/{zones_id}") +@cache async def get_zone(zones_id:int,key: str = Depends(X_API_KEY_HEADER)): check_apikey(key) use_cases = UseCases() diff --git a/backend/bloom/services/metrics.py b/backend/bloom/services/metrics.py index ced91212..4109d81e 100644 --- a/backend/bloom/services/metrics.py +++ b/backend/bloom/services/metrics.py @@ -1,27 +1,10 @@ -from fastapi import APIRouter, Depends, Query, Body,Request -from redis import Redis -from bloom.config import settings -from bloom.logger import logger from pydantic import BaseModel, Field from contextlib import AbstractContextManager from dependency_injector.providers import Callable -from typing_extensions import Annotated, Literal, Optional -from datetime import datetime, timedelta -from sqlalchemy import select, func, and_, or_, text, literal_column, Row +from sqlalchemy import select, func, and_, or_, text, literal_column from bloom.infra.database import sql_model -from bloom.infra.repositories.repository_segment import SegmentRepository -from sqlalchemy.ext.serializer import loads,dumps -import json -import time from bloom.infra.database.database_manager import Base -from bloom.domain.metrics import (ResponseMetricsVesselInActivitySchema, - ResponseMetricsZoneVisitedSchema, - ResponseMetricsZoneVisitingTimeByVesselSchema, - ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema) -from bloom.dependencies import ( DatetimeRangeRequest, - PaginatedRequest,OrderByRequest,OrderByEnum, - paginate,PagedResponseSchema,PageParams, - X_API_KEY_HEADER, check_apikey,CachedRequest) +from bloom.routers.requests import DatetimeRangeRequest,OrderByRequest,OrderByEnum, PageParams from bloom.domain.metrics import TotalTimeActivityTypeRequest From 3616352d7a4eff240defe22b519b27d054bc4650 Mon Sep 17 00:00:00 2001 From: "herve.le-bars" Date: Fri, 15 Nov 2024 13:08:41 +0100 Subject: [PATCH 4/6] clean: warning generic & - api.py empty # Conflicts: # backend/bloom/dependencies.py --- backend/bloom/domain/api.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 backend/bloom/domain/api.py diff --git a/backend/bloom/domain/api.py b/backend/bloom/domain/api.py deleted file mode 100644 index 28881bb9..00000000 --- a/backend/bloom/domain/api.py +++ /dev/null @@ -1,13 +0,0 @@ -from pydantic import BaseModel -from typing import Generic,TypeVar, List -from typing_extensions import Annotated, Literal, Optional -from datetime import datetime, timedelta -from enum import Enum -import redis -from pydantic.generics import GenericModel -from bloom.config import settings - - -rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0) - - From b690e75fbcc02199c6bfa488893f0e0cc7d16cbf Mon Sep 17 00:00:00 2001 From: "herve.le-bars" Date: Sun, 17 Nov 2024 18:47:42 +0100 Subject: [PATCH 5/6] feat: API enable Gzip middleware to reduce reponse download size (Accept-Encoding: gzip) --- backend/bloom/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/bloom/app.py b/backend/bloom/app.py index fc02d13a..ae76fd8b 100644 --- a/backend/bloom/app.py +++ b/backend/bloom/app.py @@ -7,12 +7,14 @@ from bloom.routers.v1.ports import router as router_ports_v1 from bloom.routers.v1.zones import router as router_zones_v1 from starlette.middleware.cors import CORSMiddleware +from fastapi.middleware.gzip import GZipMiddleware from bloom.config import settings API_PREFIX_V1='/api/v1' app = FastAPI() +app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=5) @app.get("/", include_in_schema=False) From 5bfc3f59fe3e6541aaa9eee7db0f69a6a4e63466 Mon Sep 17 00:00:00 2001 From: "herve.le-bars" Date: Sun, 17 Nov 2024 18:48:35 +0100 Subject: [PATCH 6/6] fix: nocache None when missing cause exception in cache decorator --- backend/bloom/dependencies.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/bloom/dependencies.py b/backend/bloom/dependencies.py index 8341f778..5ee2b767 100644 --- a/backend/bloom/dependencies.py +++ b/backend/bloom/dependencies.py @@ -30,7 +30,9 @@ async def wrapper(*args, **kwargs): start = time.time() request=kwargs['request'] cache_service=UseCases().cache_service() - nocache=True if request.query_params.get('nocache').lower() == 'true' else False + nocache=True if request.query_params.get('nocache') \ + and request.query_params.get('nocache').lower() == 'true' \ + else False cache_key=f"{request.url.path}/{request.query_params}" incache=False #incache= cache_service.get(cache_key)