Repository: openaddresses/machine
Pull request #764 (2020-02-29 16:44:39)

Support chain function in field_names_to_request
This is a newer copy of https://github.com/openaddresses/machine/pull/656


Patch:
diff --git a/openaddr/cache.py b/openaddr/cache.py
index a9fddec..fd56989 100644
--- a/openaddr/cache.py
+++ b/openaddr/cache.py
@@ -305,8 +305,24 @@ class EsriRestDownloadTask(DownloadTask):
 
         return os.path.join(dir_path, name_base + path_ext)
 
-    @staticmethod
-    def field_names_to_request(conform):
+    @classmethod
+    def fields_from_conform_function(cls, v):
+        fxn = v.get('function')
+        if fxn:
+            if fxn in ('join', 'format'):
+                return set(v['fields'])
+            elif fxn == 'chain':
+                fields = set()
+                user_vars = set([v['variable']])
+                for func in v['functions']:
+                    if isinstance(func, dict) and 'function' in func:
+                        fields |= cls.fields_from_conform_function(func) - user_vars
+                return fields
+            else:
+                return set([v.get('field')])
+
+    @classmethod
+    def field_names_to_request(cls, conform):
         ''' Return list of fieldnames to request based on conform, or None.
         '''
         if not conform:
@@ -318,10 +334,7 @@ class EsriRestDownloadTask(DownloadTask):
                 if isinstance(v, dict):
                     # It's a function of some sort?
                     if 'function' in v:
-                        if v['function'] in ('join', 'format'):
-                            fields |= set(v['fields'])
-                        else:
-                            fields.add(v.get('field'))
+                        fields |= cls.fields_from_conform_function(v)
                 elif isinstance(v, list):
                     # It's a list of field names
                     fields |= set(v)
diff --git a/openaddr/conform.py b/openaddr/conform.py
index 8991d10..1af28c0 100644
--- a/openaddr/conform.py
+++ b/openaddr/conform.py
@@ -1005,6 +1005,17 @@ def row_transform_and_convert(sd, row):
     row5 = row_calculate_hash(cache_fingerprint, row4)
     return row5
 
+def fxn_smash_case(fxn):
+    if "field" in fxn:
+        fxn["field"] = fxn["field"].lower()
+    if "fields" in fxn:
+        fxn["fields"] = [s.lower() for s in fxn["fields"]]
+    if "field_to_remove" in fxn:
+        fxn["field_to_remove"] = fxn["field_to_remove"].lower()
+    if "functions" in fxn:
+        for sub_fxn in fxn["functions"]:
+            fxn_smash_case(sub_fxn)
+
 def conform_smash_case(source_definition):
     "Convert all named fields in source_definition object to lowercase. Returns new object."
     new_sd = copy.deepcopy(source_definition)
@@ -1013,16 +1024,9 @@ def conform_smash_case(source_definition):
         if v not in (X_FIELDNAME, Y_FIELDNAME) and getattr(v, 'lower', None):
             conform[k] = v.lower()
         if type(conform[k]) is list:
-           conform[k] = [s.lower() for s in conform[k]]
+            conform[k] = [s.lower() for s in conform[k]]
         if type(conform[k]) is dict:
-            if "field" in conform[k]:
-                conform[k]["field"] = conform[k]["field"].lower()
-
-            if "fields" in conform[k]:
-                conform[k]["fields"] = [s.lower() for s in conform[k]["fields"]]
-
-            if "field_to_remove" in conform[k]:
-                conform[k]["field_to_remove"] = conform[k]["field_to_remove"].lower()
+            fxn_smash_case(conform[k])
 
             if "functions" in conform[k] and type(conform[k]["functions"]) is list:
                 for function in conform[k]["functions"]:


Test patch:
diff --git a/openaddr/tests/cache.py b/openaddr/tests/cache.py
index 84ef1e9..23da41c 100644
--- a/openaddr/tests/cache.py
+++ b/openaddr/tests/cache.py
@@ -131,3 +131,7 @@ class TestCacheEsriDownload (unittest.TestCase):
         conform9 = dict(street=['Number', 'Street'])
         fields9 = EsriRestDownloadTask.field_names_to_request(conform9)
         self.assertEqual(fields9, ['Number', 'Street'])
+
+        conform10 = dict(street=dict(function='chain', variable='foo', functions=[dict(function='postfixed_street', field='Street'), dict(function='remove_postfix', field='foo')]))
+        fields10 = EsriRestDownloadTask.field_names_to_request(conform10)
+        self.assertEqual(fields10, ['Street'])