Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
5f1a422
add FastAPI uvicorn
Kilo59 Mar 21, 2020
8166a4d
define app
Kilo59 Mar 21, 2020
d79965e
create provisional models
Kilo59 Mar 22, 2020
cd64e47
define latest, location endpoints
Kilo59 Mar 22, 2020
21f4472
update models
Kilo59 Mar 22, 2020
b362750
change model name to match pre-existing model
Kilo59 Mar 22, 2020
033d924
defaults
Kilo59 Mar 22, 2020
b1a4742
update versions and response models
Kilo59 Mar 22, 2020
bdae7f0
divide sections
Kilo59 Mar 22, 2020
825de31
create middleware to attach the "data_source"
Kilo59 Mar 22, 2020
e31aad6
validation exception handler
Kilo59 Mar 22, 2020
20c49c2
get location by id
Kilo59 Mar 22, 2020
1cfdc2c
WIP get_all_locations
Kilo59 Mar 22, 2020
e8e50e8
add Totals
Kilo59 Mar 22, 2020
94a2f4e
Mount v2 of the application
Kilo59 Mar 22, 2020
091b461
add FIXME for timelines
Kilo59 Mar 22, 2020
ce9374c
end of file newlines
Kilo59 Mar 22, 2020
0d2f5f3
move models to models.py
Kilo59 Mar 22, 2020
04a35d8
exclude unset
Kilo59 Mar 22, 2020
6ecbc26
gunicorn with FastAPI
Kilo59 Mar 23, 2020
43bdeb0
change version name
Kilo59 Mar 23, 2020
86617e6
create Sources enum
Kilo59 Mar 23, 2020
21e952c
expose v1 and v2 apis via mounted WSGI app
Kilo59 Mar 23, 2020
0a2a262
lock dependencies for linux machines
Kilo59 Mar 23, 2020
8a19959
specify python 3.8 runtime for Heroku
Kilo59 Mar 23, 2020
85e493f
Merge pull request #3 from Kilo59/master
ExpDev07 Mar 23, 2020
49800c3
Merge branch 'master' into FastAPI-conversion
Kilo59 Mar 23, 2020
ab817bd
add sources
Kilo59 Mar 24, 2020
ece30f3
update prefix
Kilo59 Mar 24, 2020
aec80ac
use separate v2 router
Kilo59 Mar 24, 2020
1949bea
add sources and make Timelines a boolean
Kilo59 Mar 23, 2020
f7b7354
fixed wrong date format and timelines appearing null
Mar 24, 2020
1faac27
fix csbs changing date format (for the second time)
Mar 24, 2020
acae882
Merge pull request #153 from ExpDev07/fix-csbs
ExpDev07 Mar 24, 2020
0ba6749
Merge pull request #6 from ExpDev07/master
ExpDev07 Mar 24, 2020
d3f0ab8
updated
Mar 24, 2020
0a82678
still support old recoveries
Mar 24, 2020
a28828e
fix tests
Mar 24, 2020
4c8da61
fix tests (2)
Mar 24, 2020
d6eea46
oops
Mar 24, 2020
17eee96
now
Mar 24, 2020
2c38d2b
now?
Mar 24, 2020
52fab64
tests fixed
Mar 24, 2020
73790f7
finally fixed tests
Mar 24, 2020
c1a5c52
works
Mar 24, 2020
b8eb7f1
Merge pull request #156 from ExpDev07/update-jhu-urls
ExpDev07 Mar 24, 2020
116d603
add funding option :)
ExpDev07 Mar 24, 2020
fe60577
Merge pull request #7 from ExpDev07/master
ExpDev07 Mar 24, 2020
63804be
hotfix
Mar 24, 2020
87a54cc
fix test
Mar 24, 2020
be2aee3
Merge pull request #159 from ExpDev07/mismatch-hotfix
ExpDev07 Mar 24, 2020
2138c57
Update README.md
ExpDev07 Mar 24, 2020
f9cbcd5
Update README.md
ExpDev07 Mar 24, 2020
7a0d38c
Merge pull request #8 from ExpDev07/master
ExpDev07 Mar 24, 2020
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
12 changes: 12 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# These are supported funding model platforms

#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#patreon: # Replace with a single Patreon username
#open_collective: # Replace with a single Open Collective username
ko_fi: ExpDev
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
## Coronavirus Tracker API
<h1 align="center">
Coronavirus Tracker API
</h1>

Provides up-to-date data about Coronavirus outbreak. Includes numbers about confirmed cases, deaths and recovered.
Support multiple data-sources.

