From 1093d5301f3c282bf7d1a67bdd6b76b6eab4aaf4 Mon Sep 17 00:00:00 2001 From: Deema Al Sheikhly Date: Tue, 16 Mar 2021 22:54:54 -0400 Subject: [PATCH 1/2] aggregation and encapsulation --- .gitignore | 1 + COVID19Py/__init__.py | 2 +- COVID19Py/covid19.py | 52 +++++++++++++++++++++++++++---------------- COVID19Py/sources.py | 21 +++++++++++++++++ Pipfile | 1 - Pipfile.lock | 41 ++-------------------------------- examples/latest.py | 3 +-- requirements.txt | 2 +- 8 files changed, 60 insertions(+), 63 deletions(-) create mode 100644 COVID19Py/sources.py 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/__init__.py b/COVID19Py/__init__.py index 33c6cf4..1da2080 100644 --- a/COVID19Py/__init__.py +++ b/COVID19Py/__init__.py @@ -1 +1 @@ -from .covid19 import COVID19 +from .covid19 import COVID19 \ No newline at end of file diff --git a/COVID19Py/covid19.py b/COVID19Py/covid19.py index a68faa7..6b50857 100644 --- a/COVID19Py/covid19.py +++ b/COVID19Py/covid19.py @@ -1,6 +1,8 @@ from typing import Dict, List import requests import json +from .sources import Reterivedata + class COVID19(object): default_url = "https://covid-tracker-us.herokuapp.com" @@ -27,7 +29,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 +48,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 +70,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 +95,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]: @@ -107,12 +108,15 @@ def getLocations(self, timelines=False, rank_by: str = None) -> List[Dict]: """ data = None if timelines: - data = self._request("/v2/locations", {"timelines": str(timelines).lower()}) + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"timelines": str(timelines).lower()}) + data = retrieve.retreive() else: - data = self._request("/v2/locations") + 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: @@ -131,11 +135,16 @@ def getLocationByCountryCode(self, country_code, timelines=False) -> List[Dict]: """ data = None if timelines: - data = self._request("/v2/locations", {"country_code": country_code, "timelines": str(timelines).lower()}) + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"country_code": country_code, "timelines": str(timelines).lower()}) + data = retrieve.retreive() else: - data = self._request("/v2/locations", {"country_code": country_code}) + 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 @@ -144,9 +153,13 @@ def getLocationByCountry(self, country, timelines=False) -> List[Dict]: """ data = None if timelines: - data = self._request("/v2/locations", {"country": country, "timelines": str(timelines).lower()}) + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"country": country, "timelines": str(timelines).lower()}) + data = retrieve.retreive() else: - data = self._request("/v2/locations", {"country": country}) + retrieve = Reterivedata(self.url, self.data_source, "/v2/locations", + {"country": country}) + data = retrieve.retreive() return data["locations"] def getLocationById(self, country_id: int): @@ -154,5 +167,6 @@ 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)) + 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 b/Pipfile index b12d70f..7a9e19a 100644 --- a/Pipfile +++ b/Pipfile @@ -4,7 +4,6 @@ verify_ssl = true name = "pypi" [packages] -requests = "~=2.24.0" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index ef1a7b1..419c7ab 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "7f2f5d1adbeccc71da52e4aa14bc80a823d3952b32091b58fbe67b6d8c826a01" + "sha256": "415dfdcb118dd9bdfef17671cb7dcd78dbd69b6ae7d4f39e8b44e71d60ca72e7" }, "pipfile-spec": 6, "requires": { @@ -15,43 +15,6 @@ } ] }, - "default": { - "certifi": { - "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" - ], - "version": "==2020.6.20" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "idna": { - "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" - ], - "version": "==2.10" - }, - "requests": { - "hashes": [ - "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", - "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" - ], - "index": "pypi", - "version": "==2.24.0" - }, - "urllib3": { - "hashes": [ - "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", - "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" - ], - "version": "==1.25.10" - } - }, + "default": {}, "develop": {} } diff --git a/examples/latest.py b/examples/latest.py index 5d95cdb..a6ab6b5 100644 --- a/examples/latest.py +++ b/examples/latest.py @@ -1,5 +1,4 @@ -import COVID19Py +from COVID19Py import COVID19Py covid19 = COVID19Py.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 From 583615afc30e19b799e507797a772f3c581f44fb Mon Sep 17 00:00:00 2001 From: Deema Al Sheikhly Date: Wed, 17 Mar 2021 14:06:16 -0400 Subject: [PATCH 2/2] aggregated locations --- COVID19Py/__init__.py | 2 +- COVID19Py/covid19.py | 52 +++++---------------------- COVID19Py/location.py | 81 +++++++++++++++++++++++++++++++++++++++++++ Pipfile | 1 + Pipfile.lock | 45 ++++++++++++++++++++++-- examples/latest.py | 4 +-- 6 files changed, 136 insertions(+), 49 deletions(-) create mode 100644 COVID19Py/location.py diff --git a/COVID19Py/__init__.py b/COVID19Py/__init__.py index 1da2080..33c6cf4 100644 --- a/COVID19Py/__init__.py +++ b/COVID19Py/__init__.py @@ -1 +1 @@ -from .covid19 import COVID19 \ No newline at end of file +from .covid19 import COVID19 diff --git a/COVID19Py/covid19.py b/COVID19Py/covid19.py index 6b50857..6427b11 100644 --- a/COVID19Py/covid19.py +++ b/COVID19Py/covid19.py @@ -2,6 +2,7 @@ import requests import json from .sources import Reterivedata +from .location import Location class COVID19(object): @@ -106,26 +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: - 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 + 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]: """ @@ -133,17 +117,8 @@ 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: - 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"] + 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]: """ @@ -151,22 +126,13 @@ def getLocationByCountry(self, country, timelines=False) -> List[Dict]: :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"] + 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. """ - retrieve = Reterivedata(self.url, self.data_source, "/v2/locations/" + str(country_id)) - data = retrieve.retreive() - 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/Pipfile b/Pipfile index 7a9e19a..b12d70f 100644 --- a/Pipfile +++ b/Pipfile @@ -4,6 +4,7 @@ verify_ssl = true name = "pypi" [packages] +requests = "~=2.24.0" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 419c7ab..7a2e9cb 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "415dfdcb118dd9bdfef17671cb7dcd78dbd69b6ae7d4f39e8b44e71d60ca72e7" + "sha256": "98578904fca2ae10bf5de4bb659ffc5d7871307bc1fbde49890e94a78b58ef9a" }, "pipfile-spec": 6, "requires": { - "python_version": "3.6" + "python_version": "3.9" }, "sources": [ { @@ -15,6 +15,45 @@ } ] }, - "default": {}, + "default": { + "certifi": { + "hashes": [ + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" + ], + "version": "==2020.12.5" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "idna": { + "hashes": [ + "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": { + "hashes": [ + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + ], + "index": "pypi", + "version": "==2.24.0" + }, + "urllib3": { + "hashes": [ + "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", + "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" + ], + "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 a6ab6b5..cac2f09 100644 --- a/examples/latest.py +++ b/examples/latest.py @@ -1,4 +1,4 @@ -from COVID19Py import COVID19Py +from COVID19Py import COVID19 -covid19 = COVID19Py.COVID19() +covid19 = COVID19() print(covid19.getLatest())