Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions app/location/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""app.location"""
from ..coordinates import Coordinates
from ..utils import countries
from ..utils.populations import country_population
from ..utils.countries import Country
from ..utils.populations import Population


# pylint: disable=redefined-builtin,invalid-name
Expand All @@ -15,7 +15,7 @@ def __init__(
): # pylint: disable=too-many-arguments
# General info.
self.id = id
self.country = country.strip()
self.country = Country(country.strip())
self.province = province.strip()
self.coordinates = coordinates

Expand All @@ -35,7 +35,7 @@ def country_code(self):
:returns: The country code.
:rtype: str
"""
return (countries.country_code(self.country) or countries.DEFAULT_COUNTRY_CODE).upper()
return (self.country.country_code() or Country.DEFAULT_COUNTRY_CODE).upper()

@property
def country_population(self):
Expand All @@ -45,7 +45,7 @@ def country_population(self):
:returns: The population.
:rtype: int
"""
return country_population(self.country_code)
return Population.country_population(self.country_code)

def serialize(self):
"""
Expand Down
4 changes: 2 additions & 2 deletions app/services/location/jhu.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ...coordinates import Coordinates
from ...location import TimelinedLocation
from ...models import Timeline
from ...utils import countries
from ...utils.countries import Country
from ...utils import date as date_util
from ...utils import httputils
from . import LocationService
Expand Down Expand Up @@ -98,7 +98,7 @@ async def get_category(category):
{
# General info.
"country": country,
"country_code": countries.country_code(country),
"country_code": Country(country).country_code(),
"province": item["Province/State"],
# Coordinates.
"coordinates": {"lat": item["Lat"], "long": item["Long"],},
Expand Down
41 changes: 30 additions & 11 deletions app/utils/countries.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,15 +366,34 @@
# "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}'!")
# # 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
# return code

class Country:

def _init_(self, name):
self.name = name

# fmt: on
def country_code(self):
"""
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(self.name, DEFAULT_COUNTRY_CODE)
if code == DEFAULT_COUNTRY_CODE:
# log at sub DEBUG level
LOGGER.log(5, f"No country code found for '{self.name}'. Using '{code}'!")

return code
102 changes: 54 additions & 48 deletions app/utils/populations.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,57 @@
GEONAMES_URL = "http://api.geonames.org/countryInfoJSON"
GEONAMES_BACKUP_PATH = "geonames_population_mappings.json"

# Fetching of the populations.
def fetch_populations(save=False):
"""
Returns a dictionary containing the population of each country fetched from the GeoNames.
https://www.geonames.org/

TODO: only skip writing to the filesystem when deployed with gunicorn, or handle concurent access, or use DB.

:returns: The mapping of populations.
:rtype: dict
"""
LOGGER.info("Fetching populations...")

# Mapping of populations
mappings = {}

# Fetch the countries.
try:
countries = requests.get(GEONAMES_URL, params={"username": "dperic"}, timeout=1.25).json()[
"geonames"
]
# Go through all the countries and perform the mapping.
for country in countries:
mappings.update({country["countryCode"]: int(country["population"]) or None})

if mappings and save:
LOGGER.info(f"Saving population data to {app.io.save(GEONAMES_BACKUP_PATH, mappings)}")
except (json.JSONDecodeError, KeyError, requests.exceptions.Timeout) as err:
LOGGER.warning(f"Error pulling population data. {err.__class__.__name__}: {err}")
mappings = app.io.load(GEONAMES_BACKUP_PATH)
LOGGER.info(f"Using backup data from {GEONAMES_BACKUP_PATH}")
# Finally, return the mappings.
LOGGER.info("Fetched populations")
return mappings


# Mapping of alpha-2 codes country codes to population.
POPULATIONS = fetch_populations()

# Retrieving.
def country_population(country_code, default=None):
"""
Fetches the population of the country with the provided country code.

:returns: The population.
:rtype: int
"""
return POPULATIONS.get(country_code, default)
class Population:

def __init__(self):
self.population = fetch_populations()


# Fetching of the populations.
def fetch_populations(save=False):
"""
Returns a dictionary containing the population of each country fetched from the GeoNames.
https://www.geonames.org/

TODO: only skip writing to the filesystem when deployed with gunicorn, or handle concurent access, or use DB.

:returns: The mapping of populations.
:rtype: dict
"""
LOGGER.info("Fetching populations...")

# Mapping of populations
mappings = {}

# Fetch the countries.
try:
countries = requests.get(GEONAMES_URL, params={"username": "dperic"}, timeout=1.25).json()[
"geonames"
]
# Go through all the countries and perform the mapping.
for country in countries:
mappings.update({country["countryCode"]: int(country["population"]) or None})

if mappings and save:
LOGGER.info(f"Saving population data to {app.io.save(GEONAMES_BACKUP_PATH, mappings)}")
except (json.JSONDecodeError, KeyError, requests.exceptions.Timeout) as err:
LOGGER.warning(f"Error pulling population data. {err.__class__.__name__}: {err}")
mappings = app.io.load(GEONAMES_BACKUP_PATH)
LOGGER.info(f"Using backup data from {GEONAMES_BACKUP_PATH}")
# Finally, return the mappings.
LOGGER.info("Fetched populations")
return mappings


# Mapping of alpha-2 codes country codes to population.
POPULATIONS = fetch_populations()

# Retrieving.
def country_population(country_code, default=None):
"""
Fetches the population of the country with the provided country code.

:returns: The population.
:rtype: int
"""
return POPULATIONS.get(country_code, default)