diff --git a/backend/bloom/dependencies.py b/backend/bloom/dependencies.py new file mode 100644 index 00000000..deac07eb --- /dev/null +++ b/backend/bloom/dependencies.py @@ -0,0 +1,84 @@ +from fastapi import Request, HTTPException +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 enum import Enum + + +## 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 + +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_costal_waters: str = "Total Time in Costal Waters" + 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_costal_waters: str = "Total Time Fishing in Costal Waters" + total_time_fishing_in_extincting_amp: str = "Total Time in Extincting AMP" \ No newline at end of file diff --git a/backend/bloom/domain/api.py b/backend/bloom/domain/api.py index baff6beb..28881bb9 100644 --- a/backend/bloom/domain/api.py +++ b/backend/bloom/domain/api.py @@ -1,89 +1,13 @@ -from fastapi import Request, HTTPException -from pydantic import BaseModel, ConfigDict, Field,conint +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 fastapi.security import APIKeyHeader from bloom.config import settings -## Reference for pagination design -## https://jayhawk24.hashnode.dev/how-to-implement-pagination-in-fastapi-feat-sqlalchemy -X_API_KEY_HEADER=APIKeyHeader(name="x-key") rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0) -class CachedRequest(BaseModel): - nocache:bool=False -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 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_costal_waters: str = "Total Time in Costal Waters" - 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_costal_waters: str = "Total Time Fishing in Costal Waters" - total_time_fishing_in_extincting_amp: str = "Total Time in Extincting AMP" - -class TotalTimeActivityTypeRequest(BaseModel): - type: TotalTimeActivityTypeEnum - -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], - ) \ No newline at end of file diff --git a/backend/bloom/domain/metrics.py b/backend/bloom/domain/metrics.py index 54be1c94..fa9501f2 100644 --- a/backend/bloom/domain/metrics.py +++ b/backend/bloom/domain/metrics.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta from enum import Enum from bloom.domain.vessel import Vessel +from bloom.dependencies import TotalTimeActivityTypeEnum class ResponseMetricsVesselInActivitySchema(BaseModel): model_config = ConfigDict(from_attributes=True) @@ -46,4 +47,9 @@ class ResponseMetricsZoneVisitingTimeByVesselSchema(BaseModel): class ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema(BaseModel): vessel_id : int - total_activity_time: timedelta \ No newline at end of file + total_activity_time: timedelta + + + +class TotalTimeActivityTypeRequest(BaseModel): + type: TotalTimeActivityTypeEnum \ No newline at end of file diff --git a/backend/bloom/services/api.py b/backend/bloom/main.py similarity index 96% rename from backend/bloom/services/api.py rename to backend/bloom/main.py index 099a2a05..49c096c3 100644 --- a/backend/bloom/services/api.py +++ b/backend/bloom/main.py @@ -6,7 +6,7 @@ from bloom.routers.vessels import router as router_vessels from bloom.routers.ports import router as router_ports from bloom.routers.zones import router as router_zones -from bloom.domain.api import ( DatetimeRangeRequest, +from bloom.dependencies import ( DatetimeRangeRequest, PaginatedRequest,OrderByRequest, paginate,PagedResponseSchema,PageParams, X_API_KEY_HEADER,check_apikey) diff --git a/backend/bloom/routers/metrics.py b/backend/bloom/routers/metrics.py index d599c36e..eb585011 100644 --- a/backend/bloom/routers/metrics.py +++ b/backend/bloom/routers/metrics.py @@ -17,11 +17,12 @@ ResponseMetricsZoneVisitedSchema, ResponseMetricsZoneVisitingTimeByVesselSchema, ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema) -from bloom.domain.api import ( DatetimeRangeRequest, +from bloom.dependencies import ( DatetimeRangeRequest, PaginatedRequest,OrderByRequest,OrderByEnum, paginate,PagedResponseSchema,PageParams, - X_API_KEY_HEADER, check_apikey,CachedRequest, - TotalTimeActivityTypeRequest) + X_API_KEY_HEADER, check_apikey,CachedRequest) + +from bloom.domain.metrics import TotalTimeActivityTypeRequest router = APIRouter() rd = Redis(host=settings.redis_host, port=settings.redis_port, db=0) diff --git a/backend/bloom/routers/ports.py b/backend/bloom/routers/ports.py index 4397f2f5..144ddde5 100644 --- a/backend/bloom/routers/ports.py +++ b/backend/bloom/routers/ports.py @@ -15,7 +15,7 @@ from bloom.container import UseCases from bloom.domain.vessel import Vessel from bloom.logger import logger -from bloom.domain.api import ( DatetimeRangeRequest, +from bloom.dependencies import ( DatetimeRangeRequest, PaginatedRequest,OrderByRequest,OrderByEnum, paginate,PagedResponseSchema,PageParams, X_API_KEY_HEADER,check_apikey,CachedRequest) diff --git a/backend/bloom/routers/vessels.py b/backend/bloom/routers/vessels.py index e052119d..f67ae7ab 100644 --- a/backend/bloom/routers/vessels.py +++ b/backend/bloom/routers/vessels.py @@ -15,7 +15,7 @@ from bloom.container import UseCases from bloom.domain.vessel import Vessel from bloom.logger import logger -from bloom.domain.api import ( DatetimeRangeRequest, +from bloom.dependencies import ( DatetimeRangeRequest, PaginatedRequest,OrderByRequest,OrderByEnum, paginate,PagedResponseSchema,PageParams, X_API_KEY_HEADER,check_apikey) diff --git a/backend/bloom/routers/zones.py b/backend/bloom/routers/zones.py index 4f6bce34..b4820bda 100644 --- a/backend/bloom/routers/zones.py +++ b/backend/bloom/routers/zones.py @@ -15,7 +15,7 @@ from bloom.container import UseCases from bloom.domain.vessel import Vessel from bloom.logger import logger -from bloom.domain.api import ( DatetimeRangeRequest, +from bloom.dependencies import ( DatetimeRangeRequest, PaginatedRequest,OrderByRequest,OrderByEnum, paginate,PagedResponseSchema,PageParams, X_API_KEY_HEADER,check_apikey) diff --git a/docker-compose.yaml b/docker-compose.yaml index e0db921c..57ca43ee 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -18,7 +18,7 @@ services: - -c - | echo "Starting Scheduler" && - service cron start && uvicorn bloom.services.api:app --host 0.0.0.0 --reload + service cron start && uvicorn bloom.main:app --host 0.0.0.0 --reload volumes: - ./:/project/ - ./data:/project/data