hdx-python-country 3.7.4__tar.gz → 3.7.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 (37) hide show
  1. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/PKG-INFO +2 -2
  2. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/documentation/main.md +3 -0
  3. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/pyproject.toml +1 -1
  4. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/requirements.txt +7 -7
  5. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/src/hdx/location/_version.py +2 -2
  6. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/src/hdx/location/adminlevel.py +29 -21
  7. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/hdx/location/test_adminlevel.py +6 -2
  8. hdx_python_country-3.7.4/src/hdx/location/names.py +0 -29
  9. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/.config/coveragerc +0 -0
  10. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/.config/pre-commit-config.yaml +0 -0
  11. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/.config/pytest.ini +0 -0
  12. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/.config/ruff.toml +0 -0
  13. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/.github/workflows/publish.yaml +0 -0
  14. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/.github/workflows/run-python-tests.yaml +0 -0
  15. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/.gitignore +0 -0
  16. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/CONTRIBUTING.md +0 -0
  17. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/LICENSE +0 -0
  18. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/README.md +0 -0
  19. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/documentation/.readthedocs.yaml +0 -0
  20. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/documentation/pydoc-markdown.yaml +0 -0
  21. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/src/hdx/location/Countries & Territories Taxonomy MVP - C&T Taxonomy with HXL Tags.csv +0 -0
  22. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/src/hdx/location/__init__.py +0 -0
  23. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/src/hdx/location/country.py +0 -0
  24. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/src/hdx/location/currency.py +0 -0
  25. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/src/hdx/location/phonetics.py +0 -0
  26. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/src/hdx/location/wfp_exchangerates.py +0 -0
  27. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/fixtures/adminlevel.yaml +0 -0
  28. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/fixtures/adminlevelparent.yaml +0 -0
  29. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/fixtures/global_pcode_lengths.csv +0 -0
  30. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/fixtures/global_pcodes_adm_1_2.csv +0 -0
  31. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/fixtures/secondary_historic_rates.csv +0 -0
  32. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/fixtures/secondary_rates.json +0 -0
  33. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/hdx/location/Countries_UZB_Deleted.csv +0 -0
  34. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/hdx/location/__init__.py +0 -0
  35. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/hdx/location/test_country.py +0 -0
  36. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/hdx/location/test_currency.py +0 -0
  37. {hdx_python_country-3.7.4 → hdx_python_country-3.7.6}/tests/hdx/location/test_wfp_exchangerates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: hdx-python-country
3
- Version: 3.7.4
3
+ Version: 3.7.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: 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.7.0
28
+ Requires-Dist: hdx-python-utilities>=3.7.2
29
29
  Requires-Dist: libhxl>=5.2.1
30
30
  Requires-Dist: pyphonetics
31
31
  Provides-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.7.5, removed clean_name function. There is now a function normalise in
46
+ HDX Python Utilities.
47
+
45
48
  From 3.5.5, after creating an Adminlevel, call either setup_from_admin_info,
46
49
  setup_from_libhxl_dataset or setup_from_url.
47
50
 
