hdx-python-country 3.9.5__tar.gz → 3.9.8__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 (50) hide show
  1. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/.pre-commit-config.yaml +4 -4
  2. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/PKG-INFO +2 -2
  3. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/hatch.toml +1 -1
  4. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/pyproject.toml +1 -1
  5. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/requirements.txt +43 -40
  6. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/_version.py +16 -3
  7. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/country.py +78 -27
  8. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/wfp_api.py +2 -2
  9. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/hdx/location/test_country.py +112 -4
  10. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/.coveragerc +0 -0
  11. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/.github/workflows/publish.yaml +0 -0
  12. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/.github/workflows/run-python-tests.yaml +0 -0
  13. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/.gitignore +0 -0
  14. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/LICENSE +0 -0
  15. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/README.md +0 -0
  16. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/documentation/.readthedocs.yaml +0 -0
  17. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/documentation/index.md +0 -0
  18. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/documentation/mkdocs.yaml +0 -0
  19. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/pytest.ini +0 -0
  20. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/ruff.toml +0 -0
  21. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/Countries & Territories Taxonomy MVP - C&T Taxonomy with HXL Tags.csv +0 -0
  22. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/__init__.py +0 -0
  23. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/adminlevel.py +0 -0
  24. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/currency.py +0 -0
  25. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/int_timestamp.py +0 -0
  26. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/src/hdx/location/wfp_exchangerates.py +0 -0
  27. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/adminlevel.yaml +0 -0
  28. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/adminlevelparent.yaml +0 -0
  29. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/download-global-pcode-lengths.csv +0 -0
  30. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/download-global-pcodes-adm-1-2.csv +0 -0
  31. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/secondary_historic_rates.csv +0 -0
  32. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/secondary_rates.json +0 -0
  33. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_List_1.json +0 -0
  34. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_List_2.json +0 -0
  35. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_1.json +0 -0
  36. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_10.json +0 -0
  37. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_2.json +0 -0
  38. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_3.json +0 -0
  39. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_4.json +0 -0
  40. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_5.json +0 -0
  41. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_6.json +0 -0
  42. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_7.json +0 -0
  43. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_8.json +0 -0
  44. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/fixtures/wfp/Currency_UsdIndirectQuotation_9.json +0 -0
  45. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/hdx/location/Countries_UZB_Deleted.csv +0 -0
  46. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/hdx/location/__init__.py +0 -0
  47. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/hdx/location/conftest.py +0 -0
  48. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/hdx/location/test_adminlevel.py +0 -0
  49. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/hdx/location/test_currency.py +0 -0
  50. {hdx_python_country-3.9.5 → hdx_python_country-3.9.8}/tests/hdx/location/test_wfp_exchangerates.py +0 -0
@@ -1,5 +1,5 @@
1
1
  default_language_version:
2
- python: python3.12
2
+ python: python3.13
3
3
  repos:
4
4
  - repo: https://github.com/pre-commit/pre-commit-hooks
5
5
  rev: v5.0.0
@@ -8,15 +8,15 @@ 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.9.10
11
+ rev: v0.12.0
12
12
  hooks:
13
13
  # Run the linter.
14
- - id: ruff
14
+ - id: ruff-check
15
15
  args: [ --fix ]
16
16
  # Run the formatter.
17
17
  - id: ruff-format
18
18
  - repo: https://github.com/astral-sh/uv-pre-commit
19
- rev: 0.6.5
19
+ rev: 0.7.14
20
20
  hooks:
21
21
  # Run the pip compile
22
22
  - id: pip-compile
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hdx-python-country
3
- Version: 3.9.5
3
+ Version: 3.9.8
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: Michael Rans
@@ -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.8.7
28
+ Requires-Dist: hdx-python-utilities>=3.9.2
29
29
  Requires-Dist: libhxl>=5.2.2
30
30
  Requires-Dist: tenacity
31
31
  Provides-Extra: dev
@@ -24,7 +24,7 @@ version_scheme = "python-simplified-semver"
24
24
  features = ["test"]
25
25
 
26
26
  [[envs.hatch-test.matrix]]
