Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
54 changes: 49 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ We provide multiple data-sources you can pick from, simply add the query paramat

* **jhu** - https://github.com/CSSEGISandData/COVID-19 - Data repository operated by the Johns Hopkins University Center for Systems Science and Engineering (JHU CSSE).

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

* **... more to come later**.

### Getting latest amount of total confirmed cases, deaths, and recoveries.
Expand Down Expand Up @@ -126,11 +128,53 @@ Exclude timelines.
GET /v2/locations?timelines=0
```

## Data

The data comes from the [2019 Novel Coronavirus (nCoV) Data Repository, provided
by JHU CCSE](https://github.com/CSSEGISandData/2019-nCoV). It is
programmatically retrieved, re-formatted and stored in the cache for one hour.
### Getting US per county information.
```http
GET /v2/locations?source=csbs
```
```json
{
"locations": [
{
"coordinates": {
"latitude": 40.71455,
"longitude": -74.00714
},
"country": "US",
"country_code": "US",
"county": "New York",
"id": 0,
"last_updated": "2020-03-21 14:00 EDT",
"latest": {
"confirmed": 6211,
"deaths": 43,
"recovered": 0
},
"province": "New York",
"state": "New York"
},
{
"coordinates": {
"latitude": 41.16319759,
"longitude": -73.7560629
},
"country": "US",
"country_code": "US",
"county": "Westchester",
"id": 1,
"last_updated": "2020-03-21 14:00 EDT",
"latest": {
"confirmed": 1385,
"deaths": 0,
"recovered": 0
},
"province": "Westchester",
"state": "New York"
},
...
]
}
```

## Wrappers

Expand Down
2 changes: 2 additions & 0 deletions app/data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from ..services.location.jhu import JhuLocationService
from ..services.location.csbs import CSBSLocationService

# Mapping of services to data-sources.
data_sources = {
'jhu': JhuLocationService(),
'csbs': CSBSLocationService()
}

def data_source(source):
Expand Down
30 changes: 30 additions & 0 deletions app/location/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,35 @@ def serialize(self, timelines = False):
key: value.serialize() for (key, value) in self.timelines.items()
}})

# Return the serialized location.
return serialized

class CSBSLocation(Location):
"""
A CSBS (county) location.
"""
def __init__(self, id, state, county, coordinates, last_updated, confirmed, deaths):
super().__init__(
id, 'US', county, coordinates, last_updated, confirmed, deaths, recovered=0
)

self.state = state
self.county = county

def serialize(self, timelines=False):
"""
Serializes the location into a dict.
:returns: The serialized location.
:rtype: dict
"""
serialized = super().serialize()

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

# Return the serialized location.
return serialized
4 changes: 3 additions & 1 deletion app/services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from .location.jhu import JhuLocationService
from .location.csbs import CSBSLocationService

# Instances of the services.
jhu = JhuLocationService()
jhu = JhuLocationService()
csbs = CSBSLocationService()
52 changes: 52 additions & 0 deletions app/services/location/csbs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from . import LocationService
from ...location import CSBSLocation
from ...coordinates import Coordinates

class CSBSLocationService(LocationService):
"""
Servive for retrieving locations from csbs
"""

def get_all(self):
# Get the locations
return get_locations()

def get(self, id):
return self.get_all()[id]

import requests
import csv
from datetime import datetime
from cachetools import cached, TTLCache

# Base URL for fetching data
base_url = 'https://facts.csbs.org/covid-19/covid19_county.csv'

@cached(cache=TTLCache(maxsize=1, ttl=3600))
def get_locations():
"""
Retrieves county locations; locations are cached for 1 hour

:returns: The locations.
:rtype: dict
"""
request = requests.get(base_url)
text = request.text

data = list(csv.DictReader(text.splitlines()))

locations = []

for i, item in enumerate(data):
state = item['State Name']
county = item['County Name']
if county == "Unassigned" or county == "Unknown":
continue

confirmed = int(item['Confirmed'] or 0)
death = int(item['Death'] or 0)
coordinates = Coordinates(float(item['Latitude']), float(item['Longitude']))
last_update = item['Last Update']
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be parsed as ISO with an added 'Z' at the end. Use import datetime from datetime to achieve this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed!

locations.append(CSBSLocation(i, state, county, coordinates, last_update, confirmed, death))

return locations
33 changes: 33 additions & 0 deletions tests/example_data/sample_covid19_county.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
County Name,State Name,Confirmed,New,Death,Fatality Rate,Latitude,Longitude,Last Update
New York,New York,4408,454,26,0.6%,40.71455,-74.00714,2020-03-20 13:58 EDT
Westchester,New York,1091,293,0,0%,41.16319759,-73.7560629,2020-03-20 13:58 EDT
Nassau,New York,754,382,4,0.5%,40.74165225,-73.58899619,2020-03-20 13:58 EDT
Yakima,Washington,7,0,0,0%,46.60448,-120.50721,2020-03-20 13:58 EDT
Thurston,Washington,6,0,0,0%,46.91980578,-122.8298691,2020-03-20 13:58 EDT
Jefferson,Washington,4,0,0,0%,47.74810608,-123.6000095,2020-03-20 13:58 EDT
Douglas,Kansas,1,0,0,0%,38.88462907,-95.29255463,2020-03-20 13:58 EDT
Cherokee,Kansas,1,0,0,0%,37.16926692,-94.8462675759999,2020-03-20 13:58 EDT
Jackson,Kansas,1,0,0,0%,39.4168027220001,-95.793674403,2020-03-20 13:58 EDT
Twin Falls,Idaho,1,0,0,0%,42.55619,-114.4696,2020-03-20 13:58 EDT
Kootenai,Idaho,1,0,0,0%,47.6775872760001,-116.697131928,2020-03-20 13:58 EDT
Chittenden,Vermont,4,0,1,25%,44.45799511,-73.05404973,2020-03-20 13:58 EDT
Bennington,Vermont,3,0,0,0%,42.87672,-73.19818,2020-03-20 13:58 EDT
Windsor,Vermont,3,0,1,33.3%,43.48115,-72.38581,2020-03-20 13:58 EDT
Washington,Vermont,1,0,0,0%,44.27344561,-72.61485925,2020-03-20 13:58 EDT
Orange,Vermont,1,0,0,0%,44.14854,-72.40233,2020-03-20 13:58 EDT
Addison,Vermont,1,0,0,0%,44.0280736,-73.13152876,2020-03-20 13:58 EDT
Burleigh,North Dakota,11,0,0,0%,46.97801044,-100.4669442,2020-03-20 13:58 EDT
Tucker,West Virginia,2,0,0,0%,39.1135508250001,-79.56492129,2020-03-20 13:58 EDT
Mercer,West Virginia,1,0,0,0%,37.40556515,-81.11143231,2020-03-20 13:58 EDT
Monongalia,West Virginia,1,0,0,0%,39.630233859,-80.0465546289999,2020-03-20 13:58 EDT
Unassigned,New York,166,149,4,2.4%,42.165726,-74.948051,2020-03-20 13:58 EDT
Unassigned,Washington,151,0,0,0%,47.400902,-121.490494,2020-03-20 13:58 EDT
Unassigned,Colorado,57,0,0,0%,39.059811,-105.311104,2020-03-20 13:58 EDT
Unknown,Pennsylvania,55,55,0,0%,40.590752,-77.209755,2020-03-20 13:58 EDT
Unassigned,Pennsylvania,0,0,0,NaN%,40.590752,-77.209755,2020-03-20 13:58 EDT
Franklin,Pennsylvania,1,1,0,0%,39.927495836,-77.721161869,2020-03-20 13:58 EDT
Franklin,North Carolina,4,4,0,0%,36.0827448150001,-78.285600305,2020-03-20 13:58 EDT
Lee,North Carolina,1,1,0,0%,35.475059921,-79.17154054,2020-03-20 13:58 EDT
Clay,Minnesota,1,1,0,0%,46.892347886,-96.490737839,2020-03-20 13:58 EDT
Yuma,Arizona,1,1,0,0%,32.768956524,-113.905830295,2020-03-20 13:58 EDT
Dunklin,Missouri,1,1,0,0%,36.105848973,-90.16563,2020-03-20 13:58 EDT
35 changes: 35 additions & 0 deletions tests/test_csbs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import app
import datetime
import pytest
from unittest import mock
from app.services.location import csbs

def mocked_csbs_requests_get(*args, **kwargs):
class FakeRequestsGetResponse:
"""
Returns instance of `FakeRequestsGetResponse`
when calling `app.services.location.csbs.requests.get()
"""
def __init__(self):
self.text = self.read_file()

def read_file(self):
"""
Mock HTTP GET-method and return text from file
"""
filepath = "tests/example_data/sample_covid19_county.csv"
print("Try to read {}".format(filepath))
with open(filepath, "r") as file:
return file.read()

return FakeRequestsGetResponse()

@mock.patch('app.services.location.csbs.requests.get', side_effect=mocked_csbs_requests_get)
def test_get_locations(mock_request_get):
data = csbs.get_locations()
assert isinstance(data, list)

# check to see that Unknown/Unassigned has been filtered
for d in data:
assert d.county != "Unknown"
assert d.county != "Unassigned"