From 92d7e1cc418428779f1fb88a78aea8d28583853e Mon Sep 17 00:00:00 2001 From: Rostislav Svoboda Date: Thu, 26 Mar 2020 15:42:33 +0100 Subject: [PATCH 01/22] pipenv installation instructions --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 72a30c6a..be50e4be 100644 --- a/README.md +++ b/README.md @@ -398,6 +398,13 @@ You will need the following things properly installed on your computer. * `git clone https://github.com/ExpDev07/coronavirus-tracker-api.git` * `cd coronavirus-tracker-api` + +Now, make sure you're running python 3.8 and that your `pipenv` actually uses it! It may use packages from `$HOME/.local/lib/python3.7` even when started with `pipenv --python 3.8`. To combat this, (re)install it using: +``` +/path/to/python3.8 -m pip install pipenv +``` +And don't despair if don't get the python setup working on the first try. No one did. Guido got pretty close... once. But that's another story. Good luck. + * `pipenv shell` * `pipenv install` From 78a189c62956955094223bb44428ebcf88b34960 Mon Sep 17 00:00:00 2001 From: Rostislav Svoboda Date: Thu, 26 Mar 2020 19:30:56 +0100 Subject: [PATCH 02/22] test and linting installation instructions --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be50e4be..5d1890be 100644 --- a/README.md +++ b/README.md @@ -415,11 +415,19 @@ And don't despair if don't get the python setup working on the first try. No one ### Running Tests -* `make test` +```bash +pipenv shell +pipenv install --dev +make test +``` ### Linting -* `make lint` +```bash +pipenv shell +pipenv install --dev +make lint +``` ### Building From f1b2c1bd8fb5ea1b400ff81fe435e28ec167bc27 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Thu, 26 Mar 2020 20:12:29 -0400 Subject: [PATCH 03/22] update installation instructions --- README.md | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5d1890be..87be1edd 100644 --- a/README.md +++ b/README.md @@ -399,14 +399,15 @@ You will need the following things properly installed on your computer. * `git clone https://github.com/ExpDev07/coronavirus-tracker-api.git` * `cd coronavirus-tracker-api` -Now, make sure you're running python 3.8 and that your `pipenv` actually uses it! It may use packages from `$HOME/.local/lib/python3.7` even when started with `pipenv --python 3.8`. To combat this, (re)install it using: -``` -/path/to/python3.8 -m pip install pipenv -``` -And don't despair if don't get the python setup working on the first try. No one did. Guido got pretty close... once. But that's another story. Good luck. +1. Make sure you have [`python3.8` installed and on your `PATH`](https://docs.python-guide.org/starting/installation/). +2. [Install the `pipenv` dependency manager](https://pipenv.readthedocs.io/en/latest/install/#installing-pipenv) + * with [pipx](https://pipxproject.github.io/pipx/) `$ pipx install pipenv` + * with [Homebrew/Linuxbrew](https://pipenv.readthedocs.io/en/latest/install/#homebrew-installation-of-pipenv) `$ brew install pipenv` + * with [pip/pip3 directly](https://pipenv.readthedocs.io/en/latest/install/#pragmatic-installation-of-pipenv) `$ pip install --user pipenv` +3. Create virtual environment and install all dependencies `$ pipenv sync --dev` +4. Activate/enter the virtual environment `$ pipenv shell` -* `pipenv shell` -* `pipenv install` +And don't despair if don't get the python setup working on the first try. No one did. Guido got pretty close... once. But that's another story. Good luck. ## Running / Development @@ -416,19 +417,31 @@ And don't despair if don't get the python setup working on the first try. No one ### Running Tests ```bash +pipenv sync --dev pipenv shell -pipenv install --dev make test ``` ### Linting ```bash +pipenv sync --dev pipenv shell -pipenv install --dev make lint ``` +### Formatting + +```bash +pipenv run fmt +``` +or +```bash +pipenv shell +make fmt +``` + + ### Building ### Deploying From a0f5c008acec7bdf42e83d6c2d8ab13586f29521 Mon Sep 17 00:00:00 2001 From: Rostislav Svoboda Date: Fri, 27 Mar 2020 01:57:12 +0100 Subject: [PATCH 04/22] Renamings --- app/location/__init__.py | 4 ++-- app/services/location/jhu.py | 4 ++-- app/utils/{countrycodes.py => countries.py} | 19 +++++++++---------- app/utils/populations.py | 2 +- ...test_countrycodes.py => test_countries.py} | 12 ++++++------ 5 files changed, 20 insertions(+), 21 deletions(-) rename app/utils/{countrycodes.py => countries.py} (97%) rename tests/{test_countrycodes.py => test_countries.py} (53%) diff --git a/app/location/__init__.py b/app/location/__init__.py index cafcf547..4782fddb 100644 --- a/app/location/__init__.py +++ b/app/location/__init__.py @@ -1,5 +1,5 @@ from ..coordinates import Coordinates -from ..utils import countrycodes +from ..utils import countries from ..utils.populations import country_population @@ -31,7 +31,7 @@ def country_code(self): :returns: The country code. :rtype: str """ - return (countrycodes.country_code(self.country) or countrycodes.default_code).upper() + return (countries.country_code(self.country) or countries.default_country_code).upper() @property def country_population(self): diff --git a/app/services/location/jhu.py b/app/services/location/jhu.py index 9fde386f..ef99dddc 100644 --- a/app/services/location/jhu.py +++ b/app/services/location/jhu.py @@ -7,7 +7,7 @@ from ...coordinates import Coordinates from ...location import TimelinedLocation from ...timeline import Timeline -from ...utils import countrycodes +from ...utils import countries from ...utils import date as date_util from . import LocationService @@ -80,7 +80,7 @@ def get_category(category): { # General info. "country": country, - "country_code": countrycodes.country_code(country), + "country_code": countries.country_code(country), "province": item["Province/State"], # Coordinates. "coordinates": {"lat": item["Lat"], "long": item["Long"],}, diff --git a/app/utils/countrycodes.py b/app/utils/countries.py similarity index 97% rename from app/utils/countrycodes.py rename to app/utils/countries.py index f3e90e8f..aaec67a4 100644 --- a/app/utils/countrycodes.py +++ b/app/utils/countries.py @@ -4,13 +4,13 @@ LOGGER = logging.getLogger(__name__) # Default country code. -default_code = "XX" +default_country_code = "XX" # Mapping of country names to alpha-2 codes according to # https://en.wikipedia.org/wiki/ISO_3166-1. # As a reference see also https://github.com/TakahikoKawasaki/nv-i18n (in Java) # fmt: off -is_3166_1 = { +country_name__country_code = { "Afghanistan" : "AF", "Åland Islands" : "AX", "Albania" : "AL", @@ -269,13 +269,13 @@ "Iraq-Saudi Arabia Neutral Zone" : "XE", "Spratly Islands" : "XS", - # TODO "Disputed Territory" conflicts with `default_code` + # TODO "Disputed Territory" conflicts with `default_country_code` # "Disputed Territory" : "XX", } # Mapping of alternative names, spelling, typos to the names of countries used # by the ISO 3166-1 norm -synonyms = { +country_alias__country_name = { "Mainland China" : "China", "Czechia" : "Czech Republic", "Channel Islands" : "United Kingdom", @@ -372,16 +372,15 @@ def country_code(country): Return two letter country code (Alpha-2) according to https://en.wikipedia.org/wiki/ISO_3166-1 Defaults to "XX". """ - # Look in synonyms if not found. - if not country in is_3166_1 and country in synonyms: - country = synonyms[country] + if not country in country_name__country_code and country in country_alias__country_name: + country = country_alias__country_name[country] # Get country or fallback to default_code. - country_code = is_3166_1.get(country, default_code) + country_code = country_name__country_code.get(country, default_country_code) # Default picked? - if country_code == default_code: - LOGGER.warning(f"No country_code found for '{country}'. Using '{country_code}'!") + if country_code == default_country_code: + LOGGER.warning(f"No country code found for '{country}'. Using '{country_code}'!") # Return. return country_code diff --git a/app/utils/populations.py b/app/utils/populations.py index 8a78ec50..ea72c334 100644 --- a/app/utils/populations.py +++ b/app/utils/populations.py @@ -5,7 +5,7 @@ import requests from cachetools import TTLCache, cached -from .countrycodes import country_code +from .countries import country_code LOGGER = logging.getLogger(__name__) diff --git a/tests/test_countrycodes.py b/tests/test_countries.py similarity index 53% rename from tests/test_countrycodes.py rename to tests/test_countries.py index 1b132266..1d50421d 100644 --- a/tests/test_countrycodes.py +++ b/tests/test_countries.py @@ -1,6 +1,6 @@ import pytest -from app.utils import countrycodes +from app.utils import countries """ @@ -18,13 +18,13 @@ ("BlaBla", "XX"), ], ) -def test_countrycodes_is_3166_1(country_name, expected_country_code): - assert countrycodes.country_code(country_name) == expected_country_code +def test_countries_country_name__country_code(country_name, expected_country_code): + assert countries.country_code(country_name) == expected_country_code @pytest.mark.parametrize( - "country_name_synonym, expected_country_code", + "country_name_alias, expected_country_code", [("Deutschland", "DE"), ("Iran (Islamic Republic of)", "IR"), ("British Virgin Islands", "VG")], ) -def test_countrycodes_synonym(country_name_synonym, expected_country_code): - assert countrycodes.country_code(country_name_synonym) == expected_country_code +def test_country_name_alias(country_name_alias, expected_country_code): + assert countries.country_code(country_name_alias) == expected_country_code From a7708be3a0e379f4a184aa7668d8fb59d5bf959f Mon Sep 17 00:00:00 2001 From: Rostislav Svoboda Date: Fri, 27 Mar 2020 12:23:14 +0100 Subject: [PATCH 05/22] Merge country_alias__country_name into country_name__country_code --- app/utils/countries.py | 195 +++++++++++++++++++--------------------- tests/test_countries.py | 12 +-- 2 files changed, 94 insertions(+), 113 deletions(-) diff --git a/app/utils/countries.py b/app/utils/countries.py index aaec67a4..f4f9fad0 100644 --- a/app/utils/countries.py +++ b/app/utils/countries.py @@ -27,7 +27,10 @@ "Australia" : "AU", "Austria" : "AT", "Azerbaijan" : "AZ", + " Azerbaijan" : "AZ", "Bahamas" : "BS", + "The Bahamas" : "BS", + "Bahamas, The" : "BS", "Bahrain" : "BH", "Bangladesh" : "BD", "Barbados" : "BB", @@ -38,13 +41,18 @@ "Bermuda" : "BM", "Bhutan" : "BT", "Bolivia, Plurinational State of" : "BO", + "Bolivia" : "BO", "Bonaire, Sint Eustatius and Saba" : "BQ", + "Caribbean Netherlands" : "BQ", "Bosnia and Herzegovina" : "BA", + # "Bosnia–Herzegovina" : "BA", + "Bosnia" : "BA", "Botswana" : "BW", "Bouvet Island" : "BV", "Brazil" : "BR", "British Indian Ocean Territory" : "IO", "Brunei Darussalam" : "BN", + "Brunei" : "BN", "Bulgaria" : "BG", "Burkina Faso" : "BF", "Burundi" : "BI", @@ -52,29 +60,40 @@ "Cameroon" : "CM", "Canada" : "CA", "Cape Verde" : "CV", + "Cabo Verde" : "CV", "Cayman Islands" : "KY", "Central African Republic" : "CF", "Chad" : "TD", "Chile" : "CL", "China" : "CN", + "Mainland China" : "CN", "Christmas Island" : "CX", "Cocos (Keeling) Islands" : "CC", "Colombia" : "CO", "Comoros" : "KM", "Congo" : "CG", + "Congo (Brazzaville)" : "CG", + "Republic of the Congo" : "CG", "Congo, the Democratic Republic of the" : "CD", + "Congo (Kinshasa)" : "CD", + "DR Congo" : "CD", "Cook Islands" : "CK", "Costa Rica" : "CR", "Côte d'Ivoire" : "CI", + "Cote d'Ivoire" : "CI", + "Ivory Coast" : "CI", "Croatia" : "HR", "Cuba" : "CU", "Curaçao" : "CW", + "Curacao" : "CW", "Cyprus" : "CY", "Czech Republic" : "CZ", + "Czechia" : "CZ", "Denmark" : "DK", "Djibouti" : "DJ", "Dominica" : "DM", "Dominican Republic" : "DO", + "Dominican Rep" : "DO", "Ecuador" : "EC", "Egypt" : "EG", "El Salvador" : "SV", @@ -83,7 +102,9 @@ "Estonia" : "EE", "Ethiopia" : "ET", "Falkland Islands (Malvinas)" : "FK", + "Falkland Islands" : "FK", "Faroe Islands" : "FO", + "Faeroe Islands" : "FO", "Fiji" : "FJ", "Finland" : "FI", "France" : "FR", @@ -92,8 +113,11 @@ "French Southern Territories" : "TF", "Gabon" : "GA", "Gambia" : "GM", + "The Gambia" : "GM", + "Gambia, The" : "GM", "Georgia" : "GE", "Germany" : "DE", + "Deutschland" : "DE", "Ghana" : "GH", "Gibraltar" : "GI", "Greece" : "GR", @@ -109,31 +133,49 @@ "Haiti" : "HT", "Heard Island and McDonald Islands" : "HM", "Holy See (Vatican City State)" : "VA", + "Holy See" : "VA", + "Vatican City" : "VA", "Honduras" : "HN", "Hong Kong" : "HK", + "Hong Kong SAR" : "HK", "Hungary" : "HU", "Iceland" : "IS", "India" : "IN", "Indonesia" : "ID", "Iran, Islamic Republic of" : "IR", + "Iran" : "IR", + "Iran (Islamic Republic of)" : "IR", "Iraq" : "IQ", "Ireland" : "IE", + "Republic of Ireland" : "IE", "Isle of Man" : "IM", "Israel" : "IL", "Italy" : "IT", "Jamaica" : "JM", "Japan" : "JP", "Jersey" : "JE", + # Guernsey and Jersey form Channel Islands. Conjoin Guernsey on Jersey. + # Jersey has higher population. + # https://en.wikipedia.org/wiki/Channel_Islands + "Guernsey and Jersey" : "JE", + "Channel Islands" : "JE", + # "Channel Islands" : "GB", "Jordan" : "JO", "Kazakhstan" : "KZ", "Kenya" : "KE", "Kiribati" : "KI", "Korea, Democratic People's Republic of" : "KP", + "North Korea" : "KP", "Korea, Republic of" : "KR", + "Korea, South" : "KR", + "South Korea" : "KR", + "Republic of Korea" : "KR", "Kosovo, Republic of" : "XK", + "Kosovo" : "XK", "Kuwait" : "KW", "Kyrgyzstan" : "KG", "Lao People's Democratic Republic" : "LA", + "Laos" : "LA", "Latvia" : "LV", "Lebanon" : "LB", "Lesotho" : "LS", @@ -143,7 +185,11 @@ "Lithuania" : "LT", "Luxembourg" : "LU", "Macao" : "MO", + # TODO Macau is probably a typo. Report it to CSSEGISandData/COVID-19 + "Macau" : "MO", + "Macao SAR" : "MO", "North Macedonia" : "MK", + "Macedonia" : "MK", "Madagascar" : "MG", "Malawi" : "MW", "Malaysia" : "MY", @@ -157,7 +203,11 @@ "Mayotte" : "YT", "Mexico" : "MX", "Micronesia, Federated States of" : "FM", + "F.S. Micronesia" : "FM", + "Micronesia" : "FM", "Moldova, Republic of" : "MD", + "Republic of Moldova" : "MD", + "Moldova" : "MD", "Monaco" : "MC", "Mongolia" : "MN", "Montenegro" : "ME", @@ -182,6 +232,10 @@ "Pakistan" : "PK", "Palau" : "PW", "Palestine, State of" : "PS", + "Palestine" : "PS", + "occupied Palestinian territory" : "PS", + "State of Palestine" : "PS", + "The West Bank and Gaza" : "PS", "Panama" : "PA", "Papua New Guinea" : "PG", "Paraguay" : "PY", @@ -193,19 +247,30 @@ "Puerto Rico" : "PR", "Qatar" : "QA", "Réunion" : "RE", + "Reunion" : "RE", "Romania" : "RO", "Russian Federation" : "RU", + "Russia" : "RU", "Rwanda" : "RW", "Saint Barthélemy" : "BL", + "Saint Barthelemy" : "BL", "Saint Helena, Ascension and Tristan da Cunha" : "SH", + "Saint Helena" : "SH", "Saint Kitts and Nevis" : "KN", + "Saint Kitts & Nevis" : "KN", "Saint Lucia" : "LC", "Saint Martin (French part)" : "MF", + "Saint Martin" : "MF", + "St. Martin" : "MF", "Saint Pierre and Miquelon" : "PM", + "Saint Pierre & Miquelon" : "PM", "Saint Vincent and the Grenadines" : "VC", + "St. Vincent & Grenadines" : "VC", "Samoa" : "WS", "San Marino" : "SM", "Sao Tome and Principe" : "ST", + "São Tomé and Príncipe" : "ST", + "Sao Tome & Principe" : "ST", "Saudi Arabia" : "SA", "Senegal" : "SN", "Serbia" : "RS", @@ -213,6 +278,7 @@ "Sierra Leone" : "SL", "Singapore" : "SG", "Sint Maarten (Dutch part)" : "SX", + "Sint Maarten" : "SX", "Slovakia" : "SK", "Slovenia" : "SI", "Solomon Islands" : "SB", @@ -226,14 +292,21 @@ "Suriname" : "SR", "Svalbard and Jan Mayen" : "SJ", "Eswatini" : "SZ", # previous name "Swaziland" + "Swaziland" : "SZ", "Sweden" : "SE", "Switzerland" : "CH", "Syrian Arab Republic" : "SY", + "Syria" : "SY", "Taiwan, Province of China" : "TW", + "Taiwan*" : "TW", + "Taipei and environs" : "TW", + "Taiwan" : "TW", "Tajikistan" : "TJ", "Tanzania, United Republic of" : "TZ", + "Tanzania" : "TZ", "Thailand" : "TH", "Timor-Leste" : "TL", + "East Timor" : "TL", "Togo" : "TG", "Tokelau" : "TK", "Tonga" : "TO", @@ -242,21 +315,32 @@ "Turkey" : "TR", "Turkmenistan" : "TM", "Turks and Caicos Islands" : "TC", + "Turks and Caicos" : "TC", "Tuvalu" : "TV", "Uganda" : "UG", "Ukraine" : "UA", "United Arab Emirates" : "AE", + "Emirates" : "AE", "United Kingdom" : "GB", + "UK" : "GB", + # Conjoin North Ireland on United Kingdom + "North Ireland" : "GB", "United States" : "US", + "US" : "US", "United States Minor Outlying Islands" : "UM", "Uruguay" : "UY", "Uzbekistan" : "UZ", "Vanuatu" : "VU", "Venezuela, Bolivarian Republic of" : "VE", + "Venezuela" : "VE", "Viet Nam" : "VN", + "Vietnam" : "VN", "Virgin Islands, British" : "VG", + "British Virgin Islands" : "VG", "Virgin Islands, U.S." : "VI", + "U.S. Virgin Islands" : "VI", "Wallis and Futuna" : "WF", + "Wallis & Futuna" : "WF", "Western Sahara" : "EH", "Yemen" : "YE", "Zambia" : "ZM", @@ -265,122 +349,25 @@ # see also # https://en.wikipedia.org/wiki/List_of_sovereign_states_and_dependent_territories_by_continent_(data_file)#Data_file # https://en.wikipedia.org/wiki/List_of_sovereign_states_and_dependent_territories_by_continent - "United Nations Neutral Zone" : "XD", - "Iraq-Saudi Arabia Neutral Zone" : "XE", - "Spratly Islands" : "XS", + "United Nations Neutral Zone" : "XD", + "Iraq-Saudi Arabia Neutral Zone" : "XE", + "Spratly Islands" : "XS", # TODO "Disputed Territory" conflicts with `default_country_code` - # "Disputed Territory" : "XX", -} + # "Disputed Territory" : "XX", -# Mapping of alternative names, spelling, typos to the names of countries used -# by the ISO 3166-1 norm -country_alias__country_name = { - "Mainland China" : "China", - "Czechia" : "Czech Republic", - "Channel Islands" : "United Kingdom", - "Republic of Korea" : "Korea, Republic of", - "Republic of Moldova" : "Moldova, Republic of", - "Taiwan" : "Taiwan, Province of China", - "US" : "United States", - # TODO Macau is probably a typo. Report it to CSSEGISandData/COVID-19 - "Macau" : "Macao", - "Macao SAR" : "Macao", - "Vietnam" : "Viet Nam", - "UK" : "United Kingdom", - "Russia" : "Russian Federation", - "Iran (Islamic Republic of)" : "Iran, Islamic Republic of", - "Saint Barthelemy" : "Saint Barthélemy", - "Saint Martin" : "Saint Martin (French part)", - "Palestine" : "Palestine, State of", - "occupied Palestinian territory" : "Palestine, State of", - "State of Palestine" : "Palestine, State of", - "The West Bank and Gaza" : "Palestine, State of", - "Holy See" : "Holy See (Vatican City State)", - "Brunei" : "Brunei Darussalam", - "Hong Kong SAR" : "Hong Kong", - "Taipei and environs" : "Taiwan, Province of China", - "South Korea" : "Korea, Republic of", - "Iran" : "Iran, Islamic Republic of", - "Vatican City" : "Holy See (Vatican City State)", - "DR Congo" : "Congo, the Democratic Republic of the", - "Republic of the Congo" : "Congo", - "Tanzania" : "Tanzania, United Republic of", - "Venezuela" : "Venezuela, Bolivarian Republic of", - "North Korea" : "Korea, Democratic People's Republic of", - "Syria" : "Syrian Arab Republic", - "Bolivia" : "Bolivia, Plurinational State of", - "Laos" : "Lao People's Democratic Republic", - "Moldova" : "Moldova, Republic of", - "Eswatini" : "Swaziland", - "Cabo Verde" : "Cape Verde", - "Sao Tome & Principe" : "Sao Tome and Principe", - "Micronesia" : "Micronesia, Federated States of", - "St. Vincent & Grenadines" : "Saint Vincent and the Grenadines", - "U.S. Virgin Islands" : "Virgin Islands, U.S.", - "Saint Kitts & Nevis" : "Saint Kitts and Nevis", - "Faeroe Islands" : "Faroe Islands", - "Sint Maarten" : "Sint Maarten (Dutch part)", - "Turks and Caicos" : "Turks and Caicos Islands", - "Saint Martin" : "Saint Martin (French part)", - "British Virgin Islands" : "Virgin Islands, British", - "Wallis & Futuna" : "Wallis and Futuna", - "Saint Helena" : "Saint Helena, Ascension and Tristan da Cunha", - "Saint Pierre & Miquelon" : "Saint Pierre and Miquelon", - "Falkland Islands" : "Falkland Islands (Malvinas)", - "Republic of Ireland" : "Ireland", - "Ivory Coast" : "Côte d'Ivoire", - " Azerbaijan" : "Azerbaijan", - # Conjoin North Ireland on United Kingdom - "North Ireland" : "United Kingdom", - "East Timor" : "Timor-Leste", - "São Tomé and Príncipe" : "Sao Tome and Principe", - # Guernsey and Jersey form Channel Islands. Conjoin Guernsey on Jersey. - # Jersey has higher population. - # https://en.wikipedia.org/wiki/Channel_Islands - "Guernsey and Jersey" : "Jersey", - "Channel Islands" : "Jersey", - "Caribbean Netherlands" : "Bonaire, Sint Eustatius and Saba", - "F.S. Micronesia" : "Micronesia, Federated States of", - "Emirates" : "United Arab Emirates", - # "Bosnia–Herzegovina" : "Bosnia and Herzegovina", - "Bosnia" : "Bosnia and Herzegovina", - "Dominican Rep" : "Dominican Republic", - "Macedonia" : "North Macedonia", - "Korea, South" : "Korea, Republic of", - "Cote d'Ivoire" : "Côte d'Ivoire", - "St. Martin" : "Saint Martin (French part)", - "Congo (Kinshasa)" : "Congo, the Democratic Republic of the", - "Taiwan*" : "Taiwan, Province of China", - "Reunion" : "Réunion", - "Curacao" : "Curaçao", - "Congo (Brazzaville)" : "Congo", - "Deutschland" : "Germany", - "The Bahamas" : "Bahamas", - "The Gambia" : "Gambia", - "Kosovo" : "Kosovo, Republic of", - "Swaziland" : "Eswatini", - "Gambia, The" : "Gambia", - "Bahamas, The" : "Bahamas", # "Others" has no mapping, i.e. the default val is used # "Cruise Ship" has no mapping, i.e. the default val is used } # fmt: on -def country_code(country): +def country_code(s): """ Return two letter country code (Alpha-2) according to https://en.wikipedia.org/wiki/ISO_3166-1 Defaults to "XX". """ - if not country in country_name__country_code and country in country_alias__country_name: - country = country_alias__country_name[country] - - # Get country or fallback to default_code. - country_code = country_name__country_code.get(country, default_country_code) - - # Default picked? + country_code = country_name__country_code.get(s, default_country_code) if country_code == default_country_code: - LOGGER.warning(f"No country code found for '{country}'. Using '{country_code}'!") + LOGGER.warning(f"No country code found for '{s}'. Using '{country_code}'!") - # Return. return country_code diff --git a/tests/test_countries.py b/tests/test_countries.py index 1d50421d..2c9ba65e 100644 --- a/tests/test_countries.py +++ b/tests/test_countries.py @@ -15,16 +15,10 @@ ("Germany", "DE"), ("Bolivia, Plurinational State of", "BO"), ("Korea, Democratic People's Republic of", "KP"), - ("BlaBla", "XX"), + ("US", "US"), + ("BlaBla", countries.default_country_code), + ("Others", countries.default_country_code), ], ) def test_countries_country_name__country_code(country_name, expected_country_code): assert countries.country_code(country_name) == expected_country_code - - -@pytest.mark.parametrize( - "country_name_alias, expected_country_code", - [("Deutschland", "DE"), ("Iran (Islamic Republic of)", "IR"), ("British Virgin Islands", "VG")], -) -def test_country_name_alias(country_name_alias, expected_country_code): - assert countries.country_code(country_name_alias) == expected_country_code From 6e1330e54b743fad623086f9b8b86a365d94d942 Mon Sep 17 00:00:00 2001 From: Rostislav Svoboda Date: Fri, 27 Mar 2020 17:00:28 +0100 Subject: [PATCH 06/22] New aliases --- app/utils/countries.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/utils/countries.py b/app/utils/countries.py index f4f9fad0..6647e679 100644 --- a/app/utils/countries.py +++ b/app/utils/countries.py @@ -236,6 +236,7 @@ "occupied Palestinian territory" : "PS", "State of Palestine" : "PS", "The West Bank and Gaza" : "PS", + "West Bank and Gaza" : "PS", "Panama" : "PA", "Papua New Guinea" : "PG", "Paraguay" : "PY", @@ -353,6 +354,7 @@ "Iraq-Saudi Arabia Neutral Zone" : "XE", "Spratly Islands" : "XS", + # "Diamond Princess" : default_country_code, # TODO "Disputed Territory" conflicts with `default_country_code` # "Disputed Territory" : "XX", From dfd1dc9459eebed7665894d0b07e0bced2175c4c Mon Sep 17 00:00:00 2001 From: ibhuiyan Date: Sat, 28 Mar 2020 16:23:56 -0400 Subject: [PATCH 07/22] moved v2 routes into v2 directory so it can work alongside v1 routes with FastAPI --- app/router/__init__.py | 2 +- app/router/{ => v2}/latest.py | 6 +++--- app/router/{ => v2}/locations.py | 8 ++++---- app/router/{ => v2}/sources.py | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) rename app/router/{ => v2}/latest.py (83%) rename app/router/{ => v2}/locations.py (91%) rename app/router/{ => v2}/sources.py (76%) diff --git a/app/router/__init__.py b/app/router/__init__.py index eefb5f0a..5827ead1 100644 --- a/app/router/__init__.py +++ b/app/router/__init__.py @@ -4,4 +4,4 @@ router = APIRouter() # The routes. -from . import latest, sources, locations # isort:skip +from .v2 import latest, sources, locations # isort:skip diff --git a/app/router/latest.py b/app/router/v2/latest.py similarity index 83% rename from app/router/latest.py rename to app/router/v2/latest.py index 81b254cf..cc250bb1 100644 --- a/app/router/latest.py +++ b/app/router/v2/latest.py @@ -1,8 +1,8 @@ from fastapi import Request -from ..enums.sources import Sources -from ..models.latest import LatestResponse as Latest -from . import router +from ...enums.sources import Sources +from ...models.latest import LatestResponse as Latest +from .. import router @router.get("/latest", response_model=Latest) diff --git a/app/router/locations.py b/app/router/v2/locations.py similarity index 91% rename from app/router/locations.py rename to app/router/v2/locations.py index af4b1cfd..acb0a9f4 100644 --- a/app/router/locations.py +++ b/app/router/v2/locations.py @@ -1,9 +1,9 @@ from fastapi import HTTPException, Request -from ..enums.sources import Sources -from ..models.location import LocationResponse as Location -from ..models.location import LocationsResponse as Locations -from . import router +from ...enums.sources import Sources +from ...models.location import LocationResponse as Location +from ...models.location import LocationsResponse as Locations +from .. import router @router.get("/locations", response_model=Locations, response_model_exclude_unset=True) diff --git a/app/router/sources.py b/app/router/v2/sources.py similarity index 76% rename from app/router/sources.py rename to app/router/v2/sources.py index 538921f4..d3807246 100644 --- a/app/router/sources.py +++ b/app/router/v2/sources.py @@ -1,5 +1,5 @@ -from ..data import data_sources -from . import router +from ...data import data_sources +from .. import router @router.get("/sources") From 4e6e8232380f87a6b0edfdbcd87fb5f6e387fa29 Mon Sep 17 00:00:00 2001 From: ibhuiyan Date: Sat, 28 Mar 2020 17:16:59 -0400 Subject: [PATCH 08/22] v2 passing tests with close to new proposed structure. Edited __init__ files so v1 and v2 have their own versions of 'router'. --- app/main.py | 4 +++- app/router/__init__.py | 3 ++- app/router/v2/__init__.py | 3 +++ app/router/v2/latest.py | 2 +- app/router/v2/locations.py | 2 +- app/router/v2/sources.py | 2 +- 6 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 app/router/v2/__init__.py diff --git a/app/main.py b/app/main.py index 44876182..0cb5bdf0 100644 --- a/app/main.py +++ b/app/main.py @@ -18,7 +18,7 @@ from .models.latest import LatestResponse as Latest from .models.location import LocationResponse as Location from .models.location import LocationsResponse as Locations -from .router import router +from .router.v2 import router # ############ # FastAPI App @@ -83,12 +83,14 @@ async def handle_validation_error(request: Request, exc: pydantic.error_wrappers # Include routers. +# APP.include_router(router, prefix="/", tags=["v1"]) APP.include_router(router, prefix="/v2", tags=["v2"]) # mount the existing Flask app # v1 @ / APP.mount("/", WSGIMiddleware(create_app())) + # Running of app. if __name__ == "__main__": uvicorn.run( diff --git a/app/router/__init__.py b/app/router/__init__.py index 5827ead1..98cb038d 100644 --- a/app/router/__init__.py +++ b/app/router/__init__.py @@ -1,7 +1,8 @@ from fastapi import APIRouter # Create the router. -router = APIRouter() +# router = APIRouter() # The routes. from .v2 import latest, sources, locations # isort:skip +# from .v1 import confirmed, deaths, recovered, all diff --git a/app/router/v2/__init__.py b/app/router/v2/__init__.py new file mode 100644 index 00000000..af9233c5 --- /dev/null +++ b/app/router/v2/__init__.py @@ -0,0 +1,3 @@ +from fastapi import APIRouter + +router = APIRouter() diff --git a/app/router/v2/latest.py b/app/router/v2/latest.py index cc250bb1..8e2e561b 100644 --- a/app/router/v2/latest.py +++ b/app/router/v2/latest.py @@ -2,7 +2,7 @@ from ...enums.sources import Sources from ...models.latest import LatestResponse as Latest -from .. import router +from . import router @router.get("/latest", response_model=Latest) diff --git a/app/router/v2/locations.py b/app/router/v2/locations.py index acb0a9f4..2fde5c9e 100644 --- a/app/router/v2/locations.py +++ b/app/router/v2/locations.py @@ -3,7 +3,7 @@ from ...enums.sources import Sources from ...models.location import LocationResponse as Location from ...models.location import LocationsResponse as Locations -from .. import router +from . import router @router.get("/locations", response_model=Locations, response_model_exclude_unset=True) diff --git a/app/router/v2/sources.py b/app/router/v2/sources.py index d3807246..4ade2fef 100644 --- a/app/router/v2/sources.py +++ b/app/router/v2/sources.py @@ -1,5 +1,5 @@ from ...data import data_sources -from .. import router +from . import router @router.get("/sources") From b6ec0c7a55b7f160b3810382372ecbb7a70b35e8 Mon Sep 17 00:00:00 2001 From: ibhuiyan Date: Sat, 28 Mar 2020 17:17:37 -0400 Subject: [PATCH 09/22] forgot to push main in last commit --- app/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/main.py b/app/main.py index 0cb5bdf0..45ccc9a0 100644 --- a/app/main.py +++ b/app/main.py @@ -18,7 +18,7 @@ from .models.latest import LatestResponse as Latest from .models.location import LocationResponse as Location from .models.location import LocationsResponse as Locations -from .router.v2 import router +from .router.v2 import router as v2router # ############ # FastAPI App @@ -84,7 +84,7 @@ async def handle_validation_error(request: Request, exc: pydantic.error_wrappers # Include routers. # APP.include_router(router, prefix="/", tags=["v1"]) -APP.include_router(router, prefix="/v2", tags=["v2"]) +APP.include_router(v2router, prefix="/v2", tags=["v2"]) # mount the existing Flask app # v1 @ / From 1117c228001f3449398f81cdf9cde0e48f47b5d1 Mon Sep 17 00:00:00 2001 From: ibhuiyan Date: Sat, 28 Mar 2020 17:43:01 -0400 Subject: [PATCH 10/22] Replaced flask based v1. Not currently passing integration tests, need to change them to work with new implementation --- app/main.py | 7 +++++-- app/router/__init__.py | 5 +---- app/router/v1/__init__.py | 3 +++ app/router/v1/all.py | 21 +++++++++++++++++++++ app/router/v1/confirmed.py | 13 +++++++++++++ app/router/v1/deaths.py | 12 ++++++++++++ app/router/v1/recovered.py | 12 ++++++++++++ app/routes/__init__.py | 13 ------------- app/routes/v1/__init__.py | 0 app/routes/v1/all.py | 23 ----------------------- app/routes/v1/confirmed.py | 9 --------- app/routes/v1/deaths.py | 9 --------- app/routes/v1/recovered.py | 9 --------- 13 files changed, 67 insertions(+), 69 deletions(-) create mode 100644 app/router/v1/__init__.py create mode 100644 app/router/v1/all.py create mode 100644 app/router/v1/confirmed.py create mode 100644 app/router/v1/deaths.py create mode 100644 app/router/v1/recovered.py delete mode 100644 app/routes/__init__.py delete mode 100644 app/routes/v1/__init__.py delete mode 100644 app/routes/v1/all.py delete mode 100644 app/routes/v1/confirmed.py delete mode 100644 app/routes/v1/deaths.py delete mode 100644 app/routes/v1/recovered.py diff --git a/app/main.py b/app/main.py index 45ccc9a0..03899e73 100644 --- a/app/main.py +++ b/app/main.py @@ -10,7 +10,7 @@ import uvicorn from fastapi import FastAPI, Request, Response from fastapi.middleware.cors import CORSMiddleware -from fastapi.middleware.wsgi import WSGIMiddleware +# from fastapi.middleware.wsgi import WSGIMiddleware from fastapi.responses import JSONResponse from .core import create_app @@ -18,6 +18,7 @@ from .models.latest import LatestResponse as Latest from .models.location import LocationResponse as Location from .models.location import LocationsResponse as Locations +from .router.v1 import router as v1router from .router.v2 import router as v2router # ############ @@ -83,12 +84,14 @@ async def handle_validation_error(request: Request, exc: pydantic.error_wrappers # Include routers. -# APP.include_router(router, prefix="/", tags=["v1"]) +APP.include_router(v1router, prefix="", tags=["v1"]) APP.include_router(v2router, prefix="/v2", tags=["v2"]) +''' # mount the existing Flask app # v1 @ / APP.mount("/", WSGIMiddleware(create_app())) +''' # Running of app. diff --git a/app/router/__init__.py b/app/router/__init__.py index 98cb038d..83dbead1 100644 --- a/app/router/__init__.py +++ b/app/router/__init__.py @@ -1,8 +1,5 @@ from fastapi import APIRouter -# Create the router. -# router = APIRouter() - # The routes. from .v2 import latest, sources, locations # isort:skip -# from .v1 import confirmed, deaths, recovered, all +from .v1 import confirmed, deaths, recovered, all diff --git a/app/router/v1/__init__.py b/app/router/v1/__init__.py new file mode 100644 index 00000000..af9233c5 --- /dev/null +++ b/app/router/v1/__init__.py @@ -0,0 +1,3 @@ +from fastapi import APIRouter + +router = APIRouter() diff --git a/app/router/v1/all.py b/app/router/v1/all.py new file mode 100644 index 00000000..dc51053a --- /dev/null +++ b/app/router/v1/all.py @@ -0,0 +1,21 @@ +#from flask import jsonify + +from ...services.location.jhu import get_category +from . import router + + +@router.get("/all") +def all(): + # Get all the categories. + confirmed = get_category("confirmed") + deaths = get_category("deaths") + recovered = get_category("recovered") + + return { + # Data. + "confirmed": confirmed, + "deaths": deaths, + "recovered": recovered, + # Latest. + "latest": {"confirmed":confirmed["latest"], "deaths": deaths["latest"], "recovered": recovered["latest"],}, + } diff --git a/app/router/v1/confirmed.py b/app/router/v1/confirmed.py new file mode 100644 index 00000000..b783afa5 --- /dev/null +++ b/app/router/v1/confirmed.py @@ -0,0 +1,13 @@ +# from flask import jsonify + +# from ...routes import api_v1 as api +from ...services.location.jhu import get_category +from . import router + + +@router.get("/confirmed") +def confirmed(): + + return { + get_category("confirmed") + } diff --git a/app/router/v1/deaths.py b/app/router/v1/deaths.py new file mode 100644 index 00000000..a2710439 --- /dev/null +++ b/app/router/v1/deaths.py @@ -0,0 +1,12 @@ +# from flask import jsonify + +# from ...routes import api_v1 as api +from ...services.location.jhu import get_category +from . import router + + +@router.get("/deaths") +def deaths(): + return { + get_category("deaths") + } diff --git a/app/router/v1/recovered.py b/app/router/v1/recovered.py new file mode 100644 index 00000000..2a034832 --- /dev/null +++ b/app/router/v1/recovered.py @@ -0,0 +1,12 @@ +# from flask import jsonify + +# from ...routes import api_v1 as api +from ...services.location.jhu import get_category +from . import router + + +@router.get("/recovered") +def recovered(): + return { + get_category("recovered") + } diff --git a/app/routes/__init__.py b/app/routes/__init__.py deleted file mode 100644 index 2a584490..00000000 --- a/app/routes/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -app.routes - -isort:skip_file -""" -from flask import Blueprint, redirect, request, abort, current_app as app -from ..data import data_source - -# Follow the import order to avoid circular dependency -api_v1 = Blueprint("api_v1", __name__, url_prefix="") - -# API version 1. -from .v1 import confirmed, deaths, recovered, all diff --git a/app/routes/v1/__init__.py b/app/routes/v1/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/app/routes/v1/all.py b/app/routes/v1/all.py deleted file mode 100644 index 9638c4bd..00000000 --- a/app/routes/v1/all.py +++ /dev/null @@ -1,23 +0,0 @@ -from flask import jsonify - -from ...routes import api_v1 as api -from ...services.location.jhu import get_category - - -@api.route("/all") -def all(): - # Get all the categories. - confirmed = get_category("confirmed") - deaths = get_category("deaths") - recovered = get_category("recovered") - - return jsonify( - { - # Data. - "confirmed": confirmed, - "deaths": deaths, - "recovered": recovered, - # Latest. - "latest": {"confirmed": confirmed["latest"], "deaths": deaths["latest"], "recovered": recovered["latest"],}, - } - ) diff --git a/app/routes/v1/confirmed.py b/app/routes/v1/confirmed.py deleted file mode 100644 index 85cfe039..00000000 --- a/app/routes/v1/confirmed.py +++ /dev/null @@ -1,9 +0,0 @@ -from flask import jsonify - -from ...routes import api_v1 as api -from ...services.location.jhu import get_category - - -@api.route("/confirmed") -def confirmed(): - return jsonify(get_category("confirmed")) diff --git a/app/routes/v1/deaths.py b/app/routes/v1/deaths.py deleted file mode 100644 index cb65874b..00000000 --- a/app/routes/v1/deaths.py +++ /dev/null @@ -1,9 +0,0 @@ -from flask import jsonify - -from ...routes import api_v1 as api -from ...services.location.jhu import get_category - - -@api.route("/deaths") -def deaths(): - return jsonify(get_category("deaths")) diff --git a/app/routes/v1/recovered.py b/app/routes/v1/recovered.py deleted file mode 100644 index be5fe646..00000000 --- a/app/routes/v1/recovered.py +++ /dev/null @@ -1,9 +0,0 @@ -from flask import jsonify - -from ...routes import api_v1 as api -from ...services.location.jhu import get_category - - -@api.route("/recovered") -def recovered(): - return jsonify(get_category("recovered")) From 9502abe53ed590f1d026a581dafeb36c405152d6 Mon Sep 17 00:00:00 2001 From: ibhuiyan Date: Sat, 28 Mar 2020 20:19:44 -0400 Subject: [PATCH 11/22] Working v1 using FastAPI and passing tests. --- app/router/v1/confirmed.py | 8 ++++---- app/router/v1/deaths.py | 6 +++--- app/router/v1/recovered.py | 6 +++--- tests/test_routes.py | 26 +++++++++++++------------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/router/v1/confirmed.py b/app/router/v1/confirmed.py index b783afa5..d080374a 100644 --- a/app/router/v1/confirmed.py +++ b/app/router/v1/confirmed.py @@ -1,4 +1,5 @@ # from flask import jsonify +import json # from ...routes import api_v1 as api from ...services.location.jhu import get_category @@ -7,7 +8,6 @@ @router.get("/confirmed") def confirmed(): - - return { - get_category("confirmed") - } + confirmed = get_category("confirmed") + + return confirmed diff --git a/app/router/v1/deaths.py b/app/router/v1/deaths.py index a2710439..b9be8954 100644 --- a/app/router/v1/deaths.py +++ b/app/router/v1/deaths.py @@ -7,6 +7,6 @@ @router.get("/deaths") def deaths(): - return { - get_category("deaths") - } + deaths = get_category("deaths") + + return deaths diff --git a/app/router/v1/recovered.py b/app/router/v1/recovered.py index 2a034832..cfcf9983 100644 --- a/app/router/v1/recovered.py +++ b/app/router/v1/recovered.py @@ -7,6 +7,6 @@ @router.get("/recovered") def recovered(): - return { - get_category("recovered") - } + recovered = get_category("recovered") + + return recovered diff --git a/tests/test_routes.py b/tests/test_routes.py index 9e1c03ef..103f7505 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -6,8 +6,8 @@ import pytest from fastapi.testclient import TestClient -import app -from app import services +# import app +# from app import services from app.main import APP from .test_jhu import DATETIME_STRING, mocked_requests_get, mocked_strptime_isoformat @@ -23,10 +23,10 @@ class FlaskRoutesTest(unittest.TestCase): """ # load app context only once. - app = app.create_app() + # app = app.create_app() def setUp(self): - self.client = FlaskRoutesTest.app.test_client() + # self.client = FlaskRoutesTest.app.test_client() self.asgi_client = TestClient(APP) self.date = DATETIME_STRING @@ -48,36 +48,36 @@ def test_v1_confirmed(self, mock_request_get, mock_datetime): mock_datetime.strptime.side_effect = mocked_strptime_isoformat state = "confirmed" expected_json_output = self.read_file_v1(state=state) - return_data = self.client.get("/{}".format(state)).data.decode() + return_data = self.asgi_client.get("/{}".format(state)).json() - assert return_data == expected_json_output + assert return_data == json.loads(expected_json_output) def test_v1_deaths(self, mock_request_get, mock_datetime): mock_datetime.utcnow.return_value.isoformat.return_value = self.date mock_datetime.strptime.side_effect = mocked_strptime_isoformat state = "deaths" expected_json_output = self.read_file_v1(state=state) - return_data = self.client.get("/{}".format(state)).data.decode() + return_data = self.asgi_client.get("/{}".format(state)).json() - assert return_data == expected_json_output + assert return_data == json.loads(expected_json_output) def test_v1_recovered(self, mock_request_get, mock_datetime): mock_datetime.utcnow.return_value.isoformat.return_value = self.date mock_datetime.strptime.side_effect = mocked_strptime_isoformat state = "recovered" expected_json_output = self.read_file_v1(state=state) - return_data = self.client.get("/{}".format(state)).data.decode() + return_data = self.asgi_client.get("/{}".format(state)).json() - assert return_data == expected_json_output + assert return_data == json.loads(expected_json_output) def test_v1_all(self, mock_request_get, mock_datetime): mock_datetime.utcnow.return_value.isoformat.return_value = self.date mock_datetime.strptime.side_effect = mocked_strptime_isoformat state = "all" expected_json_output = self.read_file_v1(state=state) - return_data = self.client.get("/{}".format(state)).data.decode() - # print(return_data) - assert return_data == expected_json_output + return_data = self.asgi_client.get("/{}".format(state)).json() + + assert return_data == json.loads(expected_json_output) def test_v2_latest(self, mock_request_get, mock_datetime): mock_datetime.utcnow.return_value.isoformat.return_value = DATETIME_STRING From 7b22966931edc0fd04cc776db2728582be7ea59a Mon Sep 17 00:00:00 2001 From: ibhuiyan Date: Sat, 28 Mar 2020 20:41:56 -0400 Subject: [PATCH 12/22] Cleaned up changes for transfer of v1 from mounted as a wsgi application fo FastAPI --- app/main.py | 7 ------- app/router/v1/all.py | 2 -- app/router/v1/confirmed.py | 4 ---- app/router/v1/deaths.py | 3 --- app/router/v1/recovered.py | 3 --- tests/test_routes.py | 4 ---- 6 files changed, 23 deletions(-) diff --git a/app/main.py b/app/main.py index 03899e73..f977622d 100644 --- a/app/main.py +++ b/app/main.py @@ -10,7 +10,6 @@ import uvicorn from fastapi import FastAPI, Request, Response from fastapi.middleware.cors import CORSMiddleware -# from fastapi.middleware.wsgi import WSGIMiddleware from fastapi.responses import JSONResponse from .core import create_app @@ -87,12 +86,6 @@ async def handle_validation_error(request: Request, exc: pydantic.error_wrappers APP.include_router(v1router, prefix="", tags=["v1"]) APP.include_router(v2router, prefix="/v2", tags=["v2"]) -''' -# mount the existing Flask app -# v1 @ / -APP.mount("/", WSGIMiddleware(create_app())) -''' - # Running of app. if __name__ == "__main__": diff --git a/app/router/v1/all.py b/app/router/v1/all.py index dc51053a..bf0f7a64 100644 --- a/app/router/v1/all.py +++ b/app/router/v1/all.py @@ -1,5 +1,3 @@ -#from flask import jsonify - from ...services.location.jhu import get_category from . import router diff --git a/app/router/v1/confirmed.py b/app/router/v1/confirmed.py index d080374a..0a8ab1c3 100644 --- a/app/router/v1/confirmed.py +++ b/app/router/v1/confirmed.py @@ -1,7 +1,3 @@ -# from flask import jsonify -import json - -# from ...routes import api_v1 as api from ...services.location.jhu import get_category from . import router diff --git a/app/router/v1/deaths.py b/app/router/v1/deaths.py index b9be8954..740e4101 100644 --- a/app/router/v1/deaths.py +++ b/app/router/v1/deaths.py @@ -1,6 +1,3 @@ -# from flask import jsonify - -# from ...routes import api_v1 as api from ...services.location.jhu import get_category from . import router diff --git a/app/router/v1/recovered.py b/app/router/v1/recovered.py index cfcf9983..bfb21a51 100644 --- a/app/router/v1/recovered.py +++ b/app/router/v1/recovered.py @@ -1,6 +1,3 @@ -# from flask import jsonify - -# from ...routes import api_v1 as api from ...services.location.jhu import get_category from . import router diff --git a/tests/test_routes.py b/tests/test_routes.py index 103f7505..b1448f9a 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -22,11 +22,7 @@ class FlaskRoutesTest(unittest.TestCase): Store all integration testcases in one class to ensure app context """ - # load app context only once. - # app = app.create_app() - def setUp(self): - # self.client = FlaskRoutesTest.app.test_client() self.asgi_client = TestClient(APP) self.date = DATETIME_STRING From 6638e6e76b9e532a6a1340f0e3165a1ff01cad02 Mon Sep 17 00:00:00 2001 From: ibhuiyan Date: Sat, 28 Mar 2020 21:13:09 -0400 Subject: [PATCH 13/22] After running formatters to fix build issue --- app/router/__init__.py | 3 ++- app/router/v1/all.py | 2 +- app/router/v1/deaths.py | 2 +- app/router/v1/recovered.py | 2 +- tests/test_routes.py | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/router/__init__.py b/app/router/__init__.py index 83dbead1..e37fdd9f 100644 --- a/app/router/__init__.py +++ b/app/router/__init__.py @@ -1,5 +1,6 @@ from fastapi import APIRouter +from .v1 import all, confirmed, deaths, recovered + # The routes. from .v2 import latest, sources, locations # isort:skip -from .v1 import confirmed, deaths, recovered, all diff --git a/app/router/v1/all.py b/app/router/v1/all.py index bf0f7a64..e528ed5a 100644 --- a/app/router/v1/all.py +++ b/app/router/v1/all.py @@ -15,5 +15,5 @@ def all(): "deaths": deaths, "recovered": recovered, # Latest. - "latest": {"confirmed":confirmed["latest"], "deaths": deaths["latest"], "recovered": recovered["latest"],}, + "latest": {"confirmed": confirmed["latest"], "deaths": deaths["latest"], "recovered": recovered["latest"],}, } diff --git a/app/router/v1/deaths.py b/app/router/v1/deaths.py index 740e4101..b3d90413 100644 --- a/app/router/v1/deaths.py +++ b/app/router/v1/deaths.py @@ -5,5 +5,5 @@ @router.get("/deaths") def deaths(): deaths = get_category("deaths") - + return deaths diff --git a/app/router/v1/recovered.py b/app/router/v1/recovered.py index bfb21a51..e9ae8f72 100644 --- a/app/router/v1/recovered.py +++ b/app/router/v1/recovered.py @@ -5,5 +5,5 @@ @router.get("/recovered") def recovered(): recovered = get_category("recovered") - + return recovered diff --git a/tests/test_routes.py b/tests/test_routes.py index b1448f9a..48d804e5 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -72,7 +72,7 @@ def test_v1_all(self, mock_request_get, mock_datetime): state = "all" expected_json_output = self.read_file_v1(state=state) return_data = self.asgi_client.get("/{}".format(state)).json() - + assert return_data == json.loads(expected_json_output) def test_v2_latest(self, mock_request_get, mock_datetime): From 7ecc9da4fdfa9449093a8f017ccf2cc11a908f33 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2020 01:33:00 +0000 Subject: [PATCH 14/22] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 87be1edd..7703aa2d 100644 --- a/README.md +++ b/README.md @@ -474,6 +474,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Turreted

