kerykeion.fetch_geonames

This is part of Kerykeion (C) 2025 Giacomo Battaglia

  1# -*- coding: utf-8 -*-
  2"""
  3    This is part of Kerykeion (C) 2025 Giacomo Battaglia
  4"""
  5
  6
  7import logging
  8from datetime import timedelta
  9from requests import Request
 10from requests_cache import CachedSession
 11from typing import Union
 12
 13
 14class FetchGeonames:
 15    """
 16    Class to handle requests to the GeoNames API
 17
 18    Args:
 19    - city_name (str): Name of the city
 20    - country_code (str): Two letters country code
 21    - username (str, optional): GeoNames username, defaults to "century.boy".
 22    - cache_expire_after_days (int, optional): Cache expiration time in days, defaults to 30.
 23    """
 24
 25    def __init__(
 26        self,
 27        city_name: str,
 28        country_code: str,
 29        username: str = "century.boy",
 30        cache_expire_after_days=30,
 31    ):
 32        self.session = CachedSession(
 33            cache_name="cache/kerykeion_geonames_cache",
 34            backend="sqlite",
 35            expire_after=timedelta(days=cache_expire_after_days),
 36        )
 37
 38        self.username = username
 39        self.city_name = city_name
 40        self.country_code = country_code
 41        self.base_url = "http://api.geonames.org/searchJSON"
 42        self.timezone_url = "http://api.geonames.org/timezoneJSON"
 43
 44    def __get_timezone(self, lat: Union[str, float, int], lon: Union[str, float, int]) -> dict[str, str]:
 45        """
 46        Get the timezone for a given latitude and longitude
 47        """
 48        # Dictionary that will be returned:
 49        timezone_data = {}
 50
 51        params = {"lat": lat, "lng": lon, "username": self.username}
 52
 53        prepared_request = Request("GET", self.timezone_url, params=params).prepare()
 54        logging.debug(f"Requesting data from GeoName timezones: {prepared_request.url}")
 55
 56        try:
 57            response = self.session.send(prepared_request)
 58            response_json = response.json()
 59
 60        except Exception as e:
 61            logging.error(f"Error fetching {self.timezone_url}: {e}")
 62            return {}
 63
 64        try:
 65            timezone_data["timezonestr"] = response_json["timezoneId"]
 66
 67        except Exception as e:
 68            logging.error(f"Error serializing data maybe wrong username? Details: {e}")
 69            return {}
 70
 71        if hasattr(response, "from_cache"):
 72            timezone_data["from_tz_cache"] = response.from_cache  # type: ignore
 73
 74        return timezone_data
 75
 76    def __get_contry_data(self, city_name: str, country_code: str) -> dict[str, str]:
 77        """
 78        Get the city data *whitout timezone* for a given city and country name
 79        """
 80        # Dictionary that will be returned:
 81        city_data_whitout_tz = {}
 82
 83        params = {
 84            "q": city_name,
 85            "country": country_code,
 86            "username": self.username,
 87            "maxRows": 1,
 88            "style": "SHORT",
 89            "featureClass": ["A", "P"],
 90        }
 91
 92        prepared_request = Request("GET", self.base_url, params=params).prepare()
 93        logging.debug(f"Requesting data from geonames basic: {prepared_request.url}")
 94
 95        try:
 96            response = self.session.send(prepared_request)
 97            response_json = response.json()
 98            logging.debug(f"Response from GeoNames: {response_json}")
 99