27
- python = ["3.12"]
27
+ python = ["3.13"]
28
28
 
29
29
  [envs.hatch-test.scripts]
30
30
  run = """
@@ -34,7 +34,7 @@ classifiers = [
34
34
  requires-python = ">=3.8"
35
35
 
36
36
  dependencies = [
37
- "hdx-python-utilities>=3.8.7",
37
+ "hdx-python-utilities>=3.9.2",
38
38
  "libhxl>=5.2.2",
39
39
  "tenacity",
40
40
  ]
@@ -2,7 +2,7 @@
2
2
  # uv pip compile pyproject.toml --resolver=backtracking --all-extras -o requirements.txt
3
3
  annotated-types==0.7.0
4
4
  # via pydantic
5
- astdoc==1.2.1
5
+ astdoc==1.3.2
6
6
  # via mkapi
7
7
  attrs==25.3.0
8
8
  # via
@@ -12,39 +12,40 @@ attrs==25.3.0
12
12
  # referencing
13
13
  babel==2.17.0
14
14
  # via mkdocs-material
15
- backrefs==5.8
15
+ backrefs==5.9
16
16
  # via mkdocs-material
17
- certifi==2025.4.26
17
+ certifi==2025.8.3
18
18
  # via requests
19
19
  cfgv==3.4.0
20
20
  # via pre-commit
21
21
  chardet==5.2.0
22
22
  # via frictionless
23
- charset-normalizer==3.4.2
23
+ charset-normalizer==3.4.3
24
24
  # via requests
25
- click==8.1.8
25
+ click==8.2.1
26
26
  # via
27
27
  # mkdocs
28
+ # mkdocs-material
28
29
  # typer
29
30
  colorama==0.4.6
30
31
  # via mkdocs-material
31
- coverage==7.8.0
32
+ coverage==7.10.7
32
33
  # via pytest-cov
33
- distlib==0.3.9
34
+ distlib==0.4.0
34
35
  # via virtualenv
35
36
  et-xmlfile==2.0.0
36
37
  # via openpyxl
37
- filelock==3.18.0
38
+ filelock==3.19.1
38
39
  # via virtualenv
39
40
  frictionless==5.18.1
40
41
  # via hdx-python-utilities
41
42
  ghp-import==2.1.0
42
43
  # via mkdocs
43
- hdx-python-utilities==3.8.7
44
+ hdx-python-utilities==3.9.2
44
45
  # via hdx-python-country (pyproject.toml)
45
- humanize==4.12.3
46
+ humanize==4.13.0
46
47
  # via frictionless
47
- identify==2.6.10
48
+ identify==2.6.14
48
49
  # via pre-commit
49
50
  idna==3.10
50
51
  # via requests
@@ -64,24 +65,24 @@ jsonlines==4.0.0
64
65
  # via hdx-python-utilities
65
66
  jsonpath-ng==1.7.0
66
67
  # via libhxl
67
- jsonschema==4.23.0
68
+ jsonschema==4.25.1
68
69
  # via
69
70
  # frictionless
70
71
  # tableschema-to-template
71
- jsonschema-specifications==2025.4.1
72
+ jsonschema-specifications==2025.9.1
72
73
  # via jsonschema
73
74
  libhxl==5.2.2
74
75
  # via hdx-python-country (pyproject.toml)
75
76
  loguru==0.7.3
76
77
  # via hdx-python-utilities
77
- markdown==3.8
78
+ markdown==3.9
78
79
  # via
79
80
  # mkdocs
80
81
  # mkdocs-material
81
82
  # pymdown-extensions
82
- markdown-it-py==3.0.0
83
+ markdown-it-py==4.0.0
83
84
  # via rich
84
- marko==2.1.3
85
+ marko==2.2.0
85
86
  # via frictionless
86
87
  markupsafe==3.0.2
87
88
  # via
@@ -93,7 +94,7 @@ mergedeep==1.3.4
93
94
  # via
94
95
  # mkdocs
95
96
  # mkdocs-get-deps
96
- mkapi==4.4.0
97
+ mkapi==4.4.5
97
98
  # via hdx-python-country (pyproject.toml)
98
99
  mkdocs==1.6.1
99
100
  # via
@@ -101,7 +102,7 @@ mkdocs==1.6.1
101
102
  # mkdocs-material
102
103
  mkdocs-get-deps==0.2.0
103
104
  # via mkdocs
104
- mkdocs-material==9.6.14
105
+ mkdocs-material==9.6.20
105
106
  # via mkapi
106
107
  mkdocs-material-extensions==1.3.1
107
108
  # via mkdocs-material
@@ -117,37 +118,40 @@ paginate==0.5.7
117
118
  # via mkdocs-material
118
119
  pathspec==0.12.1
119
120
  # via mkdocs
120
- petl==1.7.16
121
+ petl==1.7.17
121
122
  # via frictionless
122
- platformdirs==4.3.8
123
+ platformdirs==4.4.0
123
124
  # via
124
125
  # mkdocs-get-deps
125
126
  # virtualenv
126
127
  pluggy==1.6.0
127
- # via pytest
128
+ # via
129
+ # pytest
130
+ # pytest-cov
128
131
  ply==3.11
129
132
  # via
130
133
  # jsonpath-ng
131
134
  # libhxl
132
- pre-commit==4.2.0
135
+ pre-commit==4.3.0
133
136
  # via hdx-python-country (pyproject.toml)
134
- pydantic==2.11.4
137
+ pydantic==2.11.9
135
138
  # via frictionless
136
139
  pydantic-core==2.33.2
137
140
  # via pydantic
138
- pygments==2.19.1
141
+ pygments==2.19.2
139
142
  # via
140
143
  # mkdocs-material
144
+ # pytest
141
145
  # rich
142
- pymdown-extensions==10.15
146
+ pymdown-extensions==10.16.1
143
147
  # via mkdocs-material
144
148
  pyphonetics==0.5.3
145
149
  # via hdx-python-utilities
146
- pytest==8.3.5
150
+ pytest==8.4.2
147
151
  # via
148
152
  # hdx-python-country (pyproject.toml)
149
153
  # pytest-cov
150
- pytest-cov==6.1.1
154
+ pytest-cov==7.0.0
151
155
  # via hdx-python-country (pyproject.toml)
152
156
  python-dateutil==2.9.0.post0
153
157
  # via
@@ -176,7 +180,7 @@ referencing==0.36.2
176
180
  # via
177
181
  # jsonschema
178
182
  # jsonschema-specifications
179
- requests==2.32.3
183
+ requests==2.32.5
180
184
  # via
181
185
  # frictionless
182
186
  # libhxl
@@ -186,13 +190,13 @@ requests-file==2.1.0
186
190
  # via hdx-python-utilities
187
191
  rfc3986==2.0.0
188
192
  # via frictionless
189
- rich==14.0.0
193
+ rich==14.1.0
190
194
  # via typer
191
- rpds-py==0.25.0
195
+ rpds-py==0.27.1
192
196
  # via
193
197
  # jsonschema
194
198
  # referencing
195
- ruamel-yaml==0.18.10
199
+ ruamel-yaml==0.18.15
196
200
  # via hdx-python-utilities
197
201
  ruamel-yaml-clib==0.2.12
198
202
  # via ruamel-yaml
@@ -202,7 +206,7 @@ simpleeval==1.0.3
202
206
  # via frictionless
203
207
  six==1.17.0
204
208
  # via python-dateutil
205
- structlog==25.3.0
209
+ structlog==25.4.0
206
210
  # via libhxl
207
211
  tableschema-to-template==0.0.13
208
212
  # via hdx-python-utilities
@@ -212,41 +216,40 @@ tenacity==9.1.2
212
216
  # via hdx-python-country (pyproject.toml)
213
217
  text-unidecode==1.3
214
218
  # via python-slugify
215
- typer==0.15.4
219
+ typer==0.19.1
216
220
  # via frictionless
217
- typing-extensions==4.13.2
221
+ typing-extensions==4.15.0
218
222
  # via
219
223
  # frictionless
220
224
  # pydantic
221
225
  # pydantic-core
222
- # referencing
223
226
  # typer
224
227
  # typing-inspection
225
- typing-inspection==0.4.0
228
+ typing-inspection==0.4.1
226
229
  # via pydantic
227
230
  unidecode==1.4.0
228
231
  # via
229
232
  # libhxl
230
233
  # pyphonetics
231
- urllib3==2.4.0
234
+ urllib3==2.5.0
232
235
  # via
233
236
  # libhxl
234
237
  # requests
235
238
  validators==0.35.0
236
239
  # via frictionless
237
- virtualenv==20.31.2
240
+ virtualenv==20.34.0
238
241
  # via pre-commit
239
242
  watchdog==6.0.0
240
243
  # via mkdocs
241
244
  wheel==0.45.1
242
245
  # via libhxl
243
- xlrd==2.0.1
246
+ xlrd==2.0.2
244
247
  # via hdx-python-utilities
245
248
  xlrd3==1.1.0
246
249
  # via libhxl
247
250
  xlsx2csv==0.8.4
248
251
  # via hdx-python-utilities
249
- xlsxwriter==3.2.3
252
+ xlsxwriter==3.2.9
250
253
  # via tableschema-to-template
251
254
  xlwt==1.3.0
252
255
  # via hdx-python-utilities
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '3.9.5'
21
- __version_tuple__ = version_tuple = (3, 9, 5)
31
+ __version__ = version = '3.9.8'
32
+ __version_tuple__ = version_tuple = (3, 9, 8)
33
+
34
+ __commit_id__ = commit_id = None
@@ -1,10 +1,8 @@
1
1
  """Country location"""
2
2
 
3
- import copy
4
3
  import logging
5
4
  import os.path
6
5
  import re
7
- from string import punctuation
8
6
  from typing import Dict, List, Optional, Tuple, Union
9
7
 
10
8
  import hxl
@@ -62,6 +60,7 @@ class Country:
62
60
  "BOLIVARIAN",
63
61
  "PLURINATIONAL",
64
62
  "PEOPLE'S",
63
+ "PEOPLES",
65
64
  "DUTCH PART",
66
65
  "FRENCH PART",
67
66
  "MALVINAS",
@@ -721,38 +720,90 @@ class Country:
721
720
  Returns:
722
721
  Tuple[str, List[str]]: Uppercase simplified country name and list of removed words
723
722
  """
