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.

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/70909274/badge)](https://coveralls.io/builds/70909274)

coverage: 97.942% (-0.07%) from 98.008%
when pulling **522137ed269b50dc171dfe419b67f9668df0b487 on andy-isoc:load_countries_from_custom_local_file**
into **9c1759d23d952ff3e5755bc83ba052b1375ad809 on OCHA-DAP:main**.

andy-isoc: I did think about that approach initially but I was concerned that it would be a little confusing as the naming `set_ocha_url()` wouldn't reflect the usage (if you pass in a *path*). Also I think we'd still have to set `use_live=True` in that scenario, wouldn't we? Again, that doesn't quite feel right to me as if we're loading from static local data it's not really "live" data.

But happy to change it round to that approach if you feel it's better.
mcarans: Good point about `use_live`. I'll merge your PR. Thanks for making it.

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 @@ class Country:
                         "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 @@ class Country:
             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 @@ class TestCountry:
         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"
+        )