Skip to content

Commit

Permalink
Merge cf9694c into 9eba6a7
Browse files Browse the repository at this point in the history
  • Loading branch information
ibhuiyan17 committed Apr 10, 2020
2 parents 9eba6a7 + cf9694c commit 4374583
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 5 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Currently 2 different data-sources are available to retrieve the data:

* **csbs** - https://www.csbs.org/information-covid-19-coronavirus - U.S. County data that comes from the Conference of State Bank Supervisors.

* **nyt** - https://github.com/nytimes/covid-19-data - The New York Times is releasing a series of data files with cumulative counts of coronavirus cases in the United States, at the state and county level, over time.

__jhu__ data-source will be used as a default source if you don't specify a *source parameter* in your request.

## API Reference
Expand Down Expand Up @@ -71,7 +73,8 @@ __Sample response__
{
"sources": [
"jhu",
"csbs"
"csbs",
"nyt"
]
}
```
Expand All @@ -87,7 +90,7 @@ GET /v2/latest
__Query String Parameters__
| __Query string parameter__ | __Description__ | __Type__ |
| -------------------------- | -------------------------------------------------------------------------------- | -------- |
| source | The data-source where data will be retrieved from *(jhu/csbs)*. Default is *jhu* | String |
| source | The data-source where data will be retrieved from *(jhu/csbs/nyt)*. Default is *jhu* | String |

__Sample response__
```json
Expand Down Expand Up @@ -117,7 +120,7 @@ __Path Parameters__
__Query String Parameters__
| __Query string parameter__ | __Description__ | __Type__ |
| -------------------------- | -------------------------------------------------------------------------------- | -------- |
| source | The data-source where data will be retrieved from *(jhu/csbs)*. Default is *jhu* | String |
| source | The data-source where data will be retrieved from *(jhu/csbs/nyt)*. Default is *jhu* | String |

#### Example Request
```http
Expand Down Expand Up @@ -160,7 +163,7 @@ GET /v2/locations
__Query String Parameters__
| __Query string parameter__ | __Description__ | __Type__ |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
| source | The data-source where data will be retrieved from.<br>__Value__ can be: *jhu/csbs*. __Default__ is *jhu* | String |
| source | The data-source where data will be retrieved from.<br>__Value__ can be: *jhu/csbs/nyt*. __Default__ is *jhu* | String |
| country_code | The ISO ([alpha-2 country_code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)) to the Country/Province for which you're calling the Endpoint | String |
| timelines | To set the visibility of timelines (*daily tracking*).<br>__Value__ can be: *0/1*. __Default__ is *0* (timelines are not visible) | Integer |

Expand Down
4 changes: 3 additions & 1 deletion app/data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""app.data"""
from ..services.location.csbs import CSBSLocationService
from ..services.location.jhu import JhuLocationService
from ..services.location.nyt import NYTLocationService

# Mapping of services to data-sources.
DATA_SOURCES = {"jhu": JhuLocationService(), "csbs": CSBSLocationService()}
DATA_SOURCES = {"jhu": JhuLocationService(), "csbs": CSBSLocationService(), "nyt": NYTLocationService()}
# DATA_SOURCES = {"jhu": JhuLocationService(), "csbs": CSBSLocationService()}


def data_source(source):
Expand Down
1 change: 1 addition & 0 deletions app/enums/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ class Sources(str, Enum):

jhu = "jhu"
csbs = "csbs"
nyt = "nyt"
32 changes: 32 additions & 0 deletions app/location/nyt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""app.locations.nyt.py"""
from . import TimelinedLocation


class NYTLocation(TimelinedLocation):
"""
A NYT (county) Timelinedlocation.
"""

# pylint: disable=too-many-arguments,redefined-builtin
def __init__(self, id, state, county, coordinates, last_updated, timelines):
super().__init__(id, "US", state, coordinates, last_updated, timelines)

self.state = state
self.county = county

def serialize(self, timelines=False): # pylint: disable=arguments-differ,unused-argument
"""
Serializes the location into a dict.
:returns: The serialized location.
:rtype: dict
"""
serialized = super().serialize(timelines)

# Update with new fields.
serialized.update(
{"state": self.state, "county": self.county,}
)

# Return the serialized location.
return serialized
126 changes: 126 additions & 0 deletions app/services/location/nyt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""app.services.location.nyt.py"""
import csv
from datetime import datetime

from asyncache import cached
from cachetools import TTLCache

from ...coordinates import Coordinates
from ...location.nyt import NYTLocation
from ...timeline import Timeline
from ...utils import httputils
from . import LocationService


class NYTLocationService(LocationService):
"""
Service for retrieving locations from New York Times (https://github.com/nytimes/covid-19-data).
"""

async def get_all(self):
# Get the locations.
locations = await get_locations()
return locations

async def get(self, loc_id): # pylint: disable=arguments-differ
# Get location at the index equal to provided id.
locations = await self.get_all()
return locations[loc_id]


# ---------------------------------------------------------------


# Base URL for fetching category.
BASE_URL = "https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv"


def get_grouped_locations_dict(data):
"""
Helper function to group history for locations into one dict.
:returns: The complete data for each unique US county
:rdata: dict
"""
grouped_locations = {}

# in increasing order of dates
for row in data:
county_state = (row["county"], row["state"])
date = row["date"]
confirmed = row["cases"]
deaths = row["deaths"]

# initialize if not existing
if county_state not in grouped_locations:
grouped_locations[county_state] = {"confirmed": [], "deaths": []}

# append confirmed tuple to county_state (date, # confirmed)
grouped_locations[county_state]["confirmed"].append((date, confirmed))
# append deaths tuple to county_state (date, # deaths)
grouped_locations[county_state]["deaths"].append((date, deaths))

return grouped_locations


@cached(cache=TTLCache(maxsize=1024, ttl=3600))
async def get_locations():
"""
Returns a list containing parsed NYT data by US county. The data is cached for 1 hour.
:returns: The complete data for US Counties.
:rtype: dict
"""

# Request the data.
async with httputils.CLIENT_SESSION.get(BASE_URL) as response:
text = await response.text()

# Parse the CSV.
data = list(csv.DictReader(text.splitlines()))

# Group together locations (NYT data ordered by dates not location).
grouped_locations = get_grouped_locations_dict(data)

# The normalized locations.
locations = []

idx = 0
for county_state, histories in grouped_locations.items():
# Make location history for confirmed and deaths from dates.
# List is tuples of (date, amount) in order of increasing dates.
confirmed_list = histories["confirmed"]
confirmed_history = {date: int(amount or 0) for date, amount in confirmed_list}

deaths_list = histories["deaths"]
deaths_history = {date: int(amount or 0) for date, amount in deaths_list}

# Normalize the item and append to locations.
locations.append(
NYTLocation(
id=idx,
state=county_state[1],
county=county_state[0],
coordinates=Coordinates(None, None), # NYT does not provide coordinates
last_updated=datetime.utcnow().isoformat() + "Z", # since last request
timelines={
"confirmed": Timeline(
{
datetime.strptime(date, "%Y-%m-%d").isoformat() + "Z": amount
for date, amount in confirmed_history.items()
}
),
"deaths": Timeline(
{
datetime.strptime(date, "%Y-%m-%d").isoformat() + "Z": amount
for date, amount in deaths_history.items()
}
),
"recovered": Timeline({}),
},
)
)

idx += 1

return locations

0 comments on commit 4374583

Please sign in to comment.