From 51ebbe4af0e7d606e46b2f881f4c63d4f45e7824 Mon Sep 17 00:00:00 2001 From: ashnaw <44254118+ashnaw@users.noreply.github.com> Date: Sat, 24 Jul 2021 00:01:01 -0400 Subject: [PATCH 1/6] Update __init__.py --- app/location/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/location/__init__.py b/app/location/__init__.py index 1da5e9e5..46d138bb 100644 --- a/app/location/__init__.py +++ b/app/location/__init__.py @@ -1,6 +1,6 @@ """app.location""" from ..coordinates import Coordinates -from ..utils import countries +from ..utils.countries import CountryCodeUtil from ..utils.populations import country_population @@ -9,6 +9,7 @@ class Location: # pylint: disable=too-many-instance-attributes """ A location in the world affected by the coronavirus. """ + country_code_util = CountryCodeUtil() def __init__( self, id, country, province, coordinates, last_updated, confirmed, deaths, recovered, @@ -31,17 +32,15 @@ def __init__( def country_code(self): """ Gets the alpha-2 code represention of the country. Returns 'XX' if none is found. - :returns: The country code. :rtype: str """ - return (countries.country_code(self.country) or countries.DEFAULT_COUNTRY_CODE).upper() + return (country_code_util.get_country_code(self.country) or country_code_util.DEFAULT_COUNTRY_CODE).upper() @property def country_population(self): """ Gets the population of this location. - :returns: The population. :rtype: int """ @@ -50,7 +49,6 @@ def country_population(self): def serialize(self): """ Serializes the location into a dict. - :returns: The serialized location. :rtype: dict """ @@ -101,7 +99,6 @@ def __init__(self, id, country, province, coordinates, last_updated, timelines): def serialize(self, timelines=False): """ Serializes the location into a dict. - :param timelines: Whether to include the timelines. :returns: The serialized location. :rtype: dict From 5039651e3cfe88615bb4b02c35f6f6658682829d Mon Sep 17 00:00:00 2001 From: ashnaw <44254118+ashnaw@users.noreply.github.com> Date: Sat, 24 Jul 2021 00:19:41 -0400 Subject: [PATCH 2/6] Update countries.py --- app/utils/countries.py | 387 ++--------------------------------------- 1 file changed, 19 insertions(+), 368 deletions(-) diff --git a/app/utils/countries.py b/app/utils/countries.py index 9fb4f98a..aeef31da 100644 --- a/app/utils/countries.py +++ b/app/utils/countries.py @@ -1,380 +1,31 @@ """app.utils.countries.py""" import logging - -LOGGER = logging.getLogger(__name__) +import app.io # Default country code. -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 -COUNTRY_NAME__COUNTRY_CODE = { - "Afghanistan" : "AF", - "Åland Islands" : "AX", - "Albania" : "AL", - "Algeria" : "DZ", - "American Samoa" : "AS", - "Andorra" : "AD", - "Angola" : "AO", - "Anguilla" : "AI", - "Antarctica" : "AQ", - "Antigua and Barbuda" : "AG", - "Argentina" : "AR", - "Armenia" : "AM", - "Aruba" : "AW", - "Australia" : "AU", - "Austria" : "AT", - "Azerbaijan" : "AZ", - " Azerbaijan" : "AZ", - "Bahamas" : "BS", - "The Bahamas" : "BS", - "Bahamas, The" : "BS", - "Bahrain" : "BH", - "Bangladesh" : "BD", - "Barbados" : "BB", - "Belarus" : "BY", - "Belgium" : "BE", - "Belize" : "BZ", - "Benin" : "BJ", - "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", - "Cambodia" : "KH", - "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", - "Equatorial Guinea" : "GQ", - "Eritrea" : "ER", - "Estonia" : "EE", - "Ethiopia" : "ET", - "Falkland Islands (Malvinas)" : "FK", - "Falkland Islands" : "FK", - "Faroe Islands" : "FO", - "Faeroe Islands" : "FO", - "Fiji" : "FJ", - "Finland" : "FI", - "France" : "FR", - "French Guiana" : "GF", - "French Polynesia" : "PF", - "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", - "Greenland" : "GL", - "Grenada" : "GD", - "Guadeloupe" : "GP", - "Guam" : "GU", - "Guatemala" : "GT", - "Guernsey" : "GG", - "Guinea" : "GN", - "Guinea-Bissau" : "GW", - "Guyana" : "GY", - "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", - "Liberia" : "LR", - "Libya" : "LY", - "Liechtenstein" : "LI", - "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", - "Maldives" : "MV", - "Mali" : "ML", - "Malta" : "MT", - "Marshall Islands" : "MH", - "Martinique" : "MQ", - "Mauritania" : "MR", - "Mauritius" : "MU", - "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", - "Montserrat" : "MS", - "Morocco" : "MA", - "Mozambique" : "MZ", - "Myanmar" : "MM", - "Burma" : "MM", - "Namibia" : "NA", - "Nauru" : "NR", - "Nepal" : "NP", - "Netherlands" : "NL", - "New Caledonia" : "NC", - "New Zealand" : "NZ", - "Nicaragua" : "NI", - "Niger" : "NE", - "Nigeria" : "NG", - "Niue" : "NU", - "Norfolk Island" : "NF", - "Northern Mariana Islands" : "MP", - "Norway" : "NO", - "Oman" : "OM", - "Pakistan" : "PK", - "Palau" : "PW", - "Palestine, State of" : "PS", - "Palestine" : "PS", - "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", - "Peru" : "PE", - "Philippines" : "PH", - "Pitcairn" : "PN", - "Poland" : "PL", - "Portugal" : "PT", - "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", - "Seychelles" : "SC", - "Sierra Leone" : "SL", - "Singapore" : "SG", - "Sint Maarten (Dutch part)" : "SX", - "Sint Maarten" : "SX", - "Slovakia" : "SK", - "Slovenia" : "SI", - "Solomon Islands" : "SB", - "Somalia" : "SO", - "South Africa" : "ZA", - "South Georgia and the South Sandwich Islands" : "GS", - "South Sudan" : "SS", - "Spain" : "ES", - "Sri Lanka" : "LK", - "Sudan" : "SD", - "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", - "Trinidad and Tobago" : "TT", - "Tunisia" : "TN", - "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", - "Zimbabwe" : "ZW", - - # 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", - - # "Diamond Princess" : default_country_code, - # TODO "Disputed Territory" conflicts with `default_country_code` - # "Disputed Territory" : "XX", - - # "Others" has no mapping, i.e. the default val is used - - # ships: - # "Cruise Ship" - # "MS Zaandam" -} # fmt: on -def country_code(value): - """ - Return two letter country code (Alpha-2) according to https://en.wikipedia.org/wiki/ISO_3166-1 - Defaults to "XX". - """ - code = COUNTRY_NAME__COUNTRY_CODE.get(value, DEFAULT_COUNTRY_CODE) - if code == DEFAULT_COUNTRY_CODE: - # log at sub DEBUG level - LOGGER.log(5, f"No country code found for '{value}'. Using '{code}'!") - return code +class CountryCodeUtil: + + LOGGER = logging.getLogger(__name__) + DEFAULT_COUNTRY_CODE = "XX" + COUNTRY_NAME__COUNTRY_CODE_PATH = "country_name_country_code_path.json" #put that giant map in data folder as json + + def __init__(self): + self.COUNTRY_NAME__COUNTRY_CODE = app.io.load(COUNTRY_NAME__COUNTRY_CODE_PATH) + + def get_country_code(self,country_name): + code = self.COUNTRY_NAME__COUNTRY_CODE.get(value, DEFAULT_COUNTRY_CODE) + if code == DEFAULT_COUNTRY_CODE: + # log at sub DEBUG level + LOGGER.log(5, f"No country code found for '{country_name}'. Using '{code}'!") + return code + + def add_country_code(self,country_name,country_code): + self.COUNTRY_NAME__COUNTRY_CODE[country_name] = country_code From c9ffb55001a1e0f0dc127624db98ec014667702c Mon Sep 17 00:00:00 2001 From: ashnaw <44254118+ashnaw@users.noreply.github.com> Date: Thu, 12 Aug 2021 15:42:59 -0400 Subject: [PATCH 3/6] Creational Design pattern The location class mentioned in the init file of location folder had too many arguments passed. Whenever the programmer has to add a new serialize structure or add new arguments, they would have to change at many files to implement this. To enable this extensibility builder pattern has been implemented, which allows the programmer to create new serial structures, add more variables to location and pass very few arguments --- app/location/__init__.py | 124 +++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 71 deletions(-) diff --git a/app/location/__init__.py b/app/location/__init__.py index 46d138bb..0e46d0cf 100644 --- a/app/location/__init__.py +++ b/app/location/__init__.py @@ -2,32 +2,53 @@ from ..coordinates import Coordinates from ..utils.countries import CountryCodeUtil from ..utils.populations import country_population +from abc import ABC, abstractmethod +class LocationBuilder(ABC): + """ + An Abstract class to inherit to build coordinates of a location. + """ + @abstractmethod + def country_code(self): + pass + + @abstractmethod + def country_population(self): + pass + + @abstractmethod + def serialize(self): + pass # pylint: disable=redefined-builtin,invalid-name -class Location: # pylint: disable=too-many-instance-attributes +class Location(LocationBuilder): # pylint: disable=too-many-instance-attributes """ A location in the world affected by the coronavirus. """ - country_code_util = CountryCodeUtil() - def __init__( - self, id, country, province, coordinates, last_updated, confirmed, deaths, recovered, + self, locationDict ): # pylint: disable=too-many-arguments # General info. - self.id = id - self.country = country.strip() - self.province = province.strip() - self.coordinates = coordinates + #if all True, then location would be constructed of all the params passed. + pass - # Last update. - self.last_updated = last_updated + def build_location_dict(self,locationDict): + self.country_code_util = CountryCodeUtil() - # Statistics. - self.confirmed = confirmed - self.deaths = deaths - self.recovered = recovered + self.location_dict = locationDict.fromkeys([list(locationDict.keys())]) + for key, value in locationDict.items(): + if value: + if key == "country": + self.location_dict[key] = self.format_country(value) + elif key == "province": + self.location_dict[key] = self.format_province(value) + elif key == "coordinates": + self.location_dict[key] = self.format_coordinates(value) + else: + self.location_dict[key] = value + self.location_dict["country_population"] = self.country_population + @property def country_code(self): """ @@ -35,7 +56,7 @@ def country_code(self): :returns: The country code. :rtype: str """ - return (country_code_util.get_country_code(self.country) or country_code_util.DEFAULT_COUNTRY_CODE).upper() + return (self.country_code_util.get_country_code(self.location_dict["country"]) or self.country_code_util.DEFAULT_COUNTRY_CODE).upper() @property def country_population(self): @@ -54,68 +75,29 @@ def serialize(self): """ return { # General info. - "id": self.id, - "country": self.country, - "country_code": self.country_code, - "country_population": self.country_population, - "province": self.province, + "id": self.location_dict["id"], + "country": self.location_dict["country"], + "country_code": self.location_dict["country_code"], + "country_population": self.location_dict["country_population"], + "province": self.location_dict["province"], # Coordinates. - "coordinates": self.coordinates.serialize(), + "coordinates": self.location_dict["coordinates"], # Last updated. - "last_updated": self.last_updated, + "last_updated": self.location_dict["last_updated"], # Latest data (statistics). "latest": { - "confirmed": self.confirmed, - "deaths": self.deaths, - "recovered": self.recovered, + "confirmed": self.location_dict["confirmed"], + "deaths": self.location_dict["deaths"], + "recovered": self.location_dict["recovered"], }, } + + def format_country(self,country): + return country.strip() -class TimelinedLocation(Location): - """ - A location with timelines. - """ - - # pylint: disable=too-many-arguments - def __init__(self, id, country, province, coordinates, last_updated, timelines): - super().__init__( - # General info. - id, - country, - province, - coordinates, - last_updated, - # Statistics (retrieve latest from timelines). - confirmed=timelines.get("confirmed").latest or 0, - deaths=timelines.get("deaths").latest or 0, - recovered=timelines.get("recovered").latest or 0, - ) - - # Set timelines. - self.timelines = timelines - - # pylint: disable=arguments-differ - def serialize(self, timelines=False): - """ - Serializes the location into a dict. - :param timelines: Whether to include the timelines. - :returns: The serialized location. - :rtype: dict - """ - serialized = super().serialize() - - # Whether to include the timelines or not. - if timelines: - serialized.update( - { - "timelines": { - # Serialize all the timelines. - key: value.serialize() - for (key, value) in self.timelines.items() - } - } - ) + def format_province(self,province): + return province.strip() - # Return the serialized location. - return serialized + def format_coordinates(self, coordinates): + return coordinates.serialize() From 727f66292f5169a72f655b1278286d806356c3e9 Mon Sep 17 00:00:00 2001 From: ashnaw <44254118+ashnaw@users.noreply.github.com> Date: Thu, 12 Aug 2021 16:01:21 -0400 Subject: [PATCH 4/6] Create __init__.py The location class mentioned in the init file of location folder had too many arguments passed. Whenever the programmer has to add a new serialise structure or add new arguments, they would have to change at many files to implement this. To enable this extensibility builder pattern has been implemented, which allows the programmer to create new serial structures, add more variables to location and pass very few arguments From 60cd7f22bdcc23d02731a7a055fe3b0860b748e0 Mon Sep 17 00:00:00 2001 From: ashnaw <44254118+ashnaw@users.noreply.github.com> Date: Thu, 12 Aug 2021 16:01:40 -0400 Subject: [PATCH 5/6] Creational Design Pattern The location class mentioned in the init file of location folder had too many arguments passed. Whenever the programmer has to add a new serialise structure or add new arguments, they would have to change at many files to implement this. To enable this extensibility builder pattern has been implemented, which allows the programmer to create new serial structures, add more variables to location and pass very few arguments From a5399dfb58ffa607a71177bbcbd495e65809ff91 Mon Sep 17 00:00:00 2001 From: ashnaw <44254118+ashnaw@users.noreply.github.com> Date: Sun, 15 Aug 2021 19:29:45 +0530 Subject: [PATCH 6/6] Builder pattern (#3)