hdx-python-country 3.6.9__tar.gz → 3.7.0__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.
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/.github/workflows/publish.yaml +2 -2
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/.github/workflows/run-python-tests.yaml +2 -2
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/CONTRIBUTING.md +3 -4
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/PKG-INFO +4 -4
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/documentation/main.md +8 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/pyproject.toml +2 -2
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/requirements.txt +7 -7
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/_version.py +2 -2
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/adminlevel.py +106 -35
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/fixtures/adminlevel.yaml +5 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/hdx/location/test_adminlevel.py +90 -1
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/.config/coveragerc +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/.config/pre-commit-config.yaml +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/.config/pytest.ini +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/.config/ruff.toml +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/.gitignore +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/LICENSE +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/README.md +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/documentation/.readthedocs.yaml +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/documentation/pydoc-markdown.yaml +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/Countries & Territories Taxonomy MVP - C&T Taxonomy with HXL Tags.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/__init__.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/country.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/currency.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/names.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/phonetics.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/src/hdx/location/wfp_exchangerates.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/fixtures/global_pcode_lengths.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/fixtures/global_pcodes_adm_1_2.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/fixtures/secondary_historic_rates.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/fixtures/secondary_rates.json +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/hdx/location/Countries_UZB_Deleted.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/hdx/location/__init__.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/hdx/location/test_country.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/hdx/location/test_currency.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/hdx/location/test_wfp_exchangerates.py +0 -0
|
@@ -9,13 +9,13 @@ jobs:
|
|
|
9
9
|
runs-on: ubuntu-latest
|
|
10
10
|
|
|
11
11
|
steps:
|
|
12
|
-
- uses: actions/checkout@
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
13
|
- name: Get history and tags for versioning to work
|
|
14
14
|
run: |
|
|
15
15
|
git fetch --prune --unshallow
|
|
16
16
|
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
|
17
17
|
- name: Set up Python
|
|
18
|
-
uses: actions/setup-python@
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
19
|
with:
|
|
20
20
|
python-version: '3.x'
|
|
21
21
|
- name: Install dependencies
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/.github/workflows/run-python-tests.yaml
RENAMED
|
@@ -18,9 +18,9 @@ jobs:
|
|
|
18
18
|
runs-on: ubuntu-latest
|
|
19
19
|
|
|
20
20
|
steps:
|
|
21
|
-
- uses: actions/checkout@
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
22
|
- name: Set up Python
|
|
23
|
-
uses: actions/setup-python@
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
24
|
with:
|
|
25
25
|
python-version: '3.x'
|
|
26
26
|
- name: Install dependencies
|
|
@@ -21,15 +21,14 @@ you make a git commit:
|
|
|
21
21
|
pre-commit install
|
|
22
22
|
|
|
23
23
|
The configuration file for this project is in a
|
|
24
|
-
non-
|
|
24
|
+
non-standard location. Thus, you will need to edit your
|
|
25
25
|
`.git/hooks/pre-commit` file to reflect this. Change
|
|
26
26
|
the line that begins with `ARGS` to:
|
|
27
27
|
|
|
28
28
|
ARGS=(hook-impl --config=.config/pre-commit-config.yaml --hook-type=pre-commit)
|
|
29
29
|
|
|
30
30
|
With pre-commit, all code is formatted according to
|
|
31
|
-
[
|
|
32
|
-
[ruff]("https://github.com/charliermarsh/ruff") guidelines.
|
|
31
|
+
[ruff](https://github.com/astral-sh/ruff) guidelines.
|
|
33
32
|
|
|
34
33
|
To check if your changes pass pre-commit without committing, run:
|
|
35
34
|
|
|
@@ -41,7 +40,7 @@ To run the tests and view coverage, execute:
|
|
|
41
40
|
|
|
42
41
|
pytest -c .config/pytest.ini --cov hdx --cov-config .config/coveragerc
|
|
43
42
|
|
|
44
|
-
Follow the example set out already in ``
|
|
43
|
+
Follow the example set out already in ``documentation/main.md`` as you write the documentation.
|
|
45
44
|
|
|
46
45
|
## Packages
|
|
47
46
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: hdx-python-country
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.7.0
|
|
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
|
-
Author
|
|
6
|
+
Author: Michael Rans
|
|
7
7
|
License: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Keywords: HDX,country,country code,currencies,currency,exchange rate,foreign exchange,fx,iso 3166,iso2,iso3,location,region
|
|
@@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
25
25
|
Classifier: Programming Language :: Python :: 3.12
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Requires-Python: >=3.8
|
|
28
|
-
Requires-Dist: hdx-python-utilities>=3.6.
|
|
28
|
+
Requires-Dist: hdx-python-utilities>=3.6.7
|
|
29
29
|
Requires-Dist: libhxl>=5.2.1
|
|
30
30
|
Requires-Dist: pyphonetics
|
|
31
31
|
Provides-Extra: dev
|
|
@@ -126,6 +126,13 @@ Method *setup_from_admin_info* takes key *admin_info* which is a list with
|
|
|
126
126
|
values of the form:
|
|
127
127
|
|
|
128
128
|
{"iso3": "AFG", "pcode": "AF01", "name": "Kabul"}
|
|
129
|
+
{"iso3": "AFG", "pcode": "AF0101", "name": "Kabul", "parent": "AF01"}
|
|
130
|
+
|
|
131
|
+
Dictionaries *pcode_to_name* and *pcode_to_iso3* are populated in the
|
|
132
|
+
AdminLevel object. *parent* is optional, but if provided enables lookup of
|
|
133
|
+
location names by both country and parent rather than just country which should
|
|
134
|
+
help with any name clashes. It also results in the population of a dictionary
|
|
135
|
+
in the AdminLevel object *pcode_to_parent*.
|
|
129
136
|
|
|
130
137
|
Method *setup_from_libhxl_dataset* takes a libhxl Dataset object, while
|
|
131
138
|
*setup_from_url* takes a URL which defaults to a resource in the global p-codes
|
|
@@ -144,6 +151,7 @@ Examples of usage:
|
|
|
144
151
|
adminlevel.get_pcode("YEM", "YEM030", logname="test") # returns ("YE30", True)
|
|
145
152
|
adminlevel.get_pcode("YEM", "Al Dhale"e / الضالع") # returns ("YE30", False)
|
|
146
153
|
adminlevel.get_pcode("YEM", "Al Dhale"e / الضالع", fuzzy_match=False) # returns (None, True)
|
|
154
|
+
assert admintwo.get_pcode("AFG", "Kabul", parent="AF01") == ("AF0101", True)
|
|
147
155
|
|
|
148
156
|
There is basic admin 1 p-code length conversion by default. A more advanced
|
|
149
157
|
p-code length conversion can be activated by calling *load_pcode_formats*
|
|
@@ -9,7 +9,7 @@ build-backend = "hatchling.build"
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "hdx-python-country"
|
|
11
11
|
description = "HDX Python country code and exchange rate (fx) utilities"
|
|
12
|
-
authors = [{name = "Michael Rans"
|
|
12
|
+
authors = [{name = "Michael Rans"}]
|
|
13
13
|
license = {text = "MIT"}
|
|
14
14
|
keywords = ["HDX", "location", "country", "country code", "iso 3166", "iso2", "iso3", "region", "fx", "currency", "currencies", "exchange rate", "foreign exchange"]
|
|
15
15
|
classifiers = [
|
|
@@ -34,7 +34,7 @@ classifiers = [
|
|
|
34
34
|
requires-python = ">=3.8"
|
|
35
35
|
|
|
36
36
|
dependencies = [
|
|
37
|
-
"hdx-python-utilities>=3.6.
|
|
37
|
+
"hdx-python-utilities>=3.6.7",
|
|
38
38
|
"libhxl>=5.2.1",
|
|
39
39
|
"pyphonetics",
|
|
40
40
|
]
|
|
@@ -23,17 +23,17 @@ click==8.1.7
|
|
|
23
23
|
# via typer
|
|
24
24
|
colorama==0.4.6
|
|
25
25
|
# via typer
|
|
26
|
-
coverage[toml]==7.4.
|
|
26
|
+
coverage[toml]==7.4.4
|
|
27
27
|
# via pytest-cov
|
|
28
28
|
distlib==0.3.8
|
|
29
29
|
# via virtualenv
|
|
30
30
|
et-xmlfile==1.1.0
|
|
31
31
|
# via openpyxl
|
|
32
|
-
filelock==3.13.
|
|
32
|
+
filelock==3.13.3
|
|
33
33
|
# via virtualenv
|
|
34
34
|
frictionless==5.16.1
|
|
35
35
|
# via hdx-python-utilities
|
|
36
|
-
hdx-python-utilities==3.6.
|
|
36
|
+
hdx-python-utilities==3.6.7
|
|
37
37
|
# via hdx-python-country (pyproject.toml)
|
|
38
38
|
humanize==4.9.0
|
|
39
39
|
# via frictionless
|
|
@@ -85,7 +85,7 @@ ply==3.11
|
|
|
85
85
|
# via
|
|
86
86
|
# jsonpath-ng
|
|
87
87
|
# libhxl
|
|
88
|
-
pre-commit==3.
|
|
88
|
+
pre-commit==3.7.0
|
|
89
89
|
# via hdx-python-country (pyproject.toml)
|
|
90
90
|
pydantic==2.6.4
|
|
91
91
|
# via frictionless
|
|
@@ -101,7 +101,7 @@ pytest==8.1.1
|
|
|
101
101
|
# via
|
|
102
102
|
# hdx-python-country (pyproject.toml)
|
|
103
103
|
# pytest-cov
|
|
104
|
-
pytest-cov==
|
|
104
|
+
pytest-cov==5.0.0
|
|
105
105
|
# via hdx-python-country (pyproject.toml)
|
|
106
106
|
python-dateutil==2.8.2
|
|
107
107
|
# via
|
|
@@ -152,7 +152,7 @@ tabulate==0.9.0
|
|
|
152
152
|
# via frictionless
|
|
153
153
|
text-unidecode==1.3
|
|
154
154
|
# via python-slugify
|
|
155
|
-
typer[all]==0.
|
|
155
|
+
typer[all]==0.10.0
|
|
156
156
|
# via frictionless
|
|
157
157
|
typing-extensions==4.10.0
|
|
158
158
|
# via
|
|
@@ -168,7 +168,7 @@ urllib3==2.2.1
|
|
|
168
168
|
# via
|
|
169
169
|
# libhxl
|
|
170
170
|
# requests
|
|
171
|
-
validators==0.
|
|
171
|
+
validators==0.24.0
|
|
172
172
|
# via frictionless
|
|
173
173
|
virtualenv==20.25.1
|
|
174
174
|
# via pre-commit
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import re
|
|
3
|
-
from typing import Dict, List, Optional, Tuple
|
|
3
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
4
4
|
|
|
5
5
|
import hxl
|
|
6
6
|
from hxl import InputOptions
|
|
@@ -61,9 +61,12 @@ class AdminLevel:
|
|
|
61
61
|
self.pcodes = []
|
|
62
62
|
self.pcode_lengths = {}
|
|
63
63
|
self.name_to_pcode = {}
|
|
64
|
+
self.name_parent_to_pcode = {}
|
|
64
65
|
self.pcode_to_name = {}
|
|
65
66
|
self.pcode_to_iso3 = {}
|
|
67
|
+
self.pcode_to_parent = {}
|
|
66
68
|
self.pcode_formats = {}
|
|
69
|
+
self.use_parent = False
|
|
67
70
|
self.zeroes = {}
|
|
68
71
|
self.parent_admins = []
|
|
69
72
|
|
|
@@ -120,15 +123,55 @@ class AdminLevel:
|
|
|
120
123
|
)
|
|
121
124
|
raise
|
|
122
125
|
|
|
126
|
+
def setup_row(
|
|
127
|
+
self,
|
|
128
|
+
countryiso3: str,
|
|
129
|
+
pcode: str,
|
|
130
|
+
adm_name: str,
|
|
131
|
+
parent: Optional[str],
|
|
132
|
+
):
|
|
133
|
+
"""
|
|
134
|
+
Setup a single p-code
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
countryiso3 (str): Country
|
|
138
|
+
pcode (str): P-code
|
|
139
|
+
adm_name (str): Administrative name
|
|
140
|
+
parent (Optional[str]): Parent p-code
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
None
|
|
144
|
+
"""
|
|
145
|
+
self.pcode_lengths[countryiso3] = len(pcode)
|
|
146
|
+
self.pcodes.append(pcode)
|
|
147
|
+
self.pcode_to_name[pcode] = adm_name
|
|
148
|
+
|
|
149
|
+
name_to_pcode = self.name_to_pcode.get(countryiso3, {})
|
|
150
|
+
name_to_pcode[unidecode(adm_name).lower()] = pcode
|
|
151
|
+
self.name_to_pcode[countryiso3] = name_to_pcode
|
|
152
|
+
self.pcode_to_iso3[pcode] = countryiso3
|
|
153
|
+
self.pcode_to_iso3[pcode] = countryiso3
|
|
154
|
+
|
|
155
|
+
if self.use_parent:
|
|
156
|
+
name_parent_to_pcode = self.name_parent_to_pcode.get(
|
|
157
|
+
countryiso3, {}
|
|
158
|
+
)
|
|
159
|
+
name_to_pcode = name_parent_to_pcode.get(parent, {})
|
|
160
|
+
name_to_pcode[unidecode(adm_name).lower()] = pcode
|
|
161
|
+
name_parent_to_pcode[parent] = name_to_pcode
|
|
162
|
+
self.name_parent_to_pcode[countryiso3] = name_parent_to_pcode
|
|
163
|
+
self.pcode_to_parent[pcode] = parent
|
|
164
|
+
|
|
123
165
|
def setup_from_admin_info(
|
|
124
166
|
self,
|
|
125
167
|
admin_info: ListTuple[Dict],
|
|
126
168
|
countryiso3s: Optional[ListTuple[str]] = None,
|
|
127
169
|
) -> None:
|
|
128
170
|
"""
|
|
129
|
-
Setup p-codes from admin_info which is a list with values of the form
|
|
171
|
+
Setup p-codes from admin_info which is a list with values of the form
|
|
172
|
+
below with parent optional:
|
|
130
173
|
::
|
|
131
|
-
{"iso3": "AFG", "pcode": "
|
|
174
|
+
{"iso3": "AFG", "pcode": "AF0101", "name": "Kabul", parent: "AF01"}
|
|
132
175
|
Args:
|
|
133
176
|
admin_info (ListTuple[Dict]): p-code dictionary
|
|
134
177
|
countryiso3s (Optional[ListTuple[str]]): Countries to read. Defaults to None (all).
|
|
@@ -140,19 +183,15 @@ class AdminLevel:
|
|
|
140
183
|
countryiso3s = [
|
|
141
184
|
countryiso3.upper() for countryiso3 in countryiso3s
|
|
142
185
|
]
|
|
186
|
+
self.use_parent = "parent" in admin_info[0]
|
|
143
187
|
for row in admin_info:
|
|
144
188
|
countryiso3 = row["iso3"].upper()
|
|
145
189
|
if countryiso3s and countryiso3 not in countryiso3s:
|
|
146
190
|
continue
|
|
147
191
|
pcode = row.get("pcode").upper()
|
|
148
|
-
self.pcodes.append(pcode)
|
|
149
|
-
self.pcode_lengths[countryiso3] = len(pcode)
|
|
150
192
|
adm_name = row["name"]
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
name_to_pcode[unidecode(adm_name).lower()] = pcode
|
|
154
|
-
self.name_to_pcode[countryiso3] = name_to_pcode
|
|
155
|
-
self.pcode_to_iso3[pcode] = countryiso3
|
|
193
|
+
parent = row.get("parent")
|
|
194
|
+
self.setup_row(countryiso3, pcode, adm_name, parent)
|
|
156
195
|
|
|
157
196
|
def setup_from_libhxl_dataset(
|
|
158
197
|
self,
|
|
@@ -176,19 +215,15 @@ class AdminLevel:
|
|
|
176
215
|
countryiso3s = [
|
|
177
216
|
countryiso3.upper() for countryiso3 in countryiso3s
|
|
178
217
|
]
|
|
218
|
+
self.use_parent = "#adm+code+parent" in admin_info.display_tags
|
|
179
219
|
for row in admin_info:
|
|
180
220
|
countryiso3 = row.get("#country+code").upper()
|
|
181
221
|
if countryiso3s and countryiso3 not in countryiso3s:
|
|
182
222
|
continue
|
|
183
223
|
pcode = row.get("#adm+code").upper()
|
|
184
|
-
self.pcodes.append(pcode)
|
|
185
|
-
self.pcode_lengths[countryiso3] = len(pcode)
|
|
186
224
|
adm_name = row.get("#adm+name")
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
name_to_pcode[unidecode(adm_name).lower()] = pcode
|
|
190
|
-
self.name_to_pcode[countryiso3] = name_to_pcode
|
|
191
|
-
self.pcode_to_iso3[pcode] = countryiso3
|
|
225
|
+
parent = row.get("#adm+code+parent")
|
|
226
|
+
self.setup_row(countryiso3, pcode, adm_name, parent)
|
|
192
227
|
|
|
193
228
|
def setup_from_url(
|
|
194
229
|
self,
|
|
@@ -304,7 +339,7 @@ class AdminLevel:
|
|
|
304
339
|
self.errors = set()
|
|
305
340
|
|
|
306
341
|
def convert_admin_pcode_length(
|
|
307
|
-
self, countryiso3: str, pcode: str,
|
|
342
|
+
self, countryiso3: str, pcode: str, **kwargs: Any
|
|
308
343
|
) -> Optional[str]:
|
|
309
344
|
"""Standardise pcode length by country and match to an internal pcode.
|
|
310
345
|
Requires that p-code formats be loaded (eg. using load_pcode_formats)
|
|
@@ -312,11 +347,14 @@ class AdminLevel:
|
|
|
312
347
|
Args:
|
|
313
348
|
countryiso3 (str): ISO3 country code
|
|
314
349
|
pcode (str): P code to match
|
|
315
|
-
|
|
350
|
+
**kwargs:
|
|
351
|
+
parent (Optional[str]): Parent admin code
|
|
352
|
+
logname (str): Log using this identifying name. Defaults to not logging.
|
|
316
353
|
|
|
317
354
|
Returns:
|
|
318
355
|
Optional[str]: Matched P code or None if no match
|
|
319
356
|
"""
|
|
357
|
+
logname = kwargs.get("logname")
|
|
320
358
|
match = self.pcode_regex.match(pcode)
|
|
321
359
|
if not match:
|
|
322
360
|
return None
|
|
@@ -480,18 +518,24 @@ class AdminLevel:
|
|
|
480
518
|
return None
|
|
481
519
|
|
|
482
520
|
def fuzzy_pcode(
|
|
483
|
-
self,
|
|
521
|
+
self,
|
|
522
|
+
countryiso3: str,
|
|
523
|
+
name: str,
|
|
524
|
+
**kwargs: Any,
|
|
484
525
|
) -> Optional[str]:
|
|
485
526
|
"""Fuzzy match name to pcode
|
|
486
527
|
|
|
487
528
|
Args:
|
|
488
529
|
countryiso3 (str): Iso3 country code
|
|
489
530
|
name (str): Name to match
|
|
490
|
-
|
|
531
|
+
**kwargs:
|
|
532
|
+
parent (Optional[str]): Parent admin code
|
|
533
|
+
logname (str): Log using this identifying name. Defaults to not logging.
|
|
491
534
|
|
|
492
535
|
Returns:
|
|
493
536
|
Optional[str]: Matched P code or None if no match
|
|
494
537
|
"""
|
|
538
|
+
logname = kwargs.get("logname")
|
|
495
539
|
if (
|
|
496
540
|
self.countries_fuzzy_try is not None
|
|
497
541
|
and countryiso3 not in self.countries_fuzzy_try
|
|
@@ -499,11 +543,24 @@ class AdminLevel:
|
|
|
499
543
|
if logname:
|
|
500
544
|
self.ignored.add((logname, countryiso3))
|
|
501
545
|
return None
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
546
|
+
if self.use_parent and "parent" in kwargs:
|
|
547
|
+
parent = kwargs["parent"]
|
|
548
|
+
name_parent_to_pcode = self.name_parent_to_pcode.get(countryiso3)
|
|
549
|
+
if not name_parent_to_pcode:
|
|
550
|
+
if logname:
|
|
551
|
+
self.errors.add((logname, countryiso3))
|
|
552
|
+
return None
|
|
553
|
+
name_to_pcode = name_parent_to_pcode.get(parent)
|
|
554
|
+
if not name_to_pcode:
|
|
555
|
+
if logname:
|
|
556
|
+
self.errors.add((logname, countryiso3, parent))
|
|
557
|
+
return None
|
|
558
|
+
else:
|
|
559
|
+
name_to_pcode = self.name_to_pcode.get(countryiso3)
|
|
560
|
+
if not name_to_pcode:
|
|
561
|
+
if logname:
|
|
562
|
+
self.errors.add((logname, countryiso3))
|
|
563
|
+
return None
|
|
507
564
|
adm_name_lookup = clean_name(name)
|
|
508
565
|
adm_name_lookup2 = multiple_replace(
|
|
509
566
|
adm_name_lookup, self.admin_name_replacements
|
|
@@ -591,7 +648,7 @@ class AdminLevel:
|
|
|
591
648
|
countryiso3: str,
|
|
592
649
|
name: str,
|
|
593
650
|
fuzzy_match: bool = True,
|
|
594
|
-
|
|
651
|
+
**kwargs: Any,
|
|
595
652
|
) -> Tuple[Optional[str], bool]:
|
|
596
653
|
"""Get pcode for a given name
|
|
597
654
|
|
|
@@ -599,7 +656,9 @@ class AdminLevel:
|
|
|
599
656
|
countryiso3 (str): Iso3 country code
|
|
600
657
|
name (str): Name to match
|
|
601
658
|
fuzzy_match (bool): Whether to try fuzzy matching. Defaults to True.
|
|
602
|
-
|
|
659
|
+
**kwargs:
|
|
660
|
+
parent (Optional[str]): Parent admin code
|
|
661
|
+
logname (str): Log using this identifying name. Defaults to not logging.
|
|
603
662
|
|
|
604
663
|
Returns:
|
|
605
664
|
Tuple[Optional[str], bool]: (Matched P code or None if no match, True if exact match or False if not)
|
|
@@ -614,18 +673,30 @@ class AdminLevel:
|
|
|
614
673
|
# name looks like a p-code, but doesn't match p-codes
|
|
615
674
|
# so try adjusting p-code length
|
|
616
675
|
pcode = self.convert_admin_pcode_length(
|
|
617
|
-
countryiso3, pcode,
|
|
676
|
+
countryiso3, pcode, **kwargs
|
|
618
677
|
)
|
|
619
678
|
return pcode, True
|
|
620
679
|
else:
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
680
|
+
if self.use_parent and "parent" in kwargs:
|
|
681
|
+
parent = kwargs["parent"]
|
|
682
|
+
name_parent_to_pcode = self.name_parent_to_pcode.get(
|
|
683
|
+
countryiso3
|
|
684
|
+
)
|
|
685
|
+
if name_parent_to_pcode:
|
|
686
|
+
name_to_pcode = name_parent_to_pcode.get(parent)
|
|
687
|
+
if name_to_pcode is not None:
|
|
688
|
+
pcode = name_to_pcode.get(name.lower())
|
|
689
|
+
if pcode:
|
|
690
|
+
return pcode, True
|
|
691
|
+
else:
|
|
692
|
+
name_to_pcode = self.name_to_pcode.get(countryiso3)
|
|
693
|
+
if name_to_pcode is not None:
|
|
694
|
+
pcode = name_to_pcode.get(name.lower())
|
|
695
|
+
if pcode:
|
|
696
|
+
return pcode, True
|
|
626
697
|
if not fuzzy_match:
|
|
627
698
|
return None, True
|
|
628
|
-
pcode = self.fuzzy_pcode(countryiso3, name,
|
|
699
|
+
pcode = self.fuzzy_pcode(countryiso3, name, **kwargs)
|
|
629
700
|
return pcode, False
|
|
630
701
|
|
|
631
702
|
def output_matches(self) -> List[str]:
|
|
@@ -556,3 +556,8 @@ admin_fuzzy_dont:
|
|
|
556
556
|
- "sahil"
|
|
557
557
|
- "say'on"
|
|
558
558
|
- "syria"
|
|
559
|
+
|
|
560
|
+
admin_info_with_parent:
|
|
561
|
+
- {pcode: AF0101, name: Kabul, iso3: AFG, parent: AF01}
|
|
562
|
+
- {pcode: AF0102, name: Paghman, iso3: AFG, parent: AF01}
|
|
563
|
+
- {pcode: AF0201, name: Kabul, iso3: AFG, parent: AF02} # testing purposes
|
|
@@ -33,6 +33,8 @@ class TestAdminLevel:
|
|
|
33
33
|
assert adminone.get_admin_level("YEM") == 1
|
|
34
34
|
assert len(adminone.get_pcode_list()) == 433
|
|
35
35
|
assert adminone.get_pcode_length("YEM") == 4
|
|
36
|
+
assert adminone.use_parent is False
|
|
37
|
+
assert adminone.pcode_to_iso3["YE30"] == "YEM"
|
|
36
38
|
assert adminone.get_pcode("YEM", "YE30", logname="test") == (
|
|
37
39
|
"YE30",
|
|
38
40
|
True,
|
|
@@ -177,6 +179,45 @@ class TestAdminLevel:
|
|
|
177
179
|
"test - YEM: Matching (fuzzy) Al Dhale'e / الضالع to Ad Dali on map",
|
|
178
180
|
]
|
|
179
181
|
|
|
182
|
+
def test_adminlevel_parent(self, config):
|
|
183
|
+
admintwo = AdminLevel(config)
|
|
184
|
+
admintwo.countries_fuzzy_try = None
|
|
185
|
+
admintwo.setup_from_admin_info(config["admin_info_with_parent"])
|
|
186
|
+
assert admintwo.use_parent is True
|
|
187
|
+
assert admintwo.pcode_to_parent["AF0101"] == "AF01"
|
|
188
|
+
assert admintwo.get_pcode("AFG", "AF0101", logname="test") == (
|
|
189
|
+
"AF0101",
|
|
190
|
+
True,
|
|
191
|
+
)
|
|
192
|
+
assert admintwo.get_pcode(
|
|
193
|
+
"AFG", "AF0101", parent="blah", logname="test"
|
|
194
|
+
) == ("AF0101", True)
|
|
195
|
+
assert admintwo.get_pcode("AFG", "Kabul", logname="test") == (
|
|
196
|
+
"AF0201",
|
|
197
|
+
True,
|
|
198
|
+
)
|
|
199
|
+
assert admintwo.get_pcode(
|
|
200
|
+
"AFG", "Kabul", parent="AF01", logname="test"
|
|
201
|
+
) == ("AF0101", True)
|
|
202
|
+
assert admintwo.get_pcode(
|
|
203
|
+
"AFG", "Kabul", parent="blah", logname="test"
|
|
204
|
+
) == (None, False)
|
|
205
|
+
assert admintwo.get_pcode(
|
|
206
|
+
"AFG", "Kabul", parent="AF02", logname="test"
|
|
207
|
+
) == ("AF0201", True)
|
|
208
|
+
assert admintwo.get_pcode(
|
|
209
|
+
"AFG", "Kabull", parent="AF01", logname="test"
|
|
210
|
+
) == ("AF0101", False)
|
|
211
|
+
assert admintwo.get_pcode(
|
|
212
|
+
"AFG", "Kabull", parent="blah", logname="test"
|
|
213
|
+
) == (None, False)
|
|
214
|
+
assert admintwo.get_pcode(
|
|
215
|
+
"AFG", "Kabull", parent="AF02", logname="test"
|
|
216
|
+
) == ("AF0201", False)
|
|
217
|
+
assert admintwo.get_pcode(
|
|
218
|
+
"ABC", "Kabull", parent="AF02", logname="test"
|
|
219
|
+
) == (None, False)
|
|
220
|
+
|
|
180
221
|
def test_adminlevel_with_url(self, config, url):
|
|
181
222
|
adminone = AdminLevel(config)
|
|
182
223
|
with pytest.raises(HXLIOException):
|
|
@@ -190,12 +231,25 @@ class TestAdminLevel:
|
|
|
190
231
|
adminone = AdminLevel(config)
|
|
191
232
|
adminone.setup_from_url()
|
|
192
233
|
assert adminone.get_admin_level("YEM") == 1
|
|
193
|
-
assert len(adminone.get_pcode_list()) ==
|
|
234
|
+
assert len(adminone.get_pcode_list()) == 2506
|
|
194
235
|
assert adminone.get_pcode_length("YEM") == 4
|
|
195
236
|
assert adminone.get_pcode("YEM", "YE30", logname="test") == (
|
|
196
237
|
"YE30",
|
|
197
238
|
True,
|
|
198
239
|
)
|
|
240
|
+
assert adminone.get_pcode(
|
|
241
|
+
"YEM", "YE30", parent="YEM", logname="test"
|
|
242
|
+
) == (
|
|
243
|
+
"YE30",
|
|
244
|
+
True,
|
|
245
|
+
)
|
|
246
|
+
# Exact match of p-code so doesn't need parent
|
|
247
|
+
assert adminone.get_pcode(
|
|
248
|
+
"YEM", "YE30", parent="Blah1", logname="test"
|
|
249
|
+
) == (
|
|
250
|
+
"YE30",
|
|
251
|
+
True,
|
|
252
|
+
)
|
|
199
253
|
assert adminone.get_pcode("YEM", "YEM30", logname="test") == (
|
|
200
254
|
"YE30",
|
|
201
255
|
True,
|
|
@@ -269,6 +323,19 @@ class TestAdminLevel:
|
|
|
269
323
|
"YE30",
|
|
270
324
|
False,
|
|
271
325
|
)
|
|
326
|
+
assert adminone.get_pcode(
|
|
327
|
+
"YEM", "Ad Dal", parent="YEM", logname="test"
|
|
328
|
+
) == (
|
|
329
|
+
"YE30",
|
|
330
|
+
False,
|
|
331
|
+
)
|
|
332
|
+
# Invalid parent means fuzzy matching won't match
|
|
333
|
+
assert adminone.get_pcode(
|
|
334
|
+
"YEM", "Ad Dal", parent="Blah2", logname="test"
|
|
335
|
+
) == (
|
|
336
|
+
None,
|
|
337
|
+
False,
|
|
338
|
+
)
|
|
272
339
|
assert adminone.get_pcode("YEM", "nord", logname="test") == (
|
|
273
340
|
None,
|
|
274
341
|
False,
|
|
@@ -307,6 +374,7 @@ class TestAdminLevel:
|
|
|
307
374
|
assert output == [
|
|
308
375
|
"test - Could not find ABC in map names!",
|
|
309
376
|
"test - NER: Could not find ABCDEFGH in map names!",
|
|
377
|
+
"test - YEM: Could not find Blah2 in map names!",
|
|
310
378
|
]
|
|
311
379
|
|
|
312
380
|
def test_adminlevel_pcode_formats(self, config, url, formats_url):
|
|
@@ -357,6 +425,7 @@ class TestAdminLevel:
|
|
|
357
425
|
|
|
358
426
|
admintwo = AdminLevel(config, admin_level=2)
|
|
359
427
|
admintwo.setup_from_url(admin_url=url)
|
|
428
|
+
assert admintwo.pcode_to_parent["YE3001"] == "YE30"
|
|
360
429
|
assert admintwo.get_pcode("YEM", "YE03001", logname="test") == (
|
|
361
430
|
None,
|
|
362
431
|
True,
|
|
@@ -371,6 +440,12 @@ class TestAdminLevel:
|
|
|
371
440
|
"YE3001",
|
|
372
441
|
True,
|
|
373
442
|
)
|
|
443
|
+
assert admintwo.get_pcode(
|
|
444
|
+
"YEM", "YEM3001", parent="Blah", logname="test"
|
|
445
|
+
) == (
|
|
446
|
+
"YE3001",
|
|
447
|
+
True,
|
|
448
|
+
)
|
|
374
449
|
assert admintwo.get_pcode("YEM", "YEM03001", logname="test") == (
|
|
375
450
|
"YE3001",
|
|
376
451
|
True,
|
|
@@ -456,6 +531,12 @@ class TestAdminLevel:
|
|
|
456
531
|
None,
|
|
457
532
|
True,
|
|
458
533
|
)
|
|
534
|
+
assert admintwo.get_pcode(
|
|
535
|
+
"NER", "NE00409", parent="blah", logname="test"
|
|
536
|
+
) == (
|
|
537
|
+
None,
|
|
538
|
+
True,
|
|
539
|
+
)
|
|
459
540
|
|
|
460
541
|
admintwo.set_parent_admins_from_adminlevels([adminone])
|
|
461
542
|
# The lookup in admin1 reveals that adding a 0 prefix to the admin1
|
|
@@ -465,6 +546,14 @@ class TestAdminLevel:
|
|
|
465
546
|
"NER004009",
|
|
466
547
|
True,
|
|
467
548
|
)
|
|
549
|
+
# we don't use the parent because it could have a pcode length issue
|
|
550
|
+
# itself
|
|
551
|
+
assert admintwo.get_pcode(
|
|
552
|
+
"NER", "NE00409", parent="blah", logname="test"
|
|
553
|
+
) == (
|
|
554
|
+
"NER004009",
|
|
555
|
+
True,
|
|
556
|
+
)
|
|
468
557
|
# The lookup in admin1 reveals that removing the 0 prefix from the
|
|
469
558
|
# admin1 is not a valid admin1 (CO80849) so the algorithm tries
|
|
470
559
|
# removing the 0 prefix at the admin2 level instead and hence succeeds
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/fixtures/global_pcode_lengths.csv
RENAMED
|
File without changes
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/fixtures/global_pcodes_adm_1_2.csv
RENAMED
|
File without changes
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/fixtures/secondary_historic_rates.csv
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/hdx/location/Countries_UZB_Deleted.csv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.0}/tests/hdx/location/test_wfp_exchangerates.py
RENAMED
|
File without changes
|