724
- countryupper = country.upper()
723
+ # Convert the input into an upper-cased list of words
724
+ countryupper = country.upper().strip()
725
725
  words = get_words_in_sentence(countryupper)
726
+
727
+ # Strip common patterns
726
728
  index = countryupper.find(",")
727
729
  if index != -1:
728
730
  countryupper = countryupper[:index]
729
731
  index = countryupper.find(":")
730
732
  if index != -1:
731
733
  countryupper = countryupper[:index]
732
- regex = re.compile(r"\(.+?\)")
733
- countryupper = regex.sub("", countryupper)
734
- remove = copy.deepcopy(cls.simplifications)
735
- for simplification1, simplification2 in cls.abbreviations.items():
736
- countryupper = countryupper.replace(simplification1, "")
737
- remove.append(simplification2)
738
- for (
739
- simplification1,
740
- simplifications,
741
- ) in cls.multiple_abbreviations.items():
742
- countryupper = countryupper.replace(simplification1, "")
743
- for simplification2 in simplifications:
744
- remove.append(simplification2)
745
- remove = "|".join(remove)
746
- regex = re.compile(r"[^\w\s]|\b(" + remove + r")\b", flags=re.IGNORECASE)
747
- countryupper = regex.sub("", countryupper)
748
- countryupper = countryupper.strip()
749
- countryupper_words = get_words_in_sentence(countryupper)
750
- if len(countryupper_words) > 1:
751
- countryupper = countryupper_words[0]
752
- if countryupper:
753
- countryupper = countryupper.strip(punctuation)
754
- words.remove(countryupper)
755
- return countryupper, words
734
+
735
+ if countryupper and not (countryupper[0] == "(" and countryupper[-1] == ")"):
736
+ regex = re.compile(r"\(.+?\)")
737
+ countryupper = regex.sub("", countryupper)
738
+
739
+ # Find the words that remain as candidates for the simplified name.
740
+ # These are guaranteed to be a subset of `words` because we have only pruned
741
+ # parts from the sentence and not done any transformative processing.
742
+ candidate_words = get_words_in_sentence(countryupper)
743
+
744
+ if candidate_words:
745
+ # Make the simplifying terms indexable for efficient lookup
746
+ multiword_terms = {}
747
+ singleword_terms = set()
748
+
749
+ for terms in [
750
+ cls.simplifications,
751
+ cls.abbreviations.keys(),
752
+ cls.abbreviations.values(),
753
+ cls.multiple_abbreviations.keys(),
754
+ ] + list(cls.multiple_abbreviations.values()):
755
+ for term in terms:
756
+ if " " in term:
757
+ # Index multi-word terms by the first term against a list of the terms
758
+ term_parts = term.split(" ")
759
+ multiword_terms[term_parts[0]] = term_parts
760
+ else:
761
+ # Add single word terms to the set, and add their dot-less form as well
762
+ singleword_terms.add(term)
763
+ if term[-1] == ".":
764
+ singleword_terms.add(term.strip("."))
765
+
766
+ num_candidate_words = len(candidate_words)
767
+ simplified_term = ""
768
+ enumerated_words = enumerate(candidate_words)
769
+ default = (num_candidate_words, "")
770
+
771
+ # Iterate through the candidate terms until we a) find a non-simplified word
772
+ # or b) hit the end of the list of words
773
+ while (val := next(enumerated_words, default)) != default:
774
+ i, word = val
775
+ if word in singleword_terms:
776
+ # If the word was a single word simplification term then skip it
777
+ continue
778
+ if (
779
+ # If the current term is the first word in a multi-part term
780
+ (term_parts := multiword_terms.get(word))
781
+ # And there are enough words left in the sentence
782
+ and i + len(term_parts) <= num_candidate_words
783
+ # And all of the words in the multi-word phrase are in sequence
784
+ # in the candidate term starting at the current position
785
+ and all(
786
+ candidate_words[i + j] == term_part
787
+ for j, term_part in enumerate(term_parts)
788
+ )
789
+ ):
790
+ # Then skip the other words in the term and continue
791
+ for _ in range(len(term_parts) - 1):
792
+ next(enumerated_words)
793
+
794
+ continue
795
+ # Else we found a word that we aren't dropping - it is our simplified word.
796
+ # Take it and break.
797
+ simplified_term = word
798
+ break
799
+
800
+ if simplified_term:
801
+ # We found a simplified term. Remove it from the list of other terms
802
+ words.remove(simplified_term)
803
+ else:
804
+ simplified_term = ""
805
+
806
+ return simplified_term, words
756
807
 
