hdx-python-country 3.5.4__tar.gz → 3.5.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/PKG-INFO +2 -2
  2. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/documentation/main.md +20 -5
  3. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/pyproject.toml +1 -1
  4. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/requirements.txt +24 -30
  5. hdx_python_country-3.5.6/src/hdx/location/_version.py +6 -0
  6. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/src/hdx/location/adminlevel.py +117 -25
  7. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/src/hdx/location/country.py +6 -6
  8. hdx_python_country-3.5.6/tests/fixtures/global_pcodes_adm_1_2.csv +34586 -0
  9. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/tests/hdx/location/test_adminlevel.py +120 -0
  10. hdx_python_country-3.5.4/src/hdx/location/_version.py +0 -4
  11. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/.config/black.toml +0 -0
  12. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/.config/coveragerc +0 -0
  13. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/.config/pre-commit-config.yaml +0 -0
  14. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/.config/pytest.ini +0 -0
  15. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/.config/ruff.toml +0 -0
  16. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/.github/workflows/publish.yaml +0 -0
  17. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/.github/workflows/run-python-tests.yaml +0 -0
  18. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/.gitignore +0 -0
  19. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/CONTRIBUTING.md +0 -0
  20. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/LICENSE +0 -0
  21. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/README.md +0 -0
  22. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/documentation/.readthedocs.yaml +0 -0
  23. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/documentation/pydoc-markdown.yaml +0 -0
  24. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/src/hdx/location/Countries & Territories Taxonomy MVP - C&T Taxonomy with HXL Tags.csv +0 -0
  25. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/src/hdx/location/__init__.py +0 -0
  26. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/src/hdx/location/currency.py +0 -0
  27. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/src/hdx/location/names.py +0 -0
  28. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/src/hdx/location/phonetics.py +0 -0
  29. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/tests/fixtures/adminlevel.yaml +0 -0
  30. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/tests/fixtures/secondary_historic_rates.csv +0 -0
  31. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/tests/fixtures/secondary_rates.json +0 -0
  32. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/tests/hdx/location/Countries_UZB_Deleted.csv +0 -0
  33. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/tests/hdx/location/__init__.py +0 -0
  34. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/tests/hdx/location/test_country.py +0 -0
  35. {hdx_python_country-3.5.4 → hdx_python_country-3.5.6}/tests/hdx/location/test_currency.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hdx-python-country
3
- Version: 3.5.4
3
+ Version: 3.5.6
4
4
  Summary: HDX Python country code and exchange rate (fx) utilities
5
5
  Project-URL: Homepage, https://github.com/OCHA-DAP/hdx-python-country
6
6
  Author-email: Michael Rans <rans@email.com>
@@ -26,7 +26,7 @@ Classifier: Programming Language :: Python :: 3.12
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
27
  Requires-Python: >=3.8
28
28
  Requires-Dist: hdx-python-utilities>=3.6.2
29
- Requires-Dist: libhxl
29
+ Requires-Dist: libhxl>=5.0.3
30
30
  Requires-Dist: pyphonetics
31
31
  Provides-Extra: dev
32
32
  Requires-Dist: pre-commit; extra == 'dev'
