diff --git a/.gitignore b/.gitignore index b6e4761..50c7f1d 100644 --- a/.gitignore +++ b/.gitignore @@ -119,6 +119,7 @@ venv.bak/ # mkdocs documentation /site +.idea # mypy .mypy_cache/ diff --git a/COVID19Py/covid19.py b/COVID19Py/covid19.py index a68faa7..6427b11 100644 --- a/COVID19Py/covid19.py +++ b/COVID19Py/covid19.py @@ -1,6 +1,9 @@ from typing import Dict, List import requests import json +from .sources import Reterivedata +from .location import Location + class COVID19(object): default_url = "https://covid-tracker-us.herokuapp.com" @@ -27,7 +30,9 @@ def __init__(self, url="https://covid-tracker-us.herokuapp.com", data_source='jh self.url = mirror["url"] result = None try: - result = self._getSources() + retrieve = Reterivedata(url= url, data_source= data_source, + endpoint="/v2/sources", params=None) + result = retrieve.getSources() except Exception as e: # URL did not work, reset it and move on self.url = "" @@ -44,7 +49,9 @@ def __init__(self, url="https://covid-tracker-us.herokuapp.com", data_source='jh else: self.url = url - self._valid_data_sources = self._getSources() + retrieve = Reterivedata(url=url, data_source=data_source, + endpoint="/v2/sources", params=None) + self._valid_data_sources = retrieve.getSources() if data_source not in self._valid_data_sources: raise ValueError("Invalid data source. Expected one of: %s" % self._valid_data_sources) self.data_source = data_source @@ -64,14 +71,8 @@ def _getSources(self): response.raise_for_status() return response.json()["sources"] - def _request(self, endpoint, params=None): - if params is None: - params = {} - response = requests.get(self.url + endpoint, {**params, "source":self.data_source}) - response.raise_for_status() - return response.json() - def getAll(self, timelines=False): + self._update(timelines) return self.latestData @@ -95,7 +96,8 @@ def getLatest(self) -> List[Dict[str, int]]: """ :return: The latest amount of total confirmed cases, deaths, and recoveries. """ - data = self._request("/v2/latest") + retrieve = Reterivedata(self.url, self.data_source, "/v2/latest", None) + data = retrieve.retreive() return data["latest"] def getLocations(self, timelines=False, rank_by: str = None) -> List[Dict]: @@ -105,23 +107,9 @@ def getLocations(self, timelines=False, rank_by: str = None) -> List[Dict]: :param rank_by: Category to rank results by. ex: confirmed :return: List of dictionaries representing all affected locations. """ - data = None - if timelines: - data = self._request("/v2/locations", {"timelines": str(timelines).lower()}) - else: - data = self._request("/v2/locations") - data = data["locations"] - - ranking_criteria = ['confirmed', 'deaths', 'recovered'] - if rank_by is not None: - if rank_by not in ranking_criteria: - raise ValueError("Invalid ranking criteria. Expected one of: %s" % ranking_criteria) - - ranked = sorted(data, key=lambda i: i['latest'][rank_by], reverse=True) - data = ranked - - return data + location = Location(url=self.url, data_source=self.data_source) + return location.getLocations(timelines = timelines, rank_by = rank_by) def getLocationByCountryCode(self, country_code, timelines=False) -> List[Dict]: """ @@ -129,30 +117,22 @@ def getLocationByCountryCode(self, country_code, timelines=False) -> List[Dict]: :param timelines: Whether timeline information should be returned as well. :return: A list of areas that correspond to the country_code. If the country_code is invalid, it returns an empty list. """ - data = None - if timelines: - data = self._request("/v2/locations", {"country_code": country_code, "timelines": str(timelines).lower()}) - else: - data = self._request("/v2/locations", {"country_code": country_code}) - return data["locations"] - + location = Location(url=self.url, data_source=self.data_source) + return location.getLocationByCountryCode(country_code= country_code, timelines=timelines) + def getLocationByCountry(self, country, timelines=False) -> List[Dict]: """ :param country: String denoting name of the country :param timelines: Whether timeline information should be returned as well. :return: A list of areas that correspond to the country name. If the country is invalid, it returns an empty list. """ - data = None - if timelines: - data = self._request("/v2/locations", {"country": country, "timelines": str(timelines).lower()}) - else: - data = self._request("/v2/locations", {"country": country}) - return data["locations"] + location = Location(url=self.url, data_source=self.data_source) + return location.getLocationByCountry(country = country, timelines=timelines) def getLocationById(self, country_id: int): """ :param country_id: Country Id, an int :return: A dictionary with case information for the specified location. """ - data = self._request("/v2/locations/" + str(country_id)) - return data["location"] + location = Location(url=self.url, data_source=self.data_source) + return location.getLocationById(country_id = country_id) diff --git a/COVID19Py/location.py b/COVID19Py/location.py new file mode 100644 index 0000000..0090a62 --- /dev/null +++ b/COVID19Py/location.py @@ -0,0 +1,81 @@ +from typing import List, Dict + +from .sources import Reterivedata + + +class Location: + def __init__(self, url, data_source): + self.url = url + self.data_source = data_source + + def getLocations(self, timelines=False, rank_by: str = None) -> List[Dict]: + """ + Gets all locations affected by COVID-19, as well as latest case data. + :param timelines: Whether timeline information should be returned as well. + :param rank_by: Category to rank results by. ex: confirmed + :return: List of dictionaries representing all affected locations. + """ + data = None + if timelines: + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"timelines": str(timelines).lower()}) + data = retrieve.retreive() + else: + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations") + data = retrieve.retreive() + + data = data["locations"] + + ranking_criteria = ['confirmed', 'deaths', 'recovered'] + if rank_by is not None: + if rank_by not in ranking_criteria: + raise ValueError("Invalid ranking criteria. Expected one of: %s" % ranking_criteria) + + ranked = sorted(data, key=lambda i: i['latest'][rank_by], reverse=True) + data = ranked + + return data + + def getLocationByCountryCode(self, country_code, timelines=False) -> List[Dict]: + """ + :param country_code: String denoting the ISO 3166-1 alpha-2 code (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the country + :param timelines: Whether timeline information should be returned as well. + :return: A list of areas that correspond to the country_code. If the country_code is invalid, it returns an empty list. + """ + data = None + if timelines: + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"country_code": country_code, "timelines": str(timelines).lower()}) + data = retrieve.retreive() + else: + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"country_code": country_code}) + data = retrieve.retreive() + + return data["locations"] + + def getLocationByCountry(self, country, timelines=False) -> List[Dict]: + """ + :param country: String denoting name of the country + :param timelines: Whether timeline information should be returned as well. + :return: A list of areas that correspond to the country name. If the country is invalid, it returns an empty list. + """ + data = None + if timelines: + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"country": country, "timelines": str(timelines).lower()}) + data = retrieve.retreive() + else: + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"country": country}) + data = retrieve.retreive() + return data["locations"] + + def getLocationById(self, country_id: int): + """ + :param country_id: Country Id, an int + :return: A dictionary with case information for the specified location. + """ + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations/" + str(country_id)) + data = retrieve.retreive() + return data["location"] diff --git a/COVID19Py/sources.py b/COVID19Py/sources.py new file mode 100644 index 0000000..530d70c --- /dev/null +++ b/COVID19Py/sources.py @@ -0,0 +1,21 @@ +import requests + + +class Reterivedata: + def __init__(self, url, data_source, endpoint, params=None): + self.url = url + self.data_source = data_source + self.endpoint = endpoint + self.params = params if params else None + + def retreive(self): + if self.params is None: + self.params = {} + response = requests.get(self.url + self.endpoint, {**self.params, "source": self.data_source}) + response.raise_for_status() + return response.json() + + def getSources(self): + response = requests.get(self.url + "/v2/sources") + response.raise_for_status() + return response.json()["sources"] diff --git a/Pipfile.lock b/Pipfile.lock index ef1a7b1..7a2e9cb 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "7f2f5d1adbeccc71da52e4aa14bc80a823d3952b32091b58fbe67b6d8c826a01" + "sha256": "98578904fca2ae10bf5de4bb659ffc5d7871307bc1fbde49890e94a78b58ef9a" }, "pipfile-spec": 6, "requires": { - "python_version": "3.6" + "python_version": "3.9" }, "sources": [ { @@ -18,10 +18,10 @@ "default": { "certifi": { "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" ], - "version": "==2020.6.20" + "version": "==2020.12.5" }, "chardet": { "hashes": [ @@ -35,6 +35,7 @@ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.10" }, "requests": { @@ -47,10 +48,11 @@ }, "urllib3": { "hashes": [ - "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", - "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", + "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" ], - "version": "==1.25.10" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.25.11" } }, "develop": {} diff --git a/examples/latest.py b/examples/latest.py index 5d95cdb..cac2f09 100644 --- a/examples/latest.py +++ b/examples/latest.py @@ -1,5 +1,4 @@ -import COVID19Py - -covid19 = COVID19Py.COVID19() +from COVID19Py import COVID19 +covid19 = COVID19() print(covid19.getLatest()) diff --git a/requirements.txt b/requirements.txt index d6dd6ba..f4bf043 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests~=2.24.0 +requests~=2.24.0 \ No newline at end of file