diff --git a/app/main.py b/app/main.py index b9aff949..6f749bfc 100644 --- a/app/main.py +++ b/app/main.py @@ -15,7 +15,7 @@ from .config import get_settings from .data import data_source -from .routers import V1, V2 +from .routers import V1, V2, V3 from .utils.httputils import setup_client_session, teardown_client_session # ############ @@ -34,7 +34,7 @@ "API for tracking the global coronavirus (COVID-19, SARS-CoV-2) outbreak." " Project page: https://github.com/ExpDev07/coronavirus-tracker-api." ), - version="2.0.4", + version="3.0.0", docs_url="/", redoc_url="/docs", on_startup=[setup_client_session], @@ -112,6 +112,7 @@ async def handle_validation_error( # Include routers. APP.include_router(V1, prefix="", tags=["v1"]) APP.include_router(V2, prefix="/v2", tags=["v2"]) +APP.include_router(V3, prefix="/v3", tags=["v3"]) # Running of app. diff --git a/app/routers/__init__.py b/app/routers/__init__.py index 2bdd5ee3..de59cc3c 100644 --- a/app/routers/__init__.py +++ b/app/routers/__init__.py @@ -1,3 +1,4 @@ """app.routers""" from .v1 import V1 from .v2 import V2 +from .v3 import V3 diff --git a/app/routers/v3.py b/app/routers/v3.py new file mode 100644 index 00000000..a0a21e18 --- /dev/null +++ b/app/routers/v3.py @@ -0,0 +1,105 @@ +"""app.routers.v3""" +import enum + +from fastapi import APIRouter, HTTPException, Request + +from ..data import DATA_SOURCES +from ..models import LatestResponse, LocationResponse, LocationsResponse +from ..location import Location + +V3 = APIRouter() + + +class Sources(str, enum.Enum): + """ + A source available for retrieving data. + """ + + JHU = "jhu" + CSBS = "csbs" + NYT = "nyt" + + +class Source(): + """ + A datasource and its location data. + """ + + def __init__(self, name: str, locations: list[Location]): + self.name = name + self.locations = locations + + +@V3.get("/sources") +async def sources(): + """ + Retrieves a list of data-sources that are availble to use. + """ + return {"sources": list(DATA_SOURCES.keys())} + + +@V3.get("/sources/{name}") +async def data_source( + request: Request, + source: Sources, + country_code: str = None, + province: str = None, + county: str = None, + timelines: bool = False, +): + # All query paramameters. + params = dict(request.query_params) + + # Remove reserved params. + params.pop("source", None) + params.pop("timelines", None) + + source = Sources(source) + + locations = await source.get_all() + + for key, value in params.items(): + # Clean keys for security purposes. + key = key.lower() + value = value.lower().strip("__") + + # Do filtering. + try: + locations = [ + location + for location in locations + if str(getattr(location, key)).lower() == str(value) + ] + except AttributeError: + pass + if not locations: + raise HTTPException( + 404, detail=f"Source `{source}` does not have the desired location data.", + ) + + # Attempt to filter out locations with properties matching the provided query params. + for key, value in params.items(): + # Clean keys for security purposes. + key = key.lower() + value = value.lower().strip("__") + + # Do filtering. + try: + locations = [ + location + for location in locations + if str(getattr(location, key)).lower() == str(value) + ] + except AttributeError: + pass + if not locations: + raise HTTPException( + 404, detail=f"Source `{source}` does not have the desired location data.", + ) + + data_source = Source(source, locations) + + return { + "name": data_source.name, + "locations": [location.serialize(timelines) for location in data_source.locations], + }