# -*- coding: utf-8 -*-
"""Countrydata main interface module."""
import os
import json
from countrydata.data.consts import COUNTRYDATA_FILES_DIRECTORY
from countrydata.data.spec import (
disambiguate_field_name,
get_spec_field_by_value,
get_valid_field_names,
spec,
)
[docs]class Country:
"""Represents a country and exposes his data. Their initialization
is very flexible, allowing to construct it using any
field of the data that refers to a country. This possibilities
that any data field can be used as reference and this class
acts as a country data factory.
Can be initialized explicitly by optional arguments (safer)
or implicitly, delegating to the library the inference process
of discover what field of the data you are using.
Implicitly, the class can be initalized defining a country value
of any type of data as first positional argument. For example,
let's try to initialize the United States using ISO-3361-1
Alpha2 codes implicitly:
>>> country = Country("US")
>>> country.a3
'USA'
>>> country.num
840
Explicitly, the class can be initialized using one of the supported
`field names <https://countrydata.readthedocs.io/en/latest/data/overview.html#supported-fields>`_.
This way is faster and safer. United States can be initialized
by his ISO-3361-1 Numeric3 number:
>>> country = Country(num=840)
>>> country.a3
'USA'
"""
def __init__(self, *args, **kwargs):
self.data = None
self._properties = None
if len(args) > 0:
if len(args) > 1:
msg = "You must pass only one country initializator value"
raise ValueError(msg)
spec_field = get_spec_field_by_value(args[0])
if spec_field is None:
raise ValueError("%s value not found in data" % args[0])
# Get the country file path searching by field path
# and initialize the interface by filepath
self.__init_by_filepath__(
spec_field.filepath(args[0]))
else:
if len(kwargs) > 0:
if len(kwargs) > 1:
raise ValueError(
"You must pass only one country initializator value")
# Dissambiguate field name
_field_name = list(kwargs.keys())[0]
field_name = disambiguate_field_name(_field_name)
# If is not a valid field name
if field_name is None:
valid_field_names = get_valid_field_names()
valid_field_names_msg = ""
for valid_field_name in valid_field_names:
valid_field_names_msg += " - %s\n" % valid_field_name
msg_schema = "You must pass a valid field to initialize" \
+ " %s interface. Valid field names:\n%s"
msg = msg_schema % (self.__class.__name__, valid_field_names_msg)
raise ValueError(msg)
# Get value
field_value = kwargs[_field_name]
# Check by what field we must initialize the interface
for spec_field in spec:
if field_name in spec_field.ids:
# Get the country file path searching by field path
# and initialize the interface by filepath
self.__init_by_filepath__(
spec_field.filepath(field_value))
break
else:
msg = "You must pass a country initializator value."
raise ValueError(msg)
def __init_by_filepath__(self, filepath):
with open(filepath, "r") as f:
self.data = json.loads(f.read(), parse_int=int)
def __getattr__(self, attr):
return self._get_data_properties()[attr]
def __dir__(self):
return super().__dir__() + list(self._get_data_properties().keys())
def _get_data_properties(self):
if self._properties is not None:
return self._properties
self._properties = {}
# For each specification field
for spec_field in spec:
property_id = spec_field.ids[0]
# Traverse 'field_path' keys to access the data
_keys = spec_field.field_path.split(":")[1].split(".")
value = None
_first_temp_value = True
for k in _keys:
if _first_temp_value:
value = self.data[k]
self._properties[k] = value
_first_temp_value = False
else:
value = value[k]
self._properties[property_id] = value
return self._properties
class CountryName:
"""Represent a localized country name.
:param locale: Locale for the country name:
:type locale: str
:param official: Official name.
:type official: str
:param short: Short name.
:type short: str
:param alt: Alternative names.
:type alt: list
:param regex: Regex that match with all names
used for this country.
:type regex: str
"""
def __init__(self,
locale="en",
official=None,
short=None,
alt=[],
regex=None):
self.locale = locale
self.official = official
self.short = short
self.alt = alt
self.regex = regex