Repository: lifeomic/phc-sdk-py

Issue:
Multiple addresses in column breaks frame expansion
Pulling out address breaks when multiple values are present. For example:
```
[{'state': 'NC', 'postalCode': '27540', 'period': {'start': '2001'}}, {'use': 'old', 'state': 'SC', 'period': {'start': '1999', 'end': '2001'}}]
```

Error:
```
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-6-0355439bcf07> in <module>
----> 1 phc.Patient.get_data_frame()

/opt/conda/lib/python3.7/site-packages/phc/easy/patients/__init__.py in get_data_frame(limit, all_results, raw, query_overrides, auth_args, ignore_cache, expand_args)
    101             query_overrides,
    102             auth_args,
--> 103             ignore_cache,
    104         )

/opt/conda/lib/python3.7/site-packages/phc/easy/query/__init__.py in execute_fhir_dsl_with_options(query, transform, all_results, raw, query_overrides, auth_args, ignore_cache)
    168             return df
    169 
--> 170         return transform(df)

/opt/conda/lib/python3.7/site-packages/phc/easy/patients/__init__.py in transform(df)
     92 
     93         def transform(df: pd.DataFrame):
---> 94             return Patient.transform_results(df, **expand_args)
     95 
     96         return Query.execute_fhir_dsl_with_options(

/opt/conda/lib/python3.7/site-packages/phc/easy/patients/__init__.py in transform_results(data_frame, **expand_args)
     32         }
     33 
---> 34         return Frame.expand(data_frame, **args)
     35 
     36     @staticmethod

/opt/conda/lib/python3.7/site-packages/phc/easy/frame.py in expand(frame, code_columns, date_columns, custom_columns)
     94             *[
     95                 column_to_frame(frame, key, func)
---> 96                 for key, func in custom_columns
     97             ],
     98             frame.drop([*codeable_col_names, *custom_names], axis=1),

/opt/conda/lib/python3.7/site-packages/phc/easy/frame.py in <listcomp>(.0)
     94             *[
     95                 column_to_frame(frame, key, func)
---> 96                 for key, func in custom_columns
     97             ],
     98             frame.drop([*codeable_col_names, *custom_names], axis=1),

/opt/conda/lib/python3.7/site-packages/phc/easy/frame.py in column_to_frame(frame, column_name, expand_func)
     29     "Converts a column (if exists) to a data frame with multiple columns"
     30     if column_name in frame.columns:
---> 31         return expand_func(frame[column_name])
     32 
     33     return pd.DataFrame([])

/opt/conda/lib/python3.7/site-packages/phc/easy/patients/address.py in expand_address_column(address_col)
     32 
     33 def expand_address_column(address_col):
---> 34     return pd.DataFrame(map(expand_address_value, address_col.values))

/opt/conda/lib/python3.7/site-packages/pandas/core/frame.py in __init__(self, data, index, columns, dtype, copy)
    467         elif isinstance(data, abc.Iterable) and not isinstance(data, (str, bytes)):
    468             if not isinstance(data, (abc.Sequence, ExtensionArray)):
--> 469                 data = list(data)
    470             if len(data) > 0:
    471                 if is_list_like(data[0]) and getattr(data[0], "ndim", 1) == 1:

/opt/conda/lib/python3.7/site-packages/phc/easy/patients/address.py in expand_address_value(value)
     22 
     23     # Value is always list of one item
---> 24     assert len(value) == 1
     25     value = value[0]
     26 

AssertionError:
```

Discussion:

Patch:
diff --git a/phc/easy/patients/address.py b/phc/easy/patients/address.py
index edbd1c8..c7af9a3 100644
--- a/phc/easy/patients/address.py
+++ b/phc/easy/patients/address.py
@@ -1,4 +1,6 @@
 import pandas as pd
+
+from funcy import first
 from phc.easy.codeable import generic_codeable_to_dict
 from phc.easy.util import concat_dicts
 
@@ -22,18 +24,29 @@ def expand_address_value(value):
     if type(value) is not list:
         return {}
 
-    # Value is always list of one item
-    assert len(value) == 1
-    value = value[0]
+    primary_address = first(
+        filter(lambda v: v.get("use") != "old", value)
+    ) or first(value)
+
+    other_addresses = list(
+        filter(lambda address: address != primary_address, value)
+    )
 
-    return concat_dicts(
-        [
-            expand_address_attr(f"address_{key}", item_value)
-            for key, item_value in value.items()
-            if key != "text"
-        ]
+    other_attrs = (
+        {"other_addresses": other_addresses} if len(other_addresses) > 0 else {}
     )
 
+    return {
+        **concat_dicts(
+            [
+                expand_address_attr(f"address_{key}", item_value)
+                for key, item_value in primary_address.items()
+                if key != "text"
+            ]
+        ),
+        **other_attrs,
+    }
+
 
 def expand_address_column(address_col):
     return pd.DataFrame(map(expand_address_value, address_col.values))


Test patch:
diff --git a/tests/test_easy_patient_address.py b/tests/test_easy_patient_address.py
index d51ff86..42bb1cd 100644
--- a/tests/test_easy_patient_address.py
+++ b/tests/test_easy_patient_address.py
@@ -1,18 +1,64 @@
 import pandas as pd
+import math
 from phc.easy.patients.address import expand_address_column
 
+
+def non_na_dict(dictionary: dict):
+    return {
+        k: v
+        for k, v in dictionary.items()
+        if not isinstance(v, float) or not math.isnan(v)
+    }
+
+
 def test_expand_address_column():
-    sample = pd.DataFrame([{
-        'address': [
-            {'line': ['123 ABC Court'], 'city': 'Zionsville', 'state': 'IN', 'use': 'home'}
+    sample = pd.DataFrame(
+        [
+            {
+                "address": [
+                    {
+                        "line": ["123 ABC Court"],
+                        "city": "Zionsville",
+                        "state": "IN",
+                        "use": "home",
+                    }
+                ]
+            },
+            {
+                "address": [
+                    {
+                        "use": "old",
+                        "state": "SC",
+                        "period": {"start": "1999", "end": "2001"},
+                    },
+                    {
+                        "state": "NC",
+                        "city": "Raleigh",
+                        "period": {"start": "2001"},
+                    },
+                ]
+            },
         ]
-    }])
+    )
 
     df = expand_address_column(sample.address)
 
-    assert df.iloc[0].to_dict() == {
-        'address_line_0': '123 ABC Court',
-        'address_city': 'Zionsville',
-        'address_state': 'IN',
-        'address_use': 'home'
+    assert non_na_dict(df.iloc[0].to_dict()) == {
+        "address_line_0": "123 ABC Court",
+        "address_city": "Zionsville",
+        "address_state": "IN",
+        "address_use": "home",
+    }
+
+    assert non_na_dict(df.iloc[1].to_dict()) == {
+        "address_city": "Raleigh",
+        "address_state": "NC",
+        "address_period_start": "2001",
+        "other_addresses": [
+            {
+                "use": "old",
+                "state": "SC",
+                "period": {"start": "1999", "end": "2001"},
+            }
+        ],
     }