Expand All @@ -18,6 +21,10 @@ Support multiple data-sources.
![Covid-19 Recovered](https://covid19-badges.herokuapp.com/recovered/latest)
![Covid-19 Deaths](https://covid19-badges.herokuapp.com/deaths/latest)

## Recovered cases showing 0

**JHU (our main data provider) [no longer provides data for amount of recoveries](https://github.com/ExpDev07/coronavirus-tracker-api/issues/155), and as a result, the API will be showing 0 for this statistic. Apolegies for any inconvenience. Hopefully we'll be able to find an alternative data-source that offers this.**

## Available data-sources:

Currently 2 different data-sources are available to retrieve the data:
Expand All @@ -28,7 +35,6 @@ Currently 2 different data-sources are available to retrieve the data:

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


## API Reference

All endpoints are located at ``coronavirus-tracker-api.herokuapp.com/v2/`` and are accessible via https. For instance: you can get data per location by using this URL:
Expand All @@ -40,7 +46,6 @@ You can open the URL in your browser to further inspect the response. Or you can
curl https://coronavirus-tracker-api.herokuapp.com/v2/locations | json_pp
```


## API Endpoints

### Sources Endpoint
Expand Down
5 changes: 3 additions & 2 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def get_latest(request: fastapi.Request, source: Sources = "jhu"):


@V2.get(
"/locations", response_model=models.AllLocations, response_model_exclude_unset=True
"/locations", response_model=models.Locations, response_model_exclude_unset=True
)
def get_all_locations(
request: fastapi.Request,
Expand Down Expand Up @@ -131,8 +131,9 @@ async def sources():
"""
return {"sources": list(data_sources.keys())}


# Include routers.
APP.include_router(V2, prefix="/v2-beta", tags=["v2"])

# mount the existing Flask app
# v1 @ /
# v2 @ /v2
Expand Down
33 changes: 15 additions & 18 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,45 @@
~~~~~~~~~~~~~
Reponse data models.
"""
import datetime as dt
from pydantic import BaseModel
from typing import Dict, List

import pydantic


class Totals(pydantic.BaseModel):
class Totals(BaseModel):
confirmed: int
deaths: int
recovered: int


class Latest(pydantic.BaseModel):
class Latest(BaseModel):
latest: Totals


class TimelineStats(pydantic.BaseModel):
class TimelineStats(BaseModel):
latest: int
timeline: Dict[str, int]
timeline: Dict[str, int] = {}


class TimelinedLocation(pydantic.BaseModel):
class TimelinedLocation(BaseModel):
confirmed: TimelineStats
deaths: TimelineStats
recovered: TimelineStats


class Country(pydantic.BaseModel):
coordinates: Dict
class Country(BaseModel):
id: int
country: str
country_code: str
id: int
last_updated: dt.datetime
latest: Totals
province: str = ""
timelines: TimelinedLocation = None # FIXME
last_updated: str # TODO use datetime.datetime type.
coordinates: Dict
latest: Totals
timelines: TimelinedLocation = {}


class AllLocations(pydantic.BaseModel):
class Locations(BaseModel):
latest: Totals
locations: List[Country]
locations: List[Country] = []


class Location(pydantic.BaseModel):
class Location(BaseModel):
location: Country
6 changes: 0 additions & 6 deletions app/services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@
from .location.jhu import JhuLocationService
from .location.csbs import CSBSLocationService

# Instances of the services.
jhu = JhuLocationService()
csbs = CSBSLocationService()
2 changes: 1 addition & 1 deletion app/services/location/csbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def get_locations():
),

# Last update (parse as ISO).
datetime.strptime(last_update, '%Y/%m/%d %H:%M').isoformat() + 'Z',
datetime.strptime(last_update, '%Y-%m-%d %H:%M').isoformat() + 'Z',

# Statistics.
int(item['Confirmed'] or 0),
Expand Down
22 changes: 16 additions & 6 deletions app/services/location/jhu.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def get(self, id):
"""
Base URL for fetching category.
"""
base_url = 'https://raw.githubusercontent.com/CSSEGISandData/2019-nCoV/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-%s.csv';
base_url = 'https://raw.githubusercontent.com/CSSEGISandData/2019-nCoV/master/csse_covid_19_data/csse_covid_19_time_series/';

@cached(cache=TTLCache(maxsize=1024, ttl=3600))
def get_category(category):
Expand All @@ -39,10 +39,20 @@ def get_category(category):
"""

# Adhere to category naming standard.
category = category.lower().capitalize();
category = category.lower();

# URL to request data from.
url = base_url + 'time_series_covid19_%s_global.csv' % category

# Different URL is needed for recoveries.
# Read about deprecation here: https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data/csse_covid_19_time_series.
if category == 'recovered':
url = base_url + 'time_series_19-covid-Recovered.csv'

print (url)

# Request the data
request = requests.get(base_url % category)
request = requests.get(url)
text = request.text

# Parse the CSV.
Expand Down Expand Up @@ -106,7 +116,7 @@ def get_locations():
# Get all of the data categories locations.
confirmed = get_category('confirmed')['locations']
deaths = get_category('deaths')['locations']
recovered = get_category('recovered')['locations']
# recovered = get_category('recovered')['locations']

# Final locations to return.
locations = []
Expand All @@ -117,7 +127,7 @@ def get_locations():
timelines = {
'confirmed' : confirmed[index]['history'],
'deaths' : deaths[index]['history'],
'recovered' : recovered[index]['history'],
# 'recovered' : recovered[index]['history'],
}

# Grab coordinates.
Expand All @@ -141,7 +151,7 @@ def get_locations():
{
'confirmed': Timeline({ datetime.strptime(date, '%m/%d/%y').isoformat() + 'Z': amount for date, amount in timelines['confirmed'].items() }),
'deaths' : Timeline({ datetime.strptime(date, '%m/%d/%y').isoformat() + 'Z': amount for date, amount in timelines['deaths'].items() }),
'recovered': Timeline({ datetime.strptime(date, '%m/%d/%y').isoformat() + 'Z': amount for date, amount in timelines['recovered'].items() })
'recovered': Timeline({})
}
))

Expand Down
10 changes: 9 additions & 1 deletion app/timeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ def latest(self):
"""
Gets the latest available history value.
"""
return list(self.timeline.values())[-1] or 0
# Get values in a list.
values = list(self.timeline.values())

# Last item is the latest.
if len(values):
return values[-1] or 0

# Fallback value of 0.
return 0

def serialize(self):
"""
Expand Down
152 changes: 0 additions & 152 deletions coronavirus-tracker-api-swagger.yaml

This file was deleted.

Loading