@@ -34,7 +34,7 @@ classifiers = [
34
34
  requires-python = ">=3.8"
35
35
 
36
36
  dependencies = [
37
- "hdx-python-utilities>=3.7.0",
37
+ "hdx-python-utilities>=3.7.2",
38
38
  "libhxl>=5.2.1",
39
39
  "pyphonetics",
40
40
  ]
@@ -8,7 +8,7 @@ attrs==23.2.0
8
8
  # jsonlines
9
9
  # jsonschema
10
10
  # referencing
11
- certifi==2024.6.2
11
+ certifi==2024.7.4
12
12
  # via requests
13
13
  cfgv==3.4.0
14
14
  # via pre-commit
@@ -28,11 +28,11 @@ filelock==3.15.4
28
28
  # via virtualenv
29
29
  frictionless==5.17.0
30
30
  # via hdx-python-utilities
31
- hdx-python-utilities==3.7.0
31
+ hdx-python-utilities==3.7.2
32
32
  # via hdx-python-country (pyproject.toml)
33
33
  humanize==4.9.0
34
34
  # via frictionless
35
- identify==2.5.36
35
+ identify==2.6.0
36
36
  # via pre-commit
37
37
  idna==3.7
38
38
  # via requests
@@ -68,7 +68,7 @@ mdurl==0.1.2
68
68
  # via markdown-it-py
69
69
  nodeenv==1.9.1
70
70
  # via pre-commit
71
- openpyxl==3.1.4
71
+ openpyxl==3.1.5
72
72
  # via hdx-python-utilities
73
73
  packaging==24.1
74
74
  # via pytest
@@ -84,9 +84,9 @@ ply==3.11
84
84
  # libhxl
85
85
  pre-commit==3.7.1
86
86
  # via hdx-python-country (pyproject.toml)
87
- pydantic==2.7.4
87
+ pydantic==2.8.2
88
88
  # via frictionless
89
- pydantic-core==2.18.4
89
+ pydantic-core==2.20.1
90
90
  # via pydantic
91
91
  pygments==2.18.0
92
92
  # via rich
@@ -171,7 +171,7 @@ urllib3==2.2.2
171
171
  # via
172
172
  # libhxl
173
173
  # requests
174
- validators==0.28.3
174
+ validators==0.30.0
175
175
  # via frictionless
176
176
  virtualenv==20.26.3
177
177
  # via pre-commit
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.7.4'
16
- __version_tuple__ = version_tuple = (3, 7, 4)
15
+ __version__ = version = '3.7.6'
16
+ __version_tuple__ = version_tuple = (3, 7, 6)
@@ -5,13 +5,11 @@ from typing import Any, Dict, List, Optional, Tuple
5
5
  import hxl
6
6
  from hxl import InputOptions
7
7
  from hxl.input import HXLIOException
8
- from unidecode import unidecode
9
8
 
10
9
  from hdx.location.country import Country
11
- from hdx.location.names import clean_name
12
10
  from hdx.location.phonetics import Phonetics
13
11
  from hdx.utilities.dictandlist import dict_of_sets_add
14
- from hdx.utilities.text import multiple_replace
12
+ from hdx.utilities.text import multiple_replace, normalise
15
13
  from hdx.utilities.typehint import ListTuple
16
14
 
17
15
  logger = logging.getLogger(__name__)
@@ -146,8 +144,9 @@ class AdminLevel:
146
144
  self.pcodes.append(pcode)
147
145
  self.pcode_to_name[pcode] = adm_name
148
146
 
147
+ adm_name = normalise(adm_name)
149
148
  name_to_pcode = self.name_to_pcode.get(countryiso3, {})
150
- name_to_pcode[unidecode(adm_name).lower()] = pcode
149
+ name_to_pcode[adm_name] = pcode
151
150
  self.name_to_pcode[countryiso3] = name_to_pcode
152
151
  self.pcode_to_iso3[pcode] = countryiso3
153
152
  self.pcode_to_iso3[pcode] = countryiso3
@@ -157,7 +156,7 @@ class AdminLevel:
157
156
  countryiso3, {}
158
157
  )
159
158
  name_to_pcode = name_parent_to_pcode.get(parent, {})
160
- name_to_pcode[unidecode(adm_name).lower()] = pcode
159
+ name_to_pcode[adm_name] = pcode
161
160
  name_parent_to_pcode[parent] = name_to_pcode
162
161
  self.name_parent_to_pcode[countryiso3] = name_parent_to_pcode
163
162
  self.pcode_to_parent[pcode] = parent
@@ -556,6 +555,7 @@ class AdminLevel:
556
555
  self,
557
556
  countryiso3: str,
558
557
  name: str,
558
+ normalised_name: str,
559
559
  **kwargs: Any,
560
560
  ) -> Optional[str]:
561
561
  """Fuzzy match name to pcode
@@ -563,6 +563,7 @@ class AdminLevel:
563
563
  Args:
564
564
  countryiso3 (str): ISO3 country code
565
565
  name (str): Name to match
566
+ normalised_name (str): Normalised name
566
567
  **kwargs:
567
568
  parent (Optional[str]): Parent admin code
568
569
  logname (str): Log using this identifying name. Defaults to not logging.
@@ -599,13 +600,12 @@ class AdminLevel:
599
600
  if logname:
600
601
  self.errors.add((logname, countryiso3, parent))
601
602
  return None
602
- adm_name_lookup = clean_name(name)
603
- adm_name_lookup2 = multiple_replace(
604
- adm_name_lookup,
603
+ alt_normalised_name = multiple_replace(
604
+ normalised_name,
605
605
  self.get_admin_name_replacements(countryiso3, parent),
606
606
  )
607
607
  pcode = name_to_pcode.get(
608
- adm_name_lookup, name_to_pcode.get(adm_name_lookup2)
608
+ normalised_name, name_to_pcode.get(alt_normalised_name)
609
609
  )
610
610
  if not pcode and name.lower() in self.admin_fuzzy_dont:
611
611
  if logname:
@@ -613,7 +613,7 @@ class AdminLevel:
613
613
  return None
614
614
  if not pcode:
615
615
  for map_name in name_to_pcode:
616
- if adm_name_lookup in map_name:
616
+ if normalised_name in map_name:
617
617
  pcode = name_to_pcode[map_name]
618
618
  if logname:
619
619
  self.matches.add(
@@ -627,7 +627,7 @@ class AdminLevel:
627
627
  )
628
628
  break
629
629
  for map_name in name_to_pcode:
630
- if adm_name_lookup2 in map_name:
630
+ if alt_normalised_name in map_name:
631
631
  pcode = name_to_pcode[map_name]
632
632
  if logname:
633
633
  self.matches.add(
@@ -642,24 +642,27 @@ class AdminLevel:
642
642
  break
643
643
  if not pcode:
644
644
  map_names = list(name_to_pcode.keys())
645
- lower_mapnames = [x.lower() for x in map_names]
646
645
 
647
646
  def al_transform_1(name):
648
- if name[:3] == "al ":
647
+ prefix = name[:3]
648
+ if prefix == "al ":
649
649
  return f"ad {name[3:]}"
650
+ elif prefix == "ad ":
651
+ return f"al {name[3:]}"
650
652
  else:
651
653
  return None
652
654
 
653
655
  def al_transform_2(name):
654
- if name[:3] == "al ":
656
+ prefix = name[:3]
657
+ if prefix == "al " or prefix == "ad ":
655
658
  return name[3:]
656
659
  else:
657
660
  return None
658
661
 
659
662
  matching_index = self.phonetics.match(
660
- lower_mapnames,
661
- adm_name_lookup,
662
- alternative_name=adm_name_lookup2,
663
+ map_names,
664
+ normalised_name,
665
+ alternative_name=alt_normalised_name,
663
666
  transform_possible_names=[al_transform_1, al_transform_2],
664
667
  )
665
668
 
@@ -714,6 +717,7 @@ class AdminLevel:
714
717
  countryiso3: str,
715
718
  name: str,
716
719
  fuzzy_match: bool = True,
720
+ fuzzy_length: int = 4,
717
721
  **kwargs: Any,
718
722
  ) -> Tuple[Optional[str], bool]:
719
723
  """Get pcode for a given name
@@ -722,6 +726,7 @@ class AdminLevel:
722
726
  countryiso3 (str): ISO3 country code
723
727
  name (str): Name to match
724
728
  fuzzy_match (bool): Whether to try fuzzy matching. Defaults to True.
729
+ fuzzy_length (int): Minimum length for fuzzy matching. Defaults to 4.
725
730
  **kwargs:
726
731
  parent (Optional[str]): Parent admin code
727
732
  logname (str): Log using this identifying name. Defaults to not logging.
@@ -751,6 +756,7 @@ class AdminLevel:
751
756
  )
752
757
  return pcode, True
753
758
  else:
759
+ normalised_name = normalise(name)
754
760
  if parent:
755
761
  name_parent_to_pcode = self.name_parent_to_pcode.get(
756
762
  countryiso3
@@ -758,18 +764,20 @@ class AdminLevel:
758
764
  if name_parent_to_pcode:
759
765
  name_to_pcode = name_parent_to_pcode.get(parent)
760
766
  if name_to_pcode is not None:
761
- pcode = name_to_pcode.get(name.lower())
767
+ pcode = name_to_pcode.get(normalised_name)
762
768
  if pcode:
763
769
  return pcode, True
764
770
  else:
765
771
  name_to_pcode = self.name_to_pcode.get(countryiso3)
766
772
  if name_to_pcode is not None:
767
- pcode = name_to_pcode.get(name.lower())
773
+ pcode = name_to_pcode.get(normalised_name)
768
774
  if pcode:
769
775
  return pcode, True
770
- if not fuzzy_match:
776
+ if not fuzzy_match or len(normalised_name) < fuzzy_length:
771
777
  return None, True
772
- pcode = self.fuzzy_pcode(countryiso3, name, **kwargs)
778
+ pcode = self.fuzzy_pcode(
779
+ countryiso3, name, normalised_name, **kwargs
780
+ )
773
781
  return pcode, False
774
782
 
775
783
  def output_matches(self) -> List[str]:
@@ -182,6 +182,10 @@ class TestAdminLevel:
182
182
  def test_adminlevel_fuzzy(self, config):
183
183
  adminone = AdminLevel(config)
184
184
  adminone.setup_from_admin_info(config["admin_info"])
185
+ assert adminone.get_pcode("YEM", "Al_Dhale'a", logname="test") == (
186
+ "YE30",
187
+ False,
188
+ )
185
189
  assert adminone.get_pcode("YEM", "Al Dali", logname="test") == (
186
190
  "YE30",
187
191
  False,
@@ -200,6 +204,7 @@ class TestAdminLevel:
200
204
  assert output == [
201
205
  "test - YEM: Matching (fuzzy) Al Dali to Ad Dali on map",
202
206
  "test - YEM: Matching (fuzzy) Al Dhale'e / الضالع to Ad Dali on map",
207
+ "test - YEM: Matching (fuzzy) Al_Dhale'a to Ad Dali on map",
203
208
  ]
204
209
 
205
210
  def test_adminlevel_parent(self, config_parent):
@@ -457,7 +462,7 @@ class TestAdminLevel:
457
462
  )
458
463
  assert adminone.get_pcode("YEM", "Ad Dali", logname="test") == (
459
464
  "YE30",
460
- False,
465
+ True,
461
466
  )
462
467
  assert adminone.get_pcode("YEM", "Ad Dal", logname="test") == (
463
468
  "YE30",
@@ -500,7 +505,6 @@ class TestAdminLevel:
500
505
  "test - NGA: Matching (pcode length conversion) NG015 to Federal Capital Territory on map",
501
506
  "test - UKR: Matching (substring) Chernihiv Oblast to Chernihivska on map",
502
507
  "test - YEM: Matching (substring) Ad Dal to Ad Dali' on map",
503
- "test - YEM: Matching (substring) Ad Dali to Ad Dali' on map",
504
508
  "test - YEM: Matching (pcode length conversion) YE30 to Ad Dali' on map",
505
509
  ]
506
510
  output = adminone.output_ignored()
@@ -1,29 +0,0 @@
1
- import re
2
- import unicodedata
3
-
4
- from unidecode import unidecode
5
-
6
- non_ascii = "([^\x00-\x7f])+"
7
-
8
-
9
- def clean_name(name: str) -> str:
10
- """
11
- Replace accented characters with non-accented ones in given name. Also
12
- strip whitespace from start and end and make lwoercase.
13
-
14
- Args:
15
- name (str): Name to change
16
-
17
- Returns:
18
- str: Name without accented characters
19
- """
20
- #
21
- clean_name = "".join(
22
- c
23
- for c in unicodedata.normalize("NFD", name)
24
- if unicodedata.category(c) != "Mn"
25
- )
26
- # Remove all non-ASCII characters
27
- clean_name = re.sub(non_ascii, " ", clean_name)
28
- clean_name = unidecode(clean_name)
29
- return clean_name.strip().lower()