100        except Exception as e:
101            logging.error(f"Error in fetching {self.base_url}: {e}")
102            return {}
103
104        try:
105            city_data_whitout_tz["name"] = response_json["geonames"][0]["name"]
106            city_data_whitout_tz["lat"] = response_json["geonames"][0]["lat"]
107            city_data_whitout_tz["lng"] = response_json["geonames"][0]["lng"]
108            city_data_whitout_tz["countryCode"] = response_json["geonames"][0]["countryCode"]
109
110        except Exception as e:
111            logging.error(f"Error serializing data maybe wrong username? Details: {e}")
112            return {}
113
114        if hasattr(response, "from_cache"):
115            city_data_whitout_tz["from_country_cache"] = response.from_cache  # type: ignore
116
117        return city_data_whitout_tz
118
119    def get_serialized_data(self) -> dict[str, str]:
120        """
121        Returns all the data necessary for the Kerykeion calculation.
122
123        Returns:
124            dict[str, str]: _description_
125        """
126        city_data_response = self.__get_contry_data(self.city_name, self.country_code)
127        try:
128            timezone_response = self.__get_timezone(city_data_response["lat"], city_data_response["lng"])
129
130        except Exception as e:
131            logging.error(f"Error in fetching timezone: {e}")
132            return {}
133
134        return {**timezone_response, **city_data_response}
135
136
137if __name__ == "__main__":
138    from kerykeion.utilities import setup_logging
139    setup_logging(level="debug")
140
141    geonames = FetchGeonames("Montichiari", "IT")
142    print(geonames.get_serialized_data())
class FetchGeonames:
 15class FetchGeonames:
 16    """
 17    Class to handle requests to the GeoNames API
 18
 19    Args:
 20    - city_name (str): Name of the city
 21    - country_code (str): Two letters country code
 22    - username (str, optional): GeoNames username, defaults to "century.boy".
 23    - cache_expire_after_days (int, optional): Cache expiration time in days, defaults to 30.
 24    """
 25
 26    def __init__(
 27        self,
 28        city_name: str,
 29        country_code: str,
 30        username: str = "century.boy",
 31        cache_expire_after_days=30,
 32    ):
 33        self.session = CachedSession(
 34            cache_name="cache/kerykeion_geonames_cache",
 35            backend="sqlite",
 36            expire_after=timedelta(days=cache_expire_after_days),
 37        )
 38
 39        self.username = username
 40        self.city_name = city_name
 41        self.country_code = country_code
 42        self.base_url = "http://api.geonames.org/searchJSON"
 43        self.timezone_url = "http://api.geonames.org/timezoneJSON"
 44
 45    def __get_timezone(self, lat: Union[str, float, int], lon: Union[str, float, int]) -> dict[str, str]:
 46        """
 47        Get the timezone for a given latitude and longitude
 48        """
 49        # Dictionary that will be returned:
 50        timezone_data = {}
 51
 52        params = {"lat": lat, "lng": lon, "username": self.username}
 53
 54        prepared_request = Request("GET", self.timezone_url, params=params).prepare()
 55        logging.debug(f"Requesting data from GeoName timezones: {prepared_request.url}")
 56
 57        try:
 58            response = self.session.send(prepared_request)
 59            response_json = response.json()
 60
 61        except Exception as e:
 62            logging.error(f"Error fetching {self.timezone_url}: {e}")
 63            return {}
 64
 65        try:
 66            timezone_data["timezonestr"] = response_json["timezoneId"]
 67
 68        except Exception as e:
 69            logging.error(f"Error serializing data maybe wrong username? Details: {e}")
 70            return {}
 71
 72        if hasattr(response, "from_cache"):
 73            timezone_data["from_tz_cache"] = response.from_cache  # type: ignore
 74
 75        return timezone_data
 76
 77    def __get_contry_data(self, city_name: str, country_code: str) -> dict[str, str]:
 78        """
 79        Get the city data *whitout timezone* for a given city and country name
 80        """
 81        # Dictionary that will be returned:
 82        city_data_whitout_tz = {}
 83
 84        params = {
 85            "q": city_name,
 86            "country": country_code,
 87            "username": self.username,
 88            "maxRows": 1,
 89            "style": "SHORT",
 90            "featureClass": ["A", "P"],
 91        }
 92
 93        prepared_request = Request("GET", self.base_url, params=params).prepare()
 94        logging.debug(f"Requesting data from geonames basic: {prepared_request.url}")
 95
 96        try:
 97            response = self.session.send(prepared_request)
 98            response_json = response.json()
 99            logging.debug(f"Response from GeoNames: {response_json}")
100
101        except Exception as e:
102            logging.error(f"Error in fetching {self.base_url}: {e}")
103            return {}
104
105        try:
106            city_data_whitout_tz["name"] = response_json["geonames"][0]["name"]
107            city_data_whitout_tz["lat"] = response_json["geonames"][0]["lat"]
108            city_data_whitout_tz["lng"] = response_json["geonames"][0]["lng"]
109            city_data_whitout_tz["countryCode"] = response_json["geonames"][0]["countryCode"]
110
111        except Exception as e:
112            logging.error(f"Error serializing data maybe wrong username? Details: {e}")
113            return {}
114
115        if hasattr(response, "from_cache"):
116            city_data_whitout_tz["from_country_cache"] = response.from_cache  # type: ignore
117
118        return city_data_whitout_tz
119
120    def get_serialized_data(self) -> dict[str, str]:
121        """
122        Returns all the data necessary for the Kerykeion calculation.
123
124        Returns:
125            dict[str, str]: _description_
126        """
127        city_data_response = self.__get_contry_data(self.city_name, self.country_code)
128        try:
129            timezone_response = self.__get_timezone(city_data_response["lat"], city_data_response["lng"])
130
131        except Exception as e:
132            logging.error(f"Error in fetching timezone: {e}")
133            return {}
134
135        return {**timezone_response, **city_data_response}

Class to handle requests to the GeoNames API

Args:

  • city_name (str): Name of the city
  • country_code (str): Two letters country code
  • username (str, optional): GeoNames username, defaults to "century.boy".
  • cache_expire_after_days (int, optional): Cache expiration time in days, defaults to 30.
FetchGeonames( city_name: str, country_code: str, username: str = 'century.boy', cache_expire_after_days=30)
26    def __init__(
27        self,
28        city_name: str,
29        country_code: str,
30        username: str = "century.boy",
31        cache_expire_after_days=30,
32    ):
33        self.session = CachedSession(
34            cache_name="cache/kerykeion_geonames_cache",
35            backend="sqlite",
36            expire_after=timedelta(days=cache_expire_after_days),
37        )
38
39        self.username = username
40        self.city_name = city_name
41        self.country_code = country_code
42        self.base_url = "http://api.geonames.org/searchJSON"
43        self.timezone_url = "http://api.geonames.org/timezoneJSON"
session
username
city_name
country_code
base_url
timezone_url
def get_serialized_data(self) -> dict[str, str]:
120    def get_serialized_data(self) -> dict[str, str]:
121        """
122        Returns all the data necessary for the Kerykeion calculation.
123
124        Returns:
125            dict[str, str]: _description_
126        """
127        city_data_response = self.__get_contry_data(self.city_name, self.country_code)
128        try:
129            timezone_response = self.__get_timezone(city_data_response["lat"], city_data_response["lng"])
130
131        except Exception as e:
132            logging.error(f"Error in fetching timezone: {e}")
133            return {}
134
135        return {**timezone_response, **city_data_response}

Returns all the data necessary for the Kerykeion calculation.

Returns: dict[str, str]: _description_