Repository: OCHA-DAP/hdx-python-country

Issue:
Uee local file for live data rather than url
As a follow on from https://github.com/OCHA-DAP/hdx-python-country/issues/57, we've been looking into implementing this but as it stands the live data feed can only use a url and not a local file. This works but is a bit awkward. Would you be open to extending this to be able to provide the path to a local, custom file to read the live data from? If so, I would be willing to do a PR for it.

Hints from issue comments:
@mcarans: @andy-isoc Thank you for your interest. I would be happy to review a PR for this change.@mcarans: @andy-isoc  Please confirm 3.8.4 resolves this issue.@andy-isoc: This works as expected and fulfills our use case. Many thanks for the prompt review/merge/publish!

Solution patch:
diff --git a/requirements.txt b/requirements.txt
index a5e5efb..a44eb5c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,7 +18,7 @@ charset-normalizer==3.4.0
     # via requests
 click==8.1.7
     # via typer
-coverage==7.6.4
+coverage==7.6.5
     # via pytest-cov
 distlib==0.3.9
     # via virtualenv
@@ -32,7 +32,7 @@ hdx-python-utilities==3.7.4
     # via hdx-python-country (pyproject.toml)
 humanize==4.11.0
     # via frictionless
-identify==2.6.1
+identify==2.6.2
     # via pre-commit
 idna==3.10
     # via requests
@@ -70,12 +70,10 @@ nodeenv==1.9.1
     # via pre-commit
 openpyxl==3.1.5
     # via hdx-python-utilities
-packaging==24.1
+packaging==24.2
     # via pytest
 petl==1.7.15
     # via frictionless
-pip==24.3.1
-    # via simpleeval
 platformdirs==4.3.6
     # via virtualenv
 pluggy==1.5.0
@@ -129,9 +127,9 @@ requests-file==2.1.0
     # via hdx-python-utilities
 rfc3986==2.0.0
     # via frictionless
-rich==13.9.3
+rich==13.9.4
     # via typer
-rpds-py==0.20.0
+rpds-py==0.21.0
     # via
     #   jsonschema
     #   referencing
@@ -141,7 +139,7 @@ ruamel-yaml-clib==0.2.12
     # via ruamel-yaml
 shellingham==1.5.4
     # via typer
-simpleeval==1.0.1
+simpleeval==1.0.3
     # via frictionless
 six==1.16.0
     # via python-dateutil
@@ -157,7 +155,7 @@ tenacity==9.0.0
     # via hdx-python-country (pyproject.toml)
 text-unidecode==1.3
     # via python-slugify
-typer==0.12.5
+typer==0.13.0
     # via frictionless
 typing-extensions==4.12.2
     # via
@@ -177,7 +175,7 @@ validators==0.34.0
     # via frictionless
 virtualenv==20.27.1
     # via pre-commit
-wheel==0.44.0
+wheel==0.45.0
     # via libhxl
 xlrd==2.0.1
     # via hdx-python-utilities
diff --git a/src/hdx/location/country.py b/src/hdx/location/country.py
index 74e1eaf..ed1ec26 100755
--- a/src/hdx/location/country.py
+++ b/src/hdx/location/country.py
@@ -2,6 +2,7 @@
 
 import copy
 import logging
+import os.path
 import re
 from string import punctuation
 from typing import Dict, List, Optional, Tuple, Union
@@ -73,6 +74,7 @@ class Country:
     _countriesdata = None
     _ochaurl_default = "https://docs.google.com/spreadsheets/d/1NjSI2LaS3SqbgYc0HdD8oIb7lofGtiHgoKKATCpwVdY/export?format=csv&gid=1088874596"
     _ochaurl = _ochaurl_default
+    _ochapath = None
     _country_name_overrides = {}
     _country_name_mappings = {}
 
@@ -237,11 +239,14 @@ def countriesdata(
                         "Download from OCHA feed failed! Falling back to stored file."
                     )
             if countries is None:
+                file_path = script_dir_plus_file(
+                    "Countries & Territories Taxonomy MVP - C&T Taxonomy with HXL Tags.csv",
+                    CountryError,
+                )
+                if cls._ochapath:
+                    file_path = cls._ochapath
                 countries = hxl.data(
-                    script_dir_plus_file(
-                        "Countries & Territories Taxonomy MVP - C&T Taxonomy with HXL Tags.csv",
-                        Country,
-                    ),
+                    file_path,
                     InputOptions(allow_local=True, encoding="utf-8"),
                 )
             cls.set_countriesdata(countries)
@@ -278,6 +283,21 @@ def set_ocha_url(cls, url: Optional[str] = None) -> None:
             url = cls._ochaurl_default
         cls._ochaurl = url
 
+    @classmethod
+    def set_ocha_path(cls, path: Optional[str] = None) -> None:
+        """
+        Set local path from which to retrieve OCHA countries data
+
+        Args:
+            path (Optional[str]): Local path from which to retrieve countries data. Defaults to None.
+
+        Returns:
+            None
+        """
+        if path and not os.path.exists(path):
+            path = None
+        cls._ochapath = path
+
     @classmethod
     def set_country_name_overrides(cls, country_name_overrides: Dict) -> None:
         """


Test patch:
diff --git a/tests/hdx/location/test_country.py b/tests/hdx/location/test_country.py
index df253f3..ad4215a 100755
--- a/tests/hdx/location/test_country.py
+++ b/tests/hdx/location/test_country.py
@@ -716,3 +716,19 @@ def test_ocha_feed_file_working(self):
         Country.set_ocha_url("NOTEXIST")
         Country._countriesdata = None
         assert Country.get_iso3_from_iso2("AF") == "AFG"
+
+    def test_ocha_feed_local_file_working(self):
+        Country._countriesdata = None
+        Country.set_ocha_path(
+            script_dir_plus_file("Countries_UZB_Deleted.csv", TestCountry)
+        )
+        assert (
+            Country.get_iso3_country_code("UZBEKISTAN", use_live=False) is None
+        )
+
+        Country._countriesdata = None
+        Country.set_ocha_path()
+        assert (
+            Country.get_iso3_country_code("UZBEKISTAN", use_live=False)
+            == "UZB"
+        )