@@ -42,6 +42,9 @@ The code for the library is [here](https://github.com/OCHA-DAP/hdx-python-countr
42
42
  The library has detailed API documentation which can be found in the menu at the top.
43
43
 
44
44
  ## Breaking Changes
45
+ From 3.5.5, after creating an Adminlevel, call either setup_from_admin_info,
46
+ setup_from_libhxl_dataset or setup_from_url.
47
+
45
48
  From 3.4.6, Python 3.7 no longer supported
46
49
 
47
50
  From 3.3.2, major update to foreign exchange code and use of new Yahoo data source
@@ -105,21 +108,33 @@ The usage of the country mappings functionality is best illustrated by some exam
105
108
 
106
109
  ## Administration Level
107
110
 
108
- The administration level mappings requires using an input configuration dictionary,
109
- admin_config, with key *admin_info* which is a list with values of the form:
110
-
111
- {"iso3": "AFG", "pcode": "AF01", "name": "Kabul"}
111
+ The administration level mappings takes input configuration dictionary,
112
+ *admin_config*, which defaults to an empty dictionary.
112
113
 
113
- Various other keys are optional:
114
+ *admin_config* can have the following optional keys:
114
115
 
115
116
  *countries_fuzzy_try* are countries (iso3 codes) for which to try fuzzy matching. Default is all countries.
116
117
  *admin_name_mappings* is a dictionary of mappings from name to pcode (for where fuzzy matching fails)
117
118
  *admin_name_replacements* is a dictionary of textual replacements to try when fuzzy matching
118
119
  *admin_fuzzy_dont* is a list of names for which fuzzy matching should not be tried
119
120
 
121
+ Once an AdminLevel object is constructed, one of three setup methods must be
122
+ called: *setup_from_admin_info*, *setup_from_libhxl_dataset* or
123
+ *setup_from_url*.
124
+
125
+ Method *setup_from_admin_info* takes key *admin_info* which is a list with
126
+ values of the form:
127
+
128
+ {"iso3": "AFG", "pcode": "AF01", "name": "Kabul"}
129
+
130
+ Method *setup_from_libhxl_dataset* takes a libhxl Dataset object, while
131
+ *setup_from_url* takes a URL which defaults to the global p-codes dataset on
132
+ HDX.
133
+
120
134
  Examples of usage:
121
135
 
122
136
  adminlevel = AdminLevel(config)
137
+ adminlevel.setup_from_admin_info(admin_info)
123
138
  adminlevel.get_pcode("YEM", "YEM030", logname="test") # returns ("YE30", True)
124
139
  adminlevel.get_pcode("YEM", "Al Dhale"e / الضالع") # returns ("YE30", False)
125
140
  adminlevel.get_pcode("YEM", "Al Dhale"e / الضالع", fuzzy_match=False) # returns (None, True)
@@ -35,7 +35,7 @@ requires-python = ">=3.8"
35
35
 
36
36
  dependencies = [
37
37
  "hdx-python-utilities>=3.6.2",
38
- "libhxl",
38
+ "libhxl>=5.0.3",
39
39
  "pyphonetics",
40
40
  ]
41
41
  dynamic = ["version"]
@@ -13,35 +13,31 @@ attrs==23.1.0
13
13
  # jsonschema
14
14
  certifi==2023.7.22
15
15
  # via requests
16
- cfgv==3.3.1
16
+ cfgv==3.4.0
17
17
  # via pre-commit
18
18
  chardet==5.2.0
19
19
  # via frictionless
20
20
  charset-normalizer==3.2.0
21
21
  # via requests
22
- click==8.1.6
22
+ click==8.1.7
23
23
  # via typer
24
24
  colorama==0.4.6
25
25
  # via typer
26
- coverage[toml]==7.2.7
26
+ coverage[toml]==7.3.1
27
27
  # via pytest-cov
28
- decorator==5.1.1
29
- # via
30
- # jsonpath-ng
31
- # validators
32
28
  distlib==0.3.7
33
29
  # via virtualenv
34
30
  et-xmlfile==1.1.0
35
31
  # via openpyxl
36
- filelock==3.12.2
32
+ filelock==3.12.4
37
33
  # via virtualenv
38
34
  frictionless==5.15.10
39
35
  # via hdx-python-utilities
40
36
  hdx-python-utilities==3.6.2
41
37
  # via hdx-python-country (pyproject.toml)
42
- humanize==4.7.0
38
+ humanize==4.8.0
43
39
  # via frictionless
44
- identify==2.5.26
40
+ identify==2.5.29
45
41
  # via pre-commit
46
42
  idna==3.4
47
43
  # via requests
@@ -53,17 +49,17 @@ isodate==0.6.1
53
49
  # via frictionless
54
50
  jinja2==3.1.2
55
51
  # via frictionless
56
- jsonlines==3.1.0
52
+ jsonlines==4.0.0
57
53
  # via hdx-python-utilities
58
- jsonpath-ng==1.5.3
54
+ jsonpath-ng==1.6.0
59
55
  # via libhxl
60
56
  jsonschema==4.17.3
61
57
  # via
62
58
  # frictionless
63
59
  # tableschema-to-template
64
- libhxl==5.0.2
60
+ libhxl==5.0.3
65
61
  # via hdx-python-country (pyproject.toml)
66
- loguru==0.7.0
62
+ loguru==0.7.2
67
63
  # via hdx-python-utilities
68
64
  markdown-it-py==3.0.0
69
65
  # via rich
@@ -79,21 +75,21 @@ openpyxl==3.1.2
79
75
  # via hdx-python-utilities
80
76
  packaging==23.1
81
77
  # via pytest
82
- petl==1.7.12
78
+ petl==1.7.14
83
79
  # via frictionless
84
80
  platformdirs==3.10.0
85
81
  # via virtualenv
86
- pluggy==1.2.0
82
+ pluggy==1.3.0
87
83
  # via pytest
88
84
  ply==3.11
89
85
  # via
90
86
  # jsonpath-ng
91
87
  # libhxl
92
- pre-commit==3.3.3
88
+ pre-commit==3.4.0
93
89
  # via hdx-python-country (pyproject.toml)
94
- pydantic==2.1.1
90
+ pydantic==2.3.0
95
91
  # via frictionless
96
- pydantic-core==2.4.0
92
+ pydantic-core==2.6.3
97
93
  # via pydantic
98
94
  pygments==2.16.1
99
95
  # via rich
@@ -101,10 +97,9 @@ pyphonetics==0.5.3
101
97
  # via hdx-python-country (pyproject.toml)
102
98
  pyrsistent==0.19.3
103
99
  # via jsonschema
104
- pytest==7.4.0
100
+ pytest==7.4.2
105
101
  # via
106
102
  # hdx-python-country (pyproject.toml)
107
- # libhxl
108
103
  # pytest-cov
109
104
  pytest-cov==4.1.0
110
105
  # via hdx-python-country (pyproject.toml)
@@ -133,20 +128,19 @@ requests-file==1.5.1
133
128
  # via hdx-python-utilities
134
129
  rfc3986==2.0.0
135
130
  # via frictionless
136
- rich==13.5.2
131
+ rich==13.5.3
137
132
  # via typer
138
133
  ruamel-yaml==0.17.32
139
134
  # via hdx-python-utilities
140
135
  ruamel-yaml-clib==0.2.7
141
136
  # via ruamel-yaml
142
- shellingham==1.5.0.post1
137
+ shellingham==1.5.3
143
138
  # via typer
144
139
  simpleeval==0.9.13
145
140
  # via frictionless
146
141
  six==1.16.0
147
142
  # via
148
143
  # isodate
149
- # jsonpath-ng
150
144
  # python-dateutil
151
145
  # requests-file
152
146
  stringcase==1.2.0
@@ -161,7 +155,7 @@ text-unidecode==1.3
161
155
  # via python-slugify
162
156
  typer[all]==0.9.0
163
157
  # via frictionless
164
- typing-extensions==4.7.1
158
+ typing-extensions==4.8.0
165
159
  # via
166
160
  # frictionless
167
161
  # pydantic
@@ -171,21 +165,21 @@ unidecode==1.3.6
171
165
  # via
172
166
  # libhxl
173
167
  # pyphonetics
174
- urllib3==2.0.4
168
+ urllib3==2.0.5
175
169
  # via
176
170
  # libhxl
177
171
  # requests
178
- validators==0.20.0
172
+ validators==0.22.0
179
173
  # via frictionless
180
- virtualenv==20.24.2
174
+ virtualenv==20.24.5
181
175
  # via pre-commit
182
- wheel==0.41.1
176
+ wheel==0.41.2
183
177
  # via libhxl
184
178
  xlrd==2.0.1
185
179
  # via hdx-python-utilities
186
180
  xlrd3==1.1.0
187
181
  # via libhxl
188
- xlsxwriter==3.1.2
182
+ xlsxwriter==3.1.4
189
183
  # via tableschema-to-template
190
184
  xlwt==1.3.0
191
185
  # via hdx-python-utilities
@@ -0,0 +1,6 @@
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __version__ = version = '3.5.6' # type: str
6
+ __version_tuple__ = version_tuple = (3, 5, 6) # type: tuple[int | str, ...]
@@ -1,57 +1,113 @@
1
1
  import logging
2
2
  from typing import Dict, List, Optional, Tuple
3
3
 
4
+ import hxl
5
+ from hxl import InputOptions
6
+ from hxl.input import HXLIOException
4
7
  from unidecode import unidecode
5
8
 
6
9
  from hdx.location.country import Country
7
10
  from hdx.location.names import clean_name
8
11
  from hdx.location.phonetics import Phonetics
9
12
  from hdx.utilities.text import multiple_replace
13
+ from hdx.utilities.typehint import ListTuple
10
14
 
11
15
  logger = logging.getLogger(__name__)
12
16
 
13
17
 
14
18
  class AdminLevel:
15
- """AdminLevel class which takes in pcodes and then maps names to those pcodes with fuzzy matching if necessary. The
16
- input configuration dictionary, admin_config, requires key admin_info which is a list with values of the form:
17
- ::
18
- {"iso3": "AFG", "pcode": "AF01", "name": "Kabul"}
19
-
20
- Various other keys are optional:
21
- countries_fuzzy_try are countries (iso3 codes) for which to try fuzzy matching. Default is all countries.
22
- admin_name_mappings is a dictionary of mappings from name to pcode (for where fuzzy matching fails)
23
- admin_name_replacements is a dictionary of textual replacements to try when fuzzy matching
24
- admin_fuzzy_dont is a list of names for which fuzzy matching should not be tried
19
+ """AdminLevel class which takes in p-codes and then maps names to those
20
+ p-codes with fuzzy matching if necessary.
21
+
22
+ The dictionary admin_config, which defaults to an empty dictionary, can
23
+ have the following optional keys:
24
+ countries_fuzzy_try are countries (iso3 codes) for which to try fuzzy
25
+ matching. Default is all countries.
26
+ admin_name_mappings is a dictionary of mappings from name to p-code (for
27
+ where fuzzy matching fails)
28
+ admin_name_replacements is a dictionary of textual replacements to try when
29
+ fuzzy matching
30
+ admin_fuzzy_dont is a list of names for which fuzzy matching should not be
31
+ tried
25
32
 
26
33
  Args:
27
- admin_config (Dict): Configuration dictionary
34
+ admin_config (Dict): Configuration dictionary. Defaults to {}.
28
35
  admin_level (int): Admin level. Defaults to 1.
29
36
  admin_level_overrides (Dict): Countries at other admin levels.
30
37
  """
31
38
 
39
+ _admin_url_default = "https://data.humdata.org/dataset/cb963915-d7d1-4ffa-90dc-31277e24406f/resource/f65bc260-4d8b-416f-ac07-f2433b4d5142/download/global_pcodes_adm_1_2.csv"
40
+ _admin_url = _admin_url_default
41
+
32
42
  def __init__(
33
43
  self,
34
- admin_config: Dict,
44
+ admin_config: Dict = {},
35
45
  admin_level: int = 1,
36
- admin_level_overrides: Dict = dict(),
46
+ admin_level_overrides: Dict = {},
37
47
  ) -> None:
38
- admin_info = admin_config["admin_info"]
39
48
  self.admin_level = admin_level
40
49
  self.admin_level_overrides = admin_level_overrides
41
50
  self.countries_fuzzy_try = admin_config.get("countries_fuzzy_try")
42
- self.admin_name_mappings = admin_config.get(
43
- "admin_name_mappings", dict()
44
- )
51
+ self.admin_name_mappings = admin_config.get("admin_name_mappings", {})
45
52
  self.admin_name_replacements = admin_config.get(
46
- "admin_name_replacements", dict()
53
+ "admin_name_replacements", {}
47
54
  )
48
55
  self.admin_fuzzy_dont = admin_config.get("admin_fuzzy_dont", list())
49
56
  self.pcodes = list()
50
- self.pcode_lengths = dict()
51
- self.name_to_pcode = dict()
52
- self.pcode_to_name = dict()
53
- self.pcode_to_iso3 = dict()
57
+ self.pcode_lengths = {}
58
+ self.name_to_pcode = {}
59
+ self.pcode_to_name = {}
60
+ self.pcode_to_iso3 = {}
61
+
62
+ self.init_matches_errors()
63
+ self.phonetics = Phonetics()
64
+
65
+ @classmethod
66
+ def set_default_admin_url(cls, admin_url: Optional[str] = None) -> None:
67
+ """
68
+ Set default admin URL from which to retrieve admin data
69
+
70
+ Args:
71
+ admin_url (Optional[str]): Admin URL from which to retrieve admin data. Defaults to internal value.
72
+
73
+ Returns:
74
+ None
75
+ """
76
+ if admin_url is None:
77
+ admin_url = cls._admin_url_default
78
+ cls._admin_url = admin_url
54
79
 
80
+ @classmethod
81
+ def get_libhxl_dataset(cls, admin_url: str = _admin_url) -> hxl.Dataset:
82
+ """
83
+ Get libhxl Dataset object given a URL which defaults to global p-codes
84
+ dataset on HDX.
85
+
86
+ Args:
87
+ admin_url (str): URL from which to load data. Defaults to global p-codes dataset.
88
+
89
+ Returns:
90
+ None
91
+ """
92
+ try:
93
+ return hxl.data(admin_url, InputOptions(encoding="utf-8"))
94
+ except HXLIOException:
95
+ logger.exception(
96
+ f"Setup of libhxl Dataset object with {admin_url} failed!"
97
+ )
98
+ raise
99
+
100
+ def setup_from_admin_info(self, admin_info: ListTuple[Dict]) -> None:
101
+ """
102
+ Setup p-codes from admin_info which is a list with values of the form:
103
+ ::
104
+ {"iso3": "AFG", "pcode": "AF01", "name": "Kabul"}
105
+ Args:
106
+ admin_info (ListTuple[Dict]): p-code dictionary
107
+
108
+ Returns:
109
+ None
110
+ """
55
111
  for row in admin_info:
56
112
  countryiso3 = row["iso3"]
57
113
  pcode = row.get("pcode")
@@ -59,12 +115,48 @@ class AdminLevel:
59
115
  self.pcode_lengths[countryiso3] = len(pcode)
60
116
  adm_name = row["name"]
61
117
  self.pcode_to_name[pcode] = adm_name
62
- name_to_pcode = self.name_to_pcode.get(countryiso3, dict())
118
+ name_to_pcode = self.name_to_pcode.get(countryiso3, {})
63
119
  name_to_pcode[unidecode(adm_name).lower()] = pcode
64
120
  self.name_to_pcode[countryiso3] = name_to_pcode
65
121
  self.pcode_to_iso3[pcode] = countryiso3
66
- self.init_matches_errors()
67
- self.phonetics = Phonetics()
122
+
123
+ def setup_from_libhxl_dataset(self, libhxl_dataset: hxl.Dataset) -> None:
124
+ """
125
+ Setup p-codes from a libhxl Dataset object.
126
+
127
+ Args:
128
+ libhxl_dataset (hxl.Dataset): Dataset object from libhxl library
129
+
130
+ Returns:
131
+ None
132
+ """
133
+ admin_info = libhxl_dataset.with_rows(
134
+ f"#geo+admin_level={self.admin_level}"
135
+ )
136
+ for row in admin_info:
137
+ countryiso3 = row.get("#country+code")
138
+ pcode = row.get("#adm+code")
139
+ self.pcodes.append(pcode)
140
+ self.pcode_lengths[countryiso3] = len(pcode)
141
+ adm_name = row.get("#adm+name")
142
+ self.pcode_to_name[pcode] = adm_name
143
+ name_to_pcode = self.name_to_pcode.get(countryiso3, {})
144
+ name_to_pcode[unidecode(adm_name).lower()] = pcode
145
+ self.name_to_pcode[countryiso3] = name_to_pcode
146
+ self.pcode_to_iso3[pcode] = countryiso3
147
+
148
+ def setup_from_url(self, admin_url: str = _admin_url) -> None:
149
+ """
150
+ Setup p-codes from a URL. Defaults to global p-codes dataset on HDX.
151
+
152
+ Args:
153
+ admin_url (str): URL from which to load data. Defaults to global p-codes dataset.
154
+
155
+ Returns:
156
+ None
157
+ """
158
+ admin_info = self.get_libhxl_dataset(admin_url)
159
+ self.setup_from_libhxl_dataset(admin_info)
68
160
 
69
161
  def get_pcode_list(self) -> List[str]:
70
162
  """Get list of all pcodes
@@ -69,8 +69,8 @@ class Country:
69
69
  "PROTECTORATE",
70
70
  ]
71
71
  _countriesdata = None
72
- _ochaurl_int = "https://docs.google.com/spreadsheets/d/1NjSI2LaS3SqbgYc0HdD8oIb7lofGtiHgoKKATCpwVdY/export?format=csv&gid=1088874596"
73
- _ochaurl = _ochaurl_int
72
+ _ochaurl_default = "https://docs.google.com/spreadsheets/d/1NjSI2LaS3SqbgYc0HdD8oIb7lofGtiHgoKKATCpwVdY/export?format=csv&gid=1088874596"
73
+ _ochaurl = _ochaurl_default
74
74
  _country_name_overrides = dict()
75
75
  _country_name_mappings = dict()
76
76
 
@@ -246,18 +246,18 @@ class Country:
246
246
  return cls._countriesdata
247
247
 
248
248
  @classmethod
249
- def set_ocha_url(cls, url: str = None) -> None:
249
+ def set_ocha_url(cls, url: Optional[str] = None) -> None:
250
250
  """
251
- Set World Bank url from which to retrieve countries data
251
+ Set OCHA url from which to retrieve countries data
252
252
 
253
253
  Args:
254
- url (str): World Bank url from which to retrieve countries data. Defaults to internal value.
254
+ url (Optional[str]): OCHA url from which to retrieve countries data. Defaults to internal value.
255
255
 
256
256
  Returns:
257
257
  None
258
258
  """
259
259
  if url is None:
260
- url = cls._ochaurl_int
260
+ url = cls._ochaurl_default
261
261
  cls._ochaurl = url
262
262
 
263
263
  @classmethod