Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

# ############
Expand All @@ -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],
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions app/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""app.routers"""
from .v1 import V1
from .v2 import V2
from .v3 import V3
105 changes: 105 additions & 0 deletions app/routers/v3.py
Original file line number Diff line number Diff line change
@@ -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],
}