💻 +
Ibtida Bhuiyan

💻 From 9194f9a1e95fee64da64a7f39a781fa2cee49286 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2020 01:33:01 +0000 Subject: [PATCH 15/22] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0f568cbd..bbf06353 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -153,6 +153,15 @@ "contributions": [ "code" ] + }, + { + "login": "ibhuiyan17", + "name": "Ibtida Bhuiyan", + "avatar_url": "https://avatars1.githubusercontent.com/u/33792969?v=4", + "profile": "http://ibtida.me", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, From 86cba74781b6f6763e55b66d969b59d4045584a1 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Thu, 26 Mar 2020 21:46:44 -0400 Subject: [PATCH 16/22] add invoke as dev dependency --- Pipfile | 1 + Pipfile.lock | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index 6b30ef90..f68e0ae2 100644 --- a/Pipfile +++ b/Pipfile @@ -6,6 +6,7 @@ verify_ssl = true [dev-packages] bandit = "*" black = "==19.10b0" +invoke = "*" isort = "*" pytest = "*" pylint = "*" diff --git a/Pipfile.lock b/Pipfile.lock index c949f6cb..cc93e092 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ecd83aad2c3783fdaa5581f562d022a6b500b3f3b4beb7c3f63d3d5baff85813" + "sha256": "9574394caac3b437d4357507a93178b38289861241165e4736369a7b4c2a2cbc" }, "pipfile-spec": 6, "requires": { @@ -342,6 +342,15 @@ ], "version": "==3.1.0" }, + "invoke": { + "hashes": [ + "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132", + "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134", + "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d" + ], + "index": "pypi", + "version": "==1.4.1" + }, "isort": { "hashes": [ "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", From 10e49e1af0f76e88815dcbfdcd712a546d810a61 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Thu, 26 Mar 2020 22:59:08 -0400 Subject: [PATCH 17/22] invoke tasks --- tasks.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tasks.py diff --git a/tasks.py b/tasks.py new file mode 100644 index 00000000..45b1bd64 --- /dev/null +++ b/tasks.py @@ -0,0 +1,29 @@ +""" +tasks.py +-------- +Project invoke tasks + +Available commands + invoke --list + invoke fmt + invoke sort +""" +import invoke + +TARGETS_DESCRIPTION = "Paths/directories to format. [default: . ]" + + +@invoke.task(help={"targets": TARGETS_DESCRIPTION}) +def sort(ctx, targets="."): + """Sort module imports.""" + print("sorting imports ...") + args = ["isort", "-rc", "--atomic", targets] + ctx.run(" ".join(args)) + + +@invoke.task(pre=[sort], help={"targets": TARGETS_DESCRIPTION}) +def fmt(ctx, targets="."): + """Format python source code & sort imports.""" + print("formatting ...") + args = ["black", targets] + ctx.run(" ".join(args)) From cabb9dfe317ee11c4e3b9f1ecd53e4e4c172faf7 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Sun, 29 Mar 2020 09:20:59 -0400 Subject: [PATCH 18/22] add invoke check cmd update pipenv scripts and make commands --- Makefile | 6 ++---- Pipfile | 4 ++-- tasks.py | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 78f8f6b4..7059a9be 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,7 @@ lint: pylint $(APP) || true fmt: - isort --apply --atomic - black . -l 120 + invoke fmt check-fmt: - isort -rc --check - black . --check --diff + invoke check --fmt --sort diff --git a/Pipfile b/Pipfile index f68e0ae2..70c51299 100644 --- a/Pipfile +++ b/Pipfile @@ -28,5 +28,5 @@ python_version = "3.8" [scripts] dev = "uvicorn app.main:APP --reload" start = "uvicorn app.main:APP" -fmt = "black . -l 120" -sort = "isort --apply --atomic" +fmt = "invoke fmt" +sort = "invoke sort" diff --git a/tasks.py b/tasks.py index 45b1bd64..386857aa 100644 --- a/tasks.py +++ b/tasks.py @@ -27,3 +27,22 @@ def fmt(ctx, targets="."): print("formatting ...") args = ["black", targets] ctx.run(" ".join(args)) + +@invoke.task +def check(ctx, fmt=False, sort=False): + """Check code format and import order.""" + if not any([fmt, sort]): + fmt = True + sort = True + + fmt_args = ["black", "--check", "--diff", "."] + sort_args = ["isort", "-rc", "--check", "--diff", "."] + cmd_args = [] + + if fmt: + cmd_args.extend(fmt_args) + if sort: + if cmd_args: + cmd_args.append("&") + cmd_args.extend(sort_args) + ctx.run(" ".join(cmd_args)) From 8e92eaf339b738d6d4d084a39fda690ffc3563d5 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Sun, 29 Mar 2020 09:40:17 -0400 Subject: [PATCH 19/22] optional --diff --- tasks.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tasks.py b/tasks.py index 386857aa..919c1610 100644 --- a/tasks.py +++ b/tasks.py @@ -7,6 +7,7 @@ invoke --list invoke fmt invoke sort + invoke check """ import invoke @@ -28,17 +29,22 @@ def fmt(ctx, targets="."): args = ["black", targets] ctx.run(" ".join(args)) + @invoke.task -def check(ctx, fmt=False, sort=False): +def check(ctx, fmt=False, sort=False, diff=False): # pylint: disable=redefined-outer-name """Check code format and import order.""" if not any([fmt, sort]): fmt = True sort = True - fmt_args = ["black", "--check", "--diff", "."] - sort_args = ["isort", "-rc", "--check", "--diff", "."] - cmd_args = [] + fmt_args = ["black", "--check", "."] + sort_args = ["isort", "-rc", "--check", "."] + if diff: + fmt_args.append("--diff") + sort_args.append("--diff") + + cmd_args = [] if fmt: cmd_args.extend(fmt_args) if sort: From 539fe0470ec44da757a822045d3370d690c54259 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Sun, 29 Mar 2020 10:16:13 -0400 Subject: [PATCH 20/22] lint and test --- tasks.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tasks.py b/tasks.py index 919c1610..3ff5f24c 100644 --- a/tasks.py +++ b/tasks.py @@ -52,3 +52,15 @@ def check(ctx, fmt=False, sort=False, diff=False): # pylint: disable=redefined- cmd_args.append("&") cmd_args.extend(sort_args) ctx.run(" ".join(cmd_args)) + + +@invoke.task +def lint(ctx): + """Run linter.""" + ctx.run(" ".join(["pylint", "app"])) + + +@invoke.task +def test(ctx): + """Run pytest tests.""" + ctx.run(" ".join(["pytest", "-v"])) From 015efda0415753e0bc95efb82f5d8e9f4d68c88f Mon Sep 17 00:00:00 2001 From: Gabriel Date: Sun, 29 Mar 2020 11:40:31 -0400 Subject: [PATCH 21/22] update docs remove Flask as a project requirement add invoke commands --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 7703aa2d..cfa41b6c 100644 --- a/README.md +++ b/README.md @@ -391,7 +391,6 @@ These are the available API wrappers created by the community. They are not nece You will need the following things properly installed on your computer. * [Python 3](https://www.python.org/downloads/) (with pip) -* [Flask](https://pypi.org/project/Flask/) * [pipenv](https://pypi.org/project/pipenv/) ## Installation From 77e8ad8e2d3ce7b66af42e9baa1519e3f1db3b87 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Sun, 29 Mar 2020 11:51:18 -0400 Subject: [PATCH 22/22] add lint and test to Pipenv scripts, update docs --- Pipfile | 2 ++ README.md | 18 ++++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Pipfile b/Pipfile index 70c51299..21965c37 100644 --- a/Pipfile +++ b/Pipfile @@ -30,3 +30,5 @@ dev = "uvicorn app.main:APP --reload" start = "uvicorn app.main:APP" fmt = "invoke fmt" sort = "invoke sort" +lint = "invoke lint" +test = "invoke test" diff --git a/README.md b/README.md index cfa41b6c..18ad6645 100644 --- a/README.md +++ b/README.md @@ -414,32 +414,26 @@ And don't despair if don't get the python setup working on the first try. No one * Visit your app at [http://localhost:5000](http://localhost:5000). ### Running Tests +> pytest ```bash -pipenv sync --dev -pipenv shell -make test +pipenv run test ``` + ### Linting +> pylint ```bash -pipenv sync --dev -pipenv shell -make lint +pipenv run lint ``` ### Formatting +> black ```bash pipenv run fmt ``` -or -```bash -pipenv shell -make fmt -``` - ### Building