757
808
  @classmethod
758
809
  def get_iso3_country_code(
@@ -28,8 +28,8 @@ class WFPAPI:
28
28
  """
29
29
 
30
30
  token_url = "https://api.wfp.org/token"
31
- base_url = "https://api.wfp.org/vam-data-bridges/5.0.0/"
32
- scope = "vamdatabridges_commodities-list_get vamdatabridges_commodityunits-list_get vamdatabridges_marketprices-alps_get vamdatabridges_commodities-categories-list_get vamdatabridges_commodityunits-conversion-list_get vamdatabridges_marketprices-priceweekly_get vamdatabridges_markets-geojsonlist_get vamdatabridges_marketprices-pricemonthly_get vamdatabridges_markets-list_get vamdatabridges_currency-list_get vamdatabridges_currency-usdindirectquotation_get"
31
+ base_url = "https://api.wfp.org/vam-data-bridges/6.0.0/"
32
+ scope = "gefs_geoless-items-countries_get vamdatabridges_commodities-list_get vamdatabridges_commodityunits-list_get vamdatabridges_marketprices-alps_get vamdatabridges_commodities-categories-list_get vamdatabridges_commodityunits-conversion-list_get vamdatabridges_marketprices-priceweekly_get vamdatabridges_markets-geojsonlist_get vamdatabridges_marketprices-pricemonthly_get vamdatabridges_markets-list_get vamdatabridges_currency-list_get vamdatabridges_currency-usdindirectquotation_get"
33
33
  default_retry_params = {
34
34
  "retry": retry_if_exception_type(DownloadError),
35
35
  "after": after_log(logger, logging.INFO),
@@ -644,36 +644,143 @@ class TestCountry:
644
644
  ]
645
645
 
646
646
  def test_simplify_countryname(self):
647
- assert Country.simplify_countryname("jpn") == ("JPN", list())
647
+ # Test that we handle the empty string case
648
+ assert Country.simplify_countryname("") == ("", [])
649
+
650
+ # Test that country codes and arbitrary words return just the word but capitalised
651
+ assert Country.simplify_countryname("jpn") == ("JPN", [])
652
+ assert Country.simplify_countryname("test") == ("TEST", [])
653
+
654
+ # Test simplified terms are removed, including abbreviations
648
655
  assert Country.simplify_countryname("United Rep. of Tanzania") == (
649
656
  "TANZANIA",
650
657
  ["UNITED", "REP", "OF"],
651
658
  )
659
+ assert Country.simplify_countryname(
660
+ "The former Yugoslav Republic of Macedonia"
661
+ ) == ("MACEDONIA", ["THE", "FORMER", "YUGOSLAV", "REPUBLIC", "OF"])
662
+
663
+ # Test different word orderings and bracketing are consistent
652
664
  assert Country.simplify_countryname("Micronesia (Federated States of)") == (
653
665
  "MICRONESIA",
654
666
  ["FEDERATED", "STATES", "OF"],
655
667
  )
668
+ assert Country.simplify_countryname("Federated States of Micronesia") == (
669
+ "MICRONESIA",
670
+ ["FEDERATED", "STATES", "OF"],
671
+ )
672
+ assert Country.simplify_countryname("(Federated States of) Micronesia") == (
673
+ "MICRONESIA",
674
+ ["FEDERATED", "STATES", "OF"],
675
+ )
676
+
677
+ # Test that the simplified terms on their own are dropped and that we handle
678
+ # the "no simplified term" case
679
+ assert Country.simplify_countryname("Federated States") == (
680
+ "",
681
+ ["FEDERATED", "STATES"],
682
+ )
683
+
684
+ # Test that multi-word simplifications are dropped
685
+ assert Country.simplify_countryname("French Part of Saint Martin") == (
686
+ "MARTIN",
687
+ ["FRENCH", "PART", "OF", "SAINT"],
688
+ )
689
+ assert Country.simplify_countryname("French Part of Saint-Martin") == (
690
+ "MARTIN",
691
+ ["FRENCH", "PART", "OF", "SAINT"],
692
+ )
693
+ # "French Part" is a simplification and so can't be the simplified term
694
+ assert Country.simplify_countryname("French Part") == ("", ["FRENCH", "PART"])
695
+ # But the words must be consecutive for multi-part terms,
696
+ # so we don't drop "French" and "part" here
697
+ assert Country.simplify_countryname("French and Part") == (
698
+ "FRENCH",
699
+ ["AND", "PART"],
700
+ )
701
+
702
+ # Test that we handle abbreviations with and without punctuation
656
703
  assert Country.simplify_countryname("Dem. Rep. of the Congo") == (
657
704
  "CONGO",
658
705
  ["DEM", "REP", "OF", "THE"],
659
706
  )
707
+ assert Country.simplify_countryname("Dem Rep of the Congo") == (
708
+ "CONGO",
709
+ ["DEM", "REP", "OF", "THE"],
710
+ )
711
+
712
+ # Test that we handle the "Country, Specifics" comma format
660
713
  assert Country.simplify_countryname(
661
714
  "Korea, Democratic People's Republic of"
662
715
  ) == ("KOREA", ["DEMOCRATIC", "PEOPLE'S", "REPUBLIC", "OF"])
663
716
  assert Country.simplify_countryname(
664
717
  "Democratic People's Republic of Korea"
665
718
  ) == ("KOREA", ["DEMOCRATIC", "PEOPLE'S", "REPUBLIC", "OF"])
719
+
720
+ # Test that we handle more bracketed formats
666
721
  assert Country.simplify_countryname("Korea (the Republic of))") == (
667
722
  "KOREA",
668
723
  ["THE", "REPUBLIC", "OF"],
669
724
  )
725
+ # Regression test for bug #70 - partial brackets
670
726
  assert Country.simplify_countryname("Korea (the Republic of") == (
671
727
  "KOREA",
672
728
  ["THE", "REPUBLIC", "OF"],
673
729
  )
674
- assert Country.simplify_countryname(
675
- "The former Yugoslav Republic of Macedonia"
676
- ) == ("MACEDONIA", ["THE", "FORMER", "YUGOSLAV", "REPUBLIC", "OF"])
730
+
731
+ # Test that we don't strip everything just because it's bracketed, even if the brackets
732
+ # are surrounded by whitespace
733
+ assert Country.simplify_countryname("(the Republic of Korea)") == (
734
+ "KOREA",
735
+ ["THE", "REPUBLIC", "OF"],
736
+ )
737
+ assert Country.simplify_countryname(" (the Republic of Korea) ") == (
738
+ "KOREA",
739
+ ["THE", "REPUBLIC", "OF"],
740
+ )
741
+
742
+ # Test that we're actually stripping the brackets and that it's not all just been
743
+ # simplified words that we'd drop anyway, even if they weren't in brackets
744
+ assert Country.simplify_countryname("(Sometimes) Korea") == (
745
+ "KOREA",
746
+ ["SOMETIMES"],
747
+ )
748
+
749
+ # Regression test for bug #75 - apostrophes in simplified term
750
+ assert Country.simplify_countryname("d'Ivoire Côte") == ("D'IVOIRE", ["CÔTE"])
751
+
752
+ # Regression test for bug #77 - other punctuation in simplified term
753
+ assert Country.simplify_countryname("Guinea-Bissau") == ("GUINEA", ["BISSAU"])
754
+
755
+ # Test simplification of terms with apostrophes, and the non-apostrophe form
756
+ assert Country.simplify_countryname("People's Republic of Bangladesh") == (
757
+ "BANGLADESH",
758
+ ["PEOPLE'S", "REPUBLIC", "OF"],
759
+ )
760
+ assert Country.simplify_countryname("Peoples Republic of Bangladesh") == (
761
+ "BANGLADESH",
762
+ ["PEOPLES", "REPUBLIC", "OF"],
763
+ )
764
+ # Known limitation with "smart quote" handling
765
+ assert Country.simplify_countryname("People’s Republic of Bangladesh") == (
766
+ "PEOPLE’S",
767
+ ["REPUBLIC", "OF", "BANGLADESH"],
768
+ )
769
+
770
+ # Simplifying assumes that it isn't getting an address and simplifies to the first
771
+ # part around commas, even if it isn't a country
772
+ assert Country.simplify_countryname("Paris, France") == (
773
+ "PARIS",
774
+ ["FRANCE"],
775
+ )
776
+
777
+ # Some people supply strings that aren't countries
778
+ # (often indirectly via `get_iso3_country_code_fuzzy()`)
779
+ # Ensure the function doesn't error, even if the value is meaningless.
780
+ assert Country.simplify_countryname("3.1 Global scores and ranking") == (
781
+ "3",
782
+ ["1", "GLOBAL", "SCORES", "AND", "RANKING"],
783
+ )
677
784
 
678
785
  def test_get_iso3_country_code(self):
679
786
  assert Country.get_iso3_country_code("jpn") == "JPN"
@@ -826,6 +933,7 @@ class TestCountry:
826
933
  self.setup_unofficial_date()
827
934
  assert Country.get_iso3_country_code_fuzzy("Kosovo") == ("XKX", True)
828
935
  assert Country.get_iso3_country_code_fuzzy("Kosovo*") == ("XKX", False)
936
+ assert Country.get_iso3_country_code_fuzzy("d'Ivoire Côte") == ("CIV", False)
829
937
 
830
938
  def test_get_countries_in_region(self):
831
939
  assert Country.get_countries_in_region("Eastern Asia") == [