Skip to content

Commit

Permalink
Merge 311a05a into 56f51c5
Browse files Browse the repository at this point in the history
  • Loading branch information
Kilo59 committed Apr 18, 2020
2 parents 56f51c5 + 311a05a commit 5e09dbe
Show file tree
Hide file tree
Showing 23 changed files with 157 additions and 185 deletions.
4 changes: 4 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
~~~~~~~~~~~~~~~~~~~~~~~~
API for tracking the global coronavirus (COVID-19, SARS-CoV-2) outbreak.
"""
import logging

# See PEP396.
__version__ = "2.0.3"

logging.basicConfig(level=logging.INFO)
11 changes: 0 additions & 11 deletions app/enums/sources.py

This file was deleted.

5 changes: 2 additions & 3 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
from fastapi.responses import JSONResponse

from .data import data_source
from .router.v1 import V1
from .router.v2 import V2
from .routers import V1, V2
from .utils.httputils import setup_client_session, teardown_client_session

# ############
Expand Down Expand Up @@ -61,7 +60,7 @@ async def add_datasource(request: Request, call_next):
request.state.source = source

# Move on...
LOGGER.info(f"source provided: {source.__class__.__name__}")
LOGGER.debug(f"source provided: {source.__class__.__name__}")
response = await call_next(request)
return response

Expand Down
39 changes: 37 additions & 2 deletions app/models/location.py → app/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,44 @@
"""app.models.py"""
from typing import Dict, List

from pydantic import BaseModel

from .latest import Latest
from .timeline import Timelines

class Latest(BaseModel):
"""
Latest model.
"""

confirmed: int
deaths: int
recovered: int


class LatestResponse(BaseModel):
"""
Response for latest.
"""

latest: Latest


class Timeline(BaseModel):
"""
Timeline model.
"""

latest: int
timeline: Dict[str, int] = {}


class Timelines(BaseModel):
"""
Timelines model.
"""

confirmed: Timeline
deaths: Timeline
recovered: Timeline


class Location(BaseModel):
Expand Down
19 changes: 0 additions & 19 deletions app/models/latest.py

This file was deleted.

22 changes: 0 additions & 22 deletions app/models/timeline.py

This file was deleted.

8 changes: 0 additions & 8 deletions app/router/__init__.py

This file was deleted.

4 changes: 0 additions & 4 deletions app/router/v1/__init__.py

This file was deleted.

20 changes: 0 additions & 20 deletions app/router/v1/all.py

This file was deleted.

11 changes: 0 additions & 11 deletions app/router/v1/confirmed.py

This file was deleted.

11 changes: 0 additions & 11 deletions app/router/v1/deaths.py

This file was deleted.

11 changes: 0 additions & 11 deletions app/router/v1/recovered.py

This file was deleted.

4 changes: 0 additions & 4 deletions app/router/v2/__init__.py

This file was deleted.

21 changes: 0 additions & 21 deletions app/router/v2/latest.py

This file was deleted.

11 changes: 0 additions & 11 deletions app/router/v2/sources.py

This file was deleted.

3 changes: 3 additions & 0 deletions app/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""app.routers"""
from .v1 import V1
from .v2 import V2
47 changes: 47 additions & 0 deletions app/routers/v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""app.routers.v1.py"""
from fastapi import APIRouter

from ..services.location.jhu import get_category

V1 = APIRouter()


@V1.get("/all")
async def all_categories():
"""Get all the categories."""
confirmed = await get_category("confirmed")
deaths = await get_category("deaths")
recovered = await get_category("recovered")

return {
# Data.
"confirmed": confirmed,
"deaths": deaths,
"recovered": recovered,
# Latest.
"latest": {"confirmed": confirmed["latest"], "deaths": deaths["latest"], "recovered": recovered["latest"],},
}


@V1.get("/confirmed")
async def get_confirmed():
"""Confirmed cases."""
confirmed_data = await get_category("confirmed")

return confirmed_data


@V1.get("/deaths")
async def get_deaths():
"""Total deaths."""
deaths_data = await get_category("deaths")

return deaths_data


@V1.get("/recovered")
async def get_recovered():
"""Recovered cases."""
recovered_data = await get_category("recovered")

return recovered_data
51 changes: 43 additions & 8 deletions app/router/v2/locations.py → app/routers/v2.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,41 @@
"""app.router.v2.locations.py"""
from fastapi import HTTPException, Request
"""app.routers.v2"""
import enum

from ...enums.sources import Sources
from ...models.location import LocationResponse as Location
from ...models.location import LocationsResponse as Locations
from . import V2
from fastapi import APIRouter, HTTPException, Request

from ..data import DATA_SOURCES
from ..models import LatestResponse, LocationResponse, LocationsResponse

V2 = APIRouter()


class Sources(str, enum.Enum):
"""
A source available for retrieving data.
"""

jhu = "jhu"
csbs = "csbs"
nyt = "nyt"


@V2.get("/latest", response_model=LatestResponse)
async def get_latest(request: Request, source: Sources = "jhu"): # pylint: disable=unused-argument
"""
Getting latest amount of total confirmed cases, deaths, and recoveries.
"""
locations = await request.state.source.get_all()
return {
"latest": {
"confirmed": sum(map(lambda location: location.confirmed, locations)),
"deaths": sum(map(lambda location: location.deaths, locations)),
"recovered": sum(map(lambda location: location.recovered, locations)),
}
}


# pylint: disable=unused-argument,too-many-arguments,redefined-builtin
@V2.get("/locations", response_model=Locations, response_model_exclude_unset=True)
@V2.get("/locations", response_model=LocationsResponse, response_model_exclude_unset=True)
async def get_locations(
request: Request,
source: Sources = "jhu",
Expand Down Expand Up @@ -56,10 +83,18 @@ async def get_locations(


# pylint: disable=invalid-name
@V2.get("/locations/{id}", response_model=Location)
@V2.get("/locations/{id}", response_model=LocationResponse)
async def get_location_by_id(request: Request, id: int, source: Sources = "jhu", timelines: bool = True):
"""
Getting specific location by id.
"""
location = await request.state.source.get(id)
return {"location": location.serialize(timelines)}


@V2.get("/sources")
async def sources():
"""
Retrieves a list of data-sources that are availble to use.
"""
return {"sources": list(DATA_SOURCES.keys())}
11 changes: 6 additions & 5 deletions app/services/location/csbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from ...utils import httputils
from . import LocationService

LOGGER = logging.getLogger("services.location.csbs")


class CSBSLocationService(LocationService):
"""
Expand Down Expand Up @@ -40,15 +42,14 @@ async def get_locations():
:returns: The locations.
:rtype: dict
"""
logger = logging.getLogger("services.location.csbs")
logger.info("Requesting data...")
LOGGER.info("csbs Requesting data...")
async with httputils.CLIENT_SESSION.get(BASE_URL) as response:
text = await response.text()

logger.info("Data received")
LOGGER.info("csbs Data received")

data = list(csv.DictReader(text.splitlines()))
logger.info("CSV parsed")
LOGGER.info("csbs CSV parsed")

locations = []

Expand Down Expand Up @@ -83,7 +84,7 @@ async def get_locations():
int(item["Death"] or 0),
)
)
logger.info("Data normalized")
LOGGER.info("csbs Data normalized")

# Return the locations.
return locations

0 comments on commit 5e09dbe

Please sign in to comment.