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
Applied aggregate pattern on populations.py
  • Loading branch information
anjilag committed Jul 25, 2021
commit 965fa7e366929175f7b10da44f1d15f738c0aa93
4 changes: 2 additions & 2 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.populations import Population


# pylint: disable=redefined-builtin,invalid-name
Expand Down Expand Up @@ -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
98 changes: 50 additions & 48 deletions app/utils/populations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""app.utils.populations.py"""
from _typeshed import Self
import json
import logging

Expand All @@ -10,51 +11,52 @@
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

# 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 self.population.get(country_code, default)