diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..2d0d7eb9
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -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']
diff --git a/README.md b/README.md
index 2c1b3304..840efc72 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,7 @@
-## Coronavirus Tracker API
+
+ Coronavirus Tracker API
+
+
Provides up-to-date data about Coronavirus outbreak. Includes numbers about confirmed cases, deaths and recovered.
Support multiple data-sources.
@@ -18,6 +21,10 @@ Support multiple data-sources.


+## 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:
@@ -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:
@@ -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
diff --git a/app/main.py b/app/main.py
index 5b62ac9c..8e0b367c 100644
--- a/app/main.py
+++ b/app/main.py
@@ -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,
@@ -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
diff --git a/app/models.py b/app/models.py
index a67b7573..36b1396f 100644
--- a/app/models.py
+++ b/app/models.py
@@ -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
diff --git a/app/services/__init__.py b/app/services/__init__.py
index 4031209d..e69de29b 100644
--- a/app/services/__init__.py
+++ b/app/services/__init__.py
@@ -1,6 +0,0 @@
-from .location.jhu import JhuLocationService
-from .location.csbs import CSBSLocationService
-
-# Instances of the services.
-jhu = JhuLocationService()
-csbs = CSBSLocationService()
\ No newline at end of file
diff --git a/app/services/location/csbs.py b/app/services/location/csbs.py
index f6140b75..b0f3a2e2 100644
--- a/app/services/location/csbs.py
+++ b/app/services/location/csbs.py
@@ -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),
diff --git a/app/services/location/jhu.py b/app/services/location/jhu.py
index af5c126e..0a42c9d9 100644
--- a/app/services/location/jhu.py
+++ b/app/services/location/jhu.py
@@ -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):
@@ -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.
@@ -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 = []
@@ -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.
@@ -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({})
}
))
diff --git a/app/timeline.py b/app/timeline.py
index dc9adacd..44e54c12 100644
--- a/app/timeline.py
+++ b/app/timeline.py
@@ -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):
"""
diff --git a/coronavirus-tracker-api-swagger.yaml b/coronavirus-tracker-api-swagger.yaml
deleted file mode 100644
index d17b9d42..00000000
--- a/coronavirus-tracker-api-swagger.yaml
+++ /dev/null
@@ -1,152 +0,0 @@
-swagger: '2.0'
-info:
- description: 'Corona Virus Tracker API based on JHU data sets. https://github.com/ExpDev07/coronavirus-tracker-api'
- version: 1.0.0
- title: Corona Virus Tracker API
- contact:
- url: https://github.com/ExpDev07/coronavirus-tracker-api
-host: coronavirus-tracker-api.herokuapp.com
-basePath: /v2
-schemes:
- - https
-paths:
- /latest:
- get:
- summary: Latest global Corona stats
- operationId: latest
- produces:
- - application/json
- responses:
- '200':
- description: successful operation
- schema:
- type: object
- properties:
- latest:
- type: object
- properties:
- confirmed:
- type: number
- example: 214910
- deaths:
- type: number
- example: 8733
- recovered:
- type: number
- example: 83207
- /locations:
- get:
- summary: All Locations
- operationId: allLocations
- produces:
- - application/json
- parameters:
- - name: country_code
- in: query
- description: "ISO alpha 2 country code. https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2"
- required: false
- type: string
- x-example: US
- - name: timelines
- in: query
- description: "Include timeline objects in response? true or false"
- required: false
- type: string
- x-example: true
- responses:
- '200':
- description: successful operation
- schema:
- type: object
- properties:
- locations:
- type: array
- items:
- $ref: '#/definitions/Location'
- '/locations/{id}':
- get:
- summary: location
- operationId: location
- produces:
- - application/json
- parameters:
- - name: id
- in: path
- description: "Internal ID"
- required: true
- type: string
- x-example: 39
- responses:
- '200':
- description: successful operation
- schema:
- type: object
- properties:
- location:
- $ref: '#/definitions/Location'
- '404':
- description: Location not found
-definitions:
- Location:
- type: object
- properties:
- coordinates:
- type: object
- properties:
- latitude:
- type: string
- example: "47.4009"
- longitude:
- type: string
- example: "-121.4905"
- country:
- type: string
- example: "US"
- country_code:
- type: string
- example: US
- id:
- type: number
- example: 98
- latest:
- type: object
- properties:
- confirmed:
- type: number
- example: 1014
- deaths:
- type: number
- example: 55
- recovered:
- type: number
- example: 10
- province:
- type: string
- example: "Washington"
- timelines:
- type: object
- properties:
- confirmed:
- type: object
- properties:
- latest:
- type: number
- example: 1014
- timeline:
- type: object
- deaths:
- type: object
- properties:
- latest:
- type: number
- example: 55
- timeline:
- type: object
- recovered:
- type: object
- properties:
- latest:
- type: number
- example: 10
- timeline:
- type: object
diff --git a/tests/example_data/sample_covid19_county.csv b/tests/example_data/sample_covid19_county.csv
index 9f89341d..ee972c59 100644
--- a/tests/example_data/sample_covid19_county.csv
+++ b/tests/example_data/sample_covid19_county.csv
@@ -1,33 +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
+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
diff --git a/tests/example_data/time_series_19-covid-Confirmed.csv b/tests/example_data/time_series_covid19_confirmed_global.csv
similarity index 100%
rename from tests/example_data/time_series_19-covid-Confirmed.csv
rename to tests/example_data/time_series_covid19_confirmed_global.csv
diff --git a/tests/example_data/time_series_19-covid-Deaths.csv b/tests/example_data/time_series_covid19_deaths_global.csv
similarity index 100%
rename from tests/example_data/time_series_19-covid-Deaths.csv
rename to tests/example_data/time_series_covid19_deaths_global.csv
diff --git a/tests/test_jhu.py b/tests/test_jhu.py
index 7aba5f74..a503a1c2 100644
--- a/tests/test_jhu.py
+++ b/tests/test_jhu.py
@@ -24,7 +24,15 @@ def read_file(self, state):
"""
Mock HTTP GET-method and return text from file
"""
- filepath = "tests/example_data/time_series_19-covid-{}.csv".format(state)
+ state = state.lower()
+
+ # Determine filepath.
+ filepath = "tests/example_data/{}.csv".format(state)
+
+ if state == 'recovered':
+ filepath = 'tests/example_data/time_series_19-covid-Recovered.csv'
+
+ # Return fake response.
print("Try to read {}".format(filepath))
with open(filepath, "r") as file:
return file.read()
@@ -58,76 +66,6 @@ def isoformat(self):
return DateTimeStrpTime(date, strformat)
-@pytest.mark.parametrize("category, capitalize_category", [
- ("deaths", "Deaths"),
- ("recovered", "Recovered"),
- ("confirmed", "Confirmed")])
-@mock.patch('app.services.location.jhu.requests.get', side_effect=mocked_requests_get)
-def test_validate_category(mock_request_get, category, capitalize_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'
- request = app.services.location.jhu.requests.get(base_url % category)
-
- assert request.state == capitalize_category
-
-@pytest.mark.parametrize("category, datetime_str, latest_value, country_name, \
- country_code, province, latest_country_value, \
- coordinate_lat, coordinate_long",
- [("deaths", DATETIME_STRING, 1940, "Thailand", "TH", "",
- 114, "15", "101"),
- ("recovered", DATETIME_STRING, 1940, "Thailand", "TH", "",
- 114, "15", "101"),
- ("confirmed", DATETIME_STRING, 1940, "Thailand", "TH", "",
- 114, "15", "101")])
-@mock.patch('app.services.location.jhu.datetime')
-@mock.patch('app.services.location.jhu.requests.get', side_effect=mocked_requests_get)
-def test_get_category(mock_request_get, mock_datetime, category, datetime_str,
- latest_value, country_name, country_code, province, latest_country_value,
- coordinate_lat, coordinate_long):
- #mock app.services.location.jhu.datetime.utcnow().isoformat()
- mock_datetime.utcnow.return_value.isoformat.return_value = datetime_str
- output = jhu.get_category(category)
-
- #simple schema validation
- assert output["source"] == "https://github.com/ExpDev07/coronavirus-tracker-api"
-
- assert isinstance(output["latest"], int)
- assert output["latest"] == latest_value #based on example data
-
- #check for valid datestring
- assert date.is_date(output["last_updated"]) is True
- #ensure date formating
- assert output["last_updated"] == datetime_str + "Z" #based on example data
-
- #validate location schema
- location_entry = output["locations"][0]
-
- assert isinstance(location_entry["country"], str)
- assert location_entry["country"] == country_name #based on example data
-
- assert isinstance(location_entry["country_code"], str)
- assert len(location_entry["country_code"]) == 2
- assert location_entry["country_code"] == country_code #based on example data
-
- assert isinstance(location_entry["province"], str)
- assert location_entry["province"] == province #based on example data
-
- assert isinstance(location_entry["latest"], int)
- assert location_entry["latest"] == latest_country_value #based on example data
-
- #validate coordinates in location
- coordinates = location_entry["coordinates"]
-
- assert isinstance(coordinates["lat"], str)
- assert coordinates["lat"] == coordinate_lat
-
- assert isinstance(coordinates["long"], str)
- assert coordinates["long"] == coordinate_long
-
- #validate history in location
- history = location_entry["history"]
- assert date.is_date(list(history.keys())[0]) is True
- assert isinstance(list(history.values())[0], int)
-
@mock.patch('app.services.location.jhu.datetime')
@mock.patch('app.services.location.jhu.requests.get', side_effect=mocked_requests_get)
def test_get_locations(mock_request_get, mock_datetime):
diff --git a/tests/test_routes.py b/tests/test_routes.py
index 18138f09..e08eb349 100644
--- a/tests/test_routes.py
+++ b/tests/test_routes.py
@@ -86,7 +86,7 @@ def test_v2_latest(self, mock_request_get, mock_datetime):
'latest': {
'confirmed': 1940,
'deaths': 1940,
- 'recovered': 1940
+ 'recovered': 0
}
}