hdx-python-country 3.6.9__tar.gz → 3.7.1__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.1}/.config/pre-commit-config.yaml +5 -4
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/.config/ruff.toml +0 -1
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/.github/workflows/publish.yaml +2 -2
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/.github/workflows/run-python-tests.yaml +2 -2
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/CONTRIBUTING.md +3 -4
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/PKG-INFO +4 -4
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/documentation/.readthedocs.yaml +1 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/documentation/main.md +8 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/documentation/pydoc-markdown.yaml +3 -3
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/pyproject.toml +2 -2
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/requirements.txt +38 -36
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/src/hdx/location/_version.py +2 -2
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/src/hdx/location/adminlevel.py +119 -35
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/fixtures/adminlevel.yaml +5 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/hdx/location/test_adminlevel.py +95 -1
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/.config/coveragerc +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/.config/pytest.ini +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/.gitignore +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/LICENSE +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/README.md +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/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.1}/src/hdx/location/__init__.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/src/hdx/location/country.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/src/hdx/location/currency.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/src/hdx/location/names.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/src/hdx/location/phonetics.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/src/hdx/location/wfp_exchangerates.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/fixtures/global_pcode_lengths.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/fixtures/global_pcodes_adm_1_2.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/fixtures/secondary_historic_rates.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/fixtures/secondary_rates.json +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/hdx/location/Countries_UZB_Deleted.csv +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/hdx/location/__init__.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/hdx/location/test_country.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/hdx/location/test_currency.py +0 -0
- {hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/hdx/location/test_wfp_exchangerates.py +0 -0
|
@@ -8,7 +8,7 @@ repos:
|
|
|
8
8
|
- id: end-of-file-fixer
|
|
9
9
|
- id: check-ast
|
|
10
10
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
11
|
-
rev: v0.
|
|
11
|
+
rev: v0.4.4
|
|
12
12
|
hooks:
|
|
13
13
|
# Run the linter.
|
|
14
14
|
- id: ruff
|
|
@@ -16,10 +16,11 @@ repos:
|
|
|
16
16
|
# Run the formatter.
|
|
17
17
|
- id: ruff-format
|
|
18
18
|
args: [--config, .config/ruff.toml]
|
|
19
|
-
- repo: https://github.com/
|
|
20
|
-
rev:
|
|
19
|
+
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
20
|
+
rev: 0.1.45
|
|
21
21
|
hooks:
|
|
22
|
+
# Run the pip compile
|
|
22
23
|
- id: pip-compile
|
|
23
24
|
name: pip-compile requirements.txt
|
|
24
25
|
files: pyproject.toml
|
|
25
|
-
args: [pyproject.toml, --resolver=backtracking, --all-extras, --upgrade, -q, -o, requirements.txt]
|
|
26
|
+
args: [ pyproject.toml, --resolver=backtracking, --all-extras, --upgrade, -q, -o, requirements.txt ]
|
|
@@ -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.1}/.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.1
|
|
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.8
|
|
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 @@ renderer:
|
|
|
9
9
|
output_directory: docs
|
|
10
10
|
mkdocs_config:
|
|
11
11
|
site_name: HDX Python Country
|
|
12
|
-
theme:
|
|
12
|
+
theme: material
|
|
13
13
|
repo_url: "https://github.com/OCHA-DAP/hdx-python-country"
|
|
14
14
|
markdown:
|
|
15
15
|
source_linker:
|
|
@@ -24,9 +24,9 @@ renderer:
|
|
|
24
24
|
- title: Countries
|
|
25
25
|
contents:
|
|
26
26
|
- hdx.location.country.*
|
|
27
|
-
- title: Administration
|
|
27
|
+
- title: Administration Level
|
|
28
28
|
contents:
|
|
29
|
-
- hdx.location.
|
|
29
|
+
- hdx.location.adminlevel.*
|
|
30
30
|
- title: Currencies
|
|
31
31
|
contents:
|
|
32
32
|
- hdx.location.currency.*
|
|
@@ -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.8",
|
|
38
38
|
"libhxl>=5.2.1",
|
|
39
39
|
"pyphonetics",
|
|
40
40
|
]
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
|
|
4
|
-
#
|
|
5
|
-
# pip-compile --all-extras --output-file=requirements.txt pyproject.toml
|
|
6
|
-
#
|
|
7
|
-
annotated-types==0.6.0
|
|
1
|
+
# This file was autogenerated by uv via the following command:
|
|
2
|
+
# uv pip compile pyproject.toml --resolver=backtracking --all-extras -o requirements.txt
|
|
3
|
+
annotated-types==0.7.0
|
|
8
4
|
# via pydantic
|
|
9
5
|
attrs==23.2.0
|
|
10
6
|
# via
|
|
11
7
|
# frictionless
|
|
12
8
|
# jsonlines
|
|
13
9
|
# jsonschema
|
|
10
|
+
# referencing
|
|
14
11
|
certifi==2024.2.2
|
|
15
12
|
# via requests
|
|
16
13
|
cfgv==3.4.0
|
|
@@ -21,25 +18,23 @@ charset-normalizer==3.3.2
|
|
|
21
18
|
# via requests
|
|
22
19
|
click==8.1.7
|
|
23
20
|
# via typer
|
|
24
|
-
|
|
25
|
-
# via typer
|
|
26
|
-
coverage[toml]==7.4.3
|
|
21
|
+
coverage==7.5.1
|
|
27
22
|
# via pytest-cov
|
|
28
23
|
distlib==0.3.8
|
|
29
24
|
# via virtualenv
|
|
30
25
|
et-xmlfile==1.1.0
|
|
31
26
|
# via openpyxl
|
|
32
|
-
filelock==3.
|
|
27
|
+
filelock==3.14.0
|
|
33
28
|
# via virtualenv
|
|
34
|
-
frictionless==5.
|
|
29
|
+
frictionless==5.17.0
|
|
35
30
|
# via hdx-python-utilities
|
|
36
|
-
hdx-python-utilities==3.6.
|
|
31
|
+
hdx-python-utilities==3.6.8
|
|
37
32
|
# via hdx-python-country (pyproject.toml)
|
|
38
33
|
humanize==4.9.0
|
|
39
34
|
# via frictionless
|
|
40
|
-
identify==2.5.
|
|
35
|
+
identify==2.5.36
|
|
41
36
|
# via pre-commit
|
|
42
|
-
idna==3.
|
|
37
|
+
idna==3.7
|
|
43
38
|
# via requests
|
|
44
39
|
ijson==3.2.3
|
|
45
40
|
# via hdx-python-utilities
|
|
@@ -47,16 +42,18 @@ iniconfig==2.0.0
|
|
|
47
42
|
# via pytest
|
|
48
43
|
isodate==0.6.1
|
|
49
44
|
# via frictionless
|
|
50
|
-
jinja2==3.1.
|
|
45
|
+
jinja2==3.1.4
|
|
51
46
|
# via frictionless
|
|
52
47
|
jsonlines==4.0.0
|
|
53
48
|
# via hdx-python-utilities
|
|
54
49
|
jsonpath-ng==1.6.1
|
|
55
50
|
# via libhxl
|
|
56
|
-
jsonschema==4.
|
|
51
|
+
jsonschema==4.22.0
|
|
57
52
|
# via
|
|
58
53
|
# frictionless
|
|
59
54
|
# tableschema-to-template
|
|
55
|
+
jsonschema-specifications==2023.12.1
|
|
56
|
+
# via jsonschema
|
|
60
57
|
libhxl==5.2.1
|
|
61
58
|
# via hdx-python-country (pyproject.toml)
|
|
62
59
|
loguru==0.7.2
|
|
@@ -77,31 +74,29 @@ packaging==24.0
|
|
|
77
74
|
# via pytest
|
|
78
75
|
petl==1.7.15
|
|
79
76
|
# via frictionless
|
|
80
|
-
platformdirs==4.2.
|
|
77
|
+
platformdirs==4.2.2
|
|
81
78
|
# via virtualenv
|
|
82
|
-
pluggy==1.
|
|
79
|
+
pluggy==1.5.0
|
|
83
80
|
# via pytest
|
|
84
81
|
ply==3.11
|
|
85
82
|
# via
|
|
86
83
|
# jsonpath-ng
|
|
87
84
|
# libhxl
|
|
88
|
-
pre-commit==3.
|
|
85
|
+
pre-commit==3.7.1
|
|
89
86
|
# via hdx-python-country (pyproject.toml)
|
|
90
|
-
pydantic==2.
|
|
87
|
+
pydantic==2.7.1
|
|
91
88
|
# via frictionless
|
|
92
|
-
pydantic-core==2.
|
|
89
|
+
pydantic-core==2.18.2
|
|
93
90
|
# via pydantic
|
|
94
|
-
pygments==2.
|
|
91
|
+
pygments==2.18.0
|
|
95
92
|
# via rich
|
|
96
93
|
pyphonetics==0.5.3
|
|
97
94
|
# via hdx-python-country (pyproject.toml)
|
|
98
|
-
|
|
99
|
-
# via jsonschema
|
|
100
|
-
pytest==8.1.1
|
|
95
|
+
pytest==8.2.1
|
|
101
96
|
# via
|
|
102
97
|
# hdx-python-country (pyproject.toml)
|
|
103
98
|
# pytest-cov
|
|
104
|
-
pytest-cov==
|
|
99
|
+
pytest-cov==5.0.0
|
|
105
100
|
# via hdx-python-country (pyproject.toml)
|
|
106
101
|
python-dateutil==2.8.2
|
|
107
102
|
# via
|
|
@@ -119,21 +114,31 @@ pyyaml==6.0.1
|
|
|
119
114
|
# tableschema-to-template
|
|
120
115
|
ratelimit==2.2.1
|
|
121
116
|
# via hdx-python-utilities
|
|
122
|
-
|
|
117
|
+
referencing==0.35.1
|
|
118
|
+
# via
|
|
119
|
+
# jsonschema
|
|
120
|
+
# jsonschema-specifications
|
|
121
|
+
requests==2.32.2
|
|
123
122
|
# via
|
|
124
123
|
# frictionless
|
|
125
124
|
# libhxl
|
|
126
125
|
# requests-file
|
|
127
|
-
requests-file==2.
|
|
126
|
+
requests-file==2.1.0
|
|
128
127
|
# via hdx-python-utilities
|
|
129
128
|
rfc3986==2.0.0
|
|
130
129
|
# via frictionless
|
|
131
130
|
rich==13.7.1
|
|
132
131
|
# via typer
|
|
132
|
+
rpds-py==0.18.1
|
|
133
|
+
# via
|
|
134
|
+
# jsonschema
|
|
135
|
+
# referencing
|
|
133
136
|
ruamel-yaml==0.18.6
|
|
134
137
|
# via hdx-python-utilities
|
|
135
138
|
ruamel-yaml-clib==0.2.8
|
|
136
139
|
# via ruamel-yaml
|
|
140
|
+
setuptools==70.0.0
|
|
141
|
+
# via nodeenv
|
|
137
142
|
shellingham==1.5.4
|
|
138
143
|
# via typer
|
|
139
144
|
simpleeval==0.9.13
|
|
@@ -152,9 +157,9 @@ tabulate==0.9.0
|
|
|
152
157
|
# via frictionless
|
|
153
158
|
text-unidecode==1.3
|
|
154
159
|
# via python-slugify
|
|
155
|
-
typer
|
|
160
|
+
typer==0.12.3
|
|
156
161
|
# via frictionless
|
|
157
|
-
typing-extensions==4.
|
|
162
|
+
typing-extensions==4.11.0
|
|
158
163
|
# via
|
|
159
164
|
# frictionless
|
|
160
165
|
# pydantic
|
|
@@ -168,9 +173,9 @@ urllib3==2.2.1
|
|
|
168
173
|
# via
|
|
169
174
|
# libhxl
|
|
170
175
|
# requests
|
|
171
|
-
validators==0.
|
|
176
|
+
validators==0.28.1
|
|
172
177
|
# via frictionless
|
|
173
|
-
virtualenv==20.
|
|
178
|
+
virtualenv==20.26.2
|
|
174
179
|
# via pre-commit
|
|
175
180
|
wheel==0.43.0
|
|
176
181
|
# via libhxl
|
|
@@ -182,6 +187,3 @@ xlsxwriter==3.2.0
|
|
|
182
187
|
# via tableschema-to-template
|
|
183
188
|
xlwt==1.3.0
|
|
184
189
|
# via hdx-python-utilities
|
|
185
|
-
|
|
186
|
-
# The following packages are considered to be unsafe in a requirements file:
|
|
187
|
-
# setuptools
|
|
@@ -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]:
|
|
@@ -672,3 +743,16 @@ class AdminLevel:
|
|
|
672
743
|
logger.error(line)
|
|
673
744
|
output.append(line)
|
|
674
745
|
return output
|
|
746
|
+
|
|
747
|
+
def output_admin_name_mappings(self) -> List[str]:
|
|
748
|
+
"""Output log of name mappings
|
|
749
|
+
|
|
750
|
+
Returns:
|
|
751
|
+
List[str]: List of mappings
|
|
752
|
+
"""
|
|
753
|
+
output = []
|
|
754
|
+
for name, pcode in self.admin_name_mappings.items():
|
|
755
|
+
line = f"{name}: {self.pcode_to_name[pcode]} ({pcode})"
|
|
756
|
+
logger.info(line)
|
|
757
|
+
output.append(line)
|
|
758
|
+
return output
|
|
@@ -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,
|
|
@@ -153,6 +155,11 @@ class TestAdminLevel:
|
|
|
153
155
|
"test - Could not find ABC in map names!",
|
|
154
156
|
"test - NER: Could not find ABCDEFGH in map names!",
|
|
155
157
|
]
|
|
158
|
+
output = adminone.output_admin_name_mappings()
|
|
159
|
+
assert len(output) == 62
|
|
160
|
+
assert output[0] == "Nord-Ouest: North West (HT09)"
|
|
161
|
+
assert output[31] == "Juba Dhexe: Middle Juba (SO27)"
|
|
162
|
+
assert output[61] == "CU Niamey: Niamey (NER008)"
|
|
156
163
|
|
|
157
164
|
def test_adminlevel_fuzzy(self, config):
|
|
158
165
|
adminone = AdminLevel(config)
|
|
@@ -177,6 +184,45 @@ class TestAdminLevel:
|
|
|
177
184
|
"test - YEM: Matching (fuzzy) Al Dhale'e / الضالع to Ad Dali on map",
|
|
178
185
|
]
|
|
179
186
|
|
|
187
|
+
def test_adminlevel_parent(self, config):
|
|
188
|
+
admintwo = AdminLevel(config)
|
|
189
|
+
admintwo.countries_fuzzy_try = None
|
|
190
|
+
admintwo.setup_from_admin_info(config["admin_info_with_parent"])
|
|
191
|
+
assert admintwo.use_parent is True
|
|
192
|
+
assert admintwo.pcode_to_parent["AF0101"] == "AF01"
|
|
193
|
+
assert admintwo.get_pcode("AFG", "AF0101", logname="test") == (
|
|
194
|
+
"AF0101",
|
|
195
|
+
True,
|
|
196
|
+
)
|
|
197
|
+
assert admintwo.get_pcode(
|
|
198
|
+
"AFG", "AF0101", parent="blah", logname="test"
|
|
199
|
+
) == ("AF0101", True)
|
|
200
|
+
assert admintwo.get_pcode("AFG", "Kabul", logname="test") == (
|
|
201
|
+
"AF0201",
|
|
202
|
+
True,
|
|
203
|
+
)
|
|
204
|
+
assert admintwo.get_pcode(
|
|
205
|
+
"AFG", "Kabul", parent="AF01", logname="test"
|
|
206
|
+
) == ("AF0101", True)
|
|
207
|
+
assert admintwo.get_pcode(
|
|
208
|
+
"AFG", "Kabul", parent="blah", logname="test"
|
|
209
|
+
) == (None, False)
|
|
210
|
+
assert admintwo.get_pcode(
|
|
211
|
+
"AFG", "Kabul", parent="AF02", logname="test"
|
|
212
|
+
) == ("AF0201", True)
|
|
213
|
+
assert admintwo.get_pcode(
|
|
214
|
+
"AFG", "Kabull", parent="AF01", logname="test"
|
|
215
|
+
) == ("AF0101", False)
|
|
216
|
+
assert admintwo.get_pcode(
|
|
217
|
+
"AFG", "Kabull", parent="blah", logname="test"
|
|
218
|
+
) == (None, False)
|
|
219
|
+
assert admintwo.get_pcode(
|
|
220
|
+
"AFG", "Kabull", parent="AF02", logname="test"
|
|
221
|
+
) == ("AF0201", False)
|
|
222
|
+
assert admintwo.get_pcode(
|
|
223
|
+
"ABC", "Kabull", parent="AF02", logname="test"
|
|
224
|
+
) == (None, False)
|
|
225
|
+
|
|
180
226
|
def test_adminlevel_with_url(self, config, url):
|
|
181
227
|
adminone = AdminLevel(config)
|
|
182
228
|
with pytest.raises(HXLIOException):
|
|
@@ -190,12 +236,25 @@ class TestAdminLevel:
|
|
|
190
236
|
adminone = AdminLevel(config)
|
|
191
237
|
adminone.setup_from_url()
|
|
192
238
|
assert adminone.get_admin_level("YEM") == 1
|
|
193
|
-
assert len(adminone.get_pcode_list()) ==
|
|
239
|
+
assert len(adminone.get_pcode_list()) == 2509
|
|
194
240
|
assert adminone.get_pcode_length("YEM") == 4
|
|
195
241
|
assert adminone.get_pcode("YEM", "YE30", logname="test") == (
|
|
196
242
|
"YE30",
|
|
197
243
|
True,
|
|
198
244
|
)
|
|
245
|
+
assert adminone.get_pcode(
|
|
246
|
+
"YEM", "YE30", parent="YEM", logname="test"
|
|
247
|
+
) == (
|
|
248
|
+
"YE30",
|
|
249
|
+
True,
|
|
250
|
+
)
|
|
251
|
+
# Exact match of p-code so doesn't need parent
|
|
252
|
+
assert adminone.get_pcode(
|
|
253
|
+
"YEM", "YE30", parent="Blah1", logname="test"
|
|
254
|
+
) == (
|
|
255
|
+
"YE30",
|
|
256
|
+
True,
|
|
257
|
+
)
|
|
199
258
|
assert adminone.get_pcode("YEM", "YEM30", logname="test") == (
|
|
200
259
|
"YE30",
|
|
201
260
|
True,
|
|
@@ -269,6 +328,19 @@ class TestAdminLevel:
|
|
|
269
328
|
"YE30",
|
|
270
329
|
False,
|
|
271
330
|
)
|
|
331
|
+
assert adminone.get_pcode(
|
|
332
|
+
"YEM", "Ad Dal", parent="YEM", logname="test"
|
|
333
|
+
) == (
|
|
334
|
+
"YE30",
|
|
335
|
+
False,
|
|
336
|
+
)
|
|
337
|
+
# Invalid parent means fuzzy matching won't match
|
|
338
|
+
assert adminone.get_pcode(
|
|
339
|
+
"YEM", "Ad Dal", parent="Blah2", logname="test"
|
|
340
|
+
) == (
|
|
341
|
+
None,
|
|
342
|
+
False,
|
|
343
|
+
)
|
|
272
344
|
assert adminone.get_pcode("YEM", "nord", logname="test") == (
|
|
273
345
|
None,
|
|
274
346
|
False,
|
|
@@ -307,6 +379,7 @@ class TestAdminLevel:
|
|
|
307
379
|
assert output == [
|
|
308
380
|
"test - Could not find ABC in map names!",
|
|
309
381
|
"test - NER: Could not find ABCDEFGH in map names!",
|
|
382
|
+
"test - YEM: Could not find Blah2 in map names!",
|
|
310
383
|
]
|
|
311
384
|
|
|
312
385
|
def test_adminlevel_pcode_formats(self, config, url, formats_url):
|
|
@@ -357,6 +430,7 @@ class TestAdminLevel:
|
|
|
357
430
|
|
|
358
431
|
admintwo = AdminLevel(config, admin_level=2)
|
|
359
432
|
admintwo.setup_from_url(admin_url=url)
|
|
433
|
+
assert admintwo.pcode_to_parent["YE3001"] == "YE30"
|
|
360
434
|
assert admintwo.get_pcode("YEM", "YE03001", logname="test") == (
|
|
361
435
|
None,
|
|
362
436
|
True,
|
|
@@ -371,6 +445,12 @@ class TestAdminLevel:
|
|
|
371
445
|
"YE3001",
|
|
372
446
|
True,
|
|
373
447
|
)
|
|
448
|
+
assert admintwo.get_pcode(
|
|
449
|
+
"YEM", "YEM3001", parent="Blah", logname="test"
|
|
450
|
+
) == (
|
|
451
|
+
"YE3001",
|
|
452
|
+
True,
|
|
453
|
+
)
|
|
374
454
|
assert admintwo.get_pcode("YEM", "YEM03001", logname="test") == (
|
|
375
455
|
"YE3001",
|
|
376
456
|
True,
|
|
@@ -456,6 +536,12 @@ class TestAdminLevel:
|
|
|
456
536
|
None,
|
|
457
537
|
True,
|
|
458
538
|
)
|
|
539
|
+
assert admintwo.get_pcode(
|
|
540
|
+
"NER", "NE00409", parent="blah", logname="test"
|
|
541
|
+
) == (
|
|
542
|
+
None,
|
|
543
|
+
True,
|
|
544
|
+
)
|
|
459
545
|
|
|
460
546
|
admintwo.set_parent_admins_from_adminlevels([adminone])
|
|
461
547
|
# The lookup in admin1 reveals that adding a 0 prefix to the admin1
|
|
@@ -465,6 +551,14 @@ class TestAdminLevel:
|
|
|
465
551
|
"NER004009",
|
|
466
552
|
True,
|
|
467
553
|
)
|
|
554
|
+
# we don't use the parent because it could have a pcode length issue
|
|
555
|
+
# itself
|
|
556
|
+
assert admintwo.get_pcode(
|
|
557
|
+
"NER", "NE00409", parent="blah", logname="test"
|
|
558
|
+
) == (
|
|
559
|
+
"NER004009",
|
|
560
|
+
True,
|
|
561
|
+
)
|
|
468
562
|
# The lookup in admin1 reveals that removing the 0 prefix from the
|
|
469
563
|
# admin1 is not a valid admin1 (CO80849) so the algorithm tries
|
|
470
564
|
# 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
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/fixtures/global_pcode_lengths.csv
RENAMED
|
File without changes
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/fixtures/global_pcodes_adm_1_2.csv
RENAMED
|
File without changes
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/tests/fixtures/secondary_historic_rates.csv
RENAMED
|
File without changes
|
|
File without changes
|
{hdx_python_country-3.6.9 → hdx_python_country-3.7.1}/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.1}/tests/hdx/location/test_wfp_exchangerates.py
RENAMED
|
File without changes
|