From 4f37d265c8227c74ab271c30296d9888277de7fe Mon Sep 17 00:00:00 2001 From: Salfiii <45657297+Salfiii@users.noreply.github.com> Date: Tue, 30 Apr 2019 02:06:10 +0200 Subject: [PATCH] "nested_alter" and update of "nested_update" (#16) * Update lookup_api.py moved the typecheck of the value argument to the wrapper function. * added tests for nested_update Test * pep8 format standards moded _call_callback out of the nested_alter Method Raised an Exception is no list is provided but treas_as_elemenet is false treat_list_as_element:bool renamed to treat_as_element:bool and set default to True * Auto commit via script * Create CONTRIBUTORS.rst * Rename CONTRIBUTORS.rst to CONTRIBUTING.rst * Update CONTRIBUTING.rst * Fix for issue #17 (#18) * Fix for issue #17 * Updating version (and) Adding Travis CI support * Adding Build status to README * changed the type annotation of the input document (removed it) removed the output type hint change the Readme * Update lookup_api.py moved the typecheck of the value argument to the wrapper function. * added tests for nested_update Test * pep8 format standards moded _call_callback out of the nested_alter Method Raised an Exception is no list is provided but treas_as_elemenet is false treat_list_as_element:bool renamed to treat_as_element:bool and set default to True * changed the type annotation of the input document (removed it) removed the output type hint change the Readme --- .gitignore | 4 +- README.rst | 32 +++ nested_lookup/__init__.py | 2 +- nested_lookup/lookup_api.py | 184 +++++++++++- test_lookup_api.py | 539 +++++++++++++++++++++++++++++++----- 5 files changed, 670 insertions(+), 91 deletions(-) diff --git a/.gitignore b/.gitignore index 7ebb9bf..b53d4da 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ __pycache__* build/* dist/* nested_lookup.egg-info/* -.cache/ +.cache +.vscode +git_commit.sh \ No newline at end of file diff --git a/README.rst b/README.rst index e5a4922..56d2053 100644 --- a/README.rst +++ b/README.rst @@ -24,6 +24,11 @@ A document in this case is a a mixture of Python dictionary and list objects typ Given a document, find all occurrences of the given key and delete it. By default, returns a copy of the document. To mutate the original specify the `in_place=True` argument. + +*nested_alter:* + Given a document, find all occurrences of the given key and alter it with a callback function + By default, returns a copy of the document. + To mutate the original specify the `in_place=True` argument. *get_all_keys:* Fetch all keys from a deeply nested dictionary. @@ -75,6 +80,31 @@ quick tutorial >>> nested_delete(document, 'taco') [{}, {'salsa': [{'burrito': {}}]}] + + +*Nested Alter*: +write a callback function which processes a scalar value. +Be aware about the possible types which can be passed to the callback functions. +In this example we can be sure that only int will be passed, in production you should check the type because it could be anything. + +.. code-block:: python + +>>> def callback(data): +>>> return data + 10 # add 10 to every taco prize + +The alter-version only works for scalar input (one dict), if you need to adress a list of dicts, you have to +manually iterate over those and pass them to nested_update one by one + +.. code-block:: python + +>>> out =[] +>>> for elem in document: +>>> altered_document = nested_alter(elem,"taco", callback) +>>> out.append(altered_document) + +>>> print(out) +[ { 'taco' : 52 } , { 'salsa' : [ { 'burrito' : { 'taco' : 79 } } ] } ] + >>> from nested_lookup import get_all_keys >>> get_all_keys(document) @@ -210,8 +240,10 @@ misc * Russell Ballestrini * Douglas Miranda * Ramesh RV + * Salfiii (Florian S.) :web: * http://russell.ballestrini.net * http://douglasmiranda.com * https://gist.github.com/douglasmiranda/5127251 + * https://github.com/Salfiii diff --git a/nested_lookup/__init__.py b/nested_lookup/__init__.py index ba135f6..c1a964a 100644 --- a/nested_lookup/__init__.py +++ b/nested_lookup/__init__.py @@ -1,3 +1,3 @@ from .nested_lookup import nested_lookup, get_all_keys, get_occurrence_of_key,\ get_occurrence_of_value -from .lookup_api import nested_update, nested_delete +from .lookup_api import nested_update, nested_delete, nested_alter diff --git a/nested_lookup/lookup_api.py b/nested_lookup/lookup_api.py index 1d8f898..282bc45 100644 --- a/nested_lookup/lookup_api.py +++ b/nested_lookup/lookup_api.py @@ -1,14 +1,16 @@ import copy +import warnings from six import iteritems +from nested_lookup import nested_lookup -def nested_delete(document, key, in_place=False): +def nested_delete(document, key: str, in_place: bool = False): if not in_place: document = copy.deepcopy(document) return _nested_delete(document=document, key=key) -def _nested_delete(document, key): +def _nested_delete(document, key: str) -> dict: """ Method to delete a key->value pair from a nested document Args: @@ -22,35 +24,187 @@ def _nested_delete(document, key): for list_items in document: _nested_delete(document=list_items, key=key) elif isinstance(document, dict): - if document.get(key) is not None: + if document.get(key): del document[key] for dict_key, dict_value in iteritems(document): _nested_delete(document=dict_value, key=key) return document -def nested_update(document, key, value, in_place=False): - if not in_place: - document = copy.deepcopy(document) - return _nested_update(document=document, key=key, value=value) - - -def _nested_update(document, key, value): +def nested_update(document, key: str, value: object, + in_place: bool = False, + treat_as_element: bool = True): """ Method to update a key->value pair in a nested document Args: document: Might be List of Dicts (or) Dict of Lists (or) - Dict of List of Dicts etc... + Dict of List of Dicts etc... key: Key to update the value + value: Value to set + in_place (bool): + True: modify the dict in place; + False: create a deep copy of the dict and modify it + Defaults to False + treat_list_element (bool): + True: if a list is provided as "value", the function trys + to match the list elements to the occurences of the key. + If the key occures more often than the provided list has + elements, the first element gets recycled. + False: the provided list is treated as one scalar value and + will be set as value to every key that matches. + Defaults to True (because of backwards portability of the package). + Return: + Returns a document that has updated key, value pair. + """ + + # check if a list or scalar value is provided and create a list + # from the scalar value + # check the length of the list and provide it to _nested_update + if not treat_as_element and not isinstance(value, list): + raise Exception('You need to pass value as list if you opt for' + + 'this feature') + elif treat_as_element: + value = [value] + + val_len = len(value) + + if not in_place: + document = copy.deepcopy(document) + return _nested_update(document=document, key=key, value=value, + val_len=val_len) + + +def _nested_update(document, key: str, value: object, + val_len: int, run: int = 0): + """ + Method to update a key->value pair in a nested document + Args: + document: Might be List of Dicts (or) Dict of Lists (or) + Dict of List of Dicts etc... + key (str): Key to update the value + value (list): value(s) which should be used for replacement purpouse + val_len (int): lenght of the value element + run (int): holds the number of findings for the given key. + Every time the key is found, run = run + 1. If the list value[run] + exists, + the corresponding element is used for replacement purpouse. + Defaults to 0. Return: Returns a document that has updated key, value pair. """ if isinstance(document, list): for list_items in document: - _nested_update(document=list_items, key=key, value=value) + _nested_update(document=list_items, key=key, value=value, + val_len=val_len, run=run) elif isinstance(document, dict): - if document.get(key) is not None: - document[key] = value + if document.get(key): + # check if a value with the coresponding index exists and + # use it otherwise recycle the intially given value + if run < val_len: + val = value[run] + else: + run = 0 + val = value[run] + document[key] = val + run = run + 1 for dict_key, dict_value in iteritems(document): - _nested_update(document=dict_value, key=key, value=value) + _nested_update(document=dict_value, key=key, value=value, + val_len=val_len, run=run) + return document + + +def nested_alter(document, key: str, callback_function=None, + function_parameters: list = None, conversion_function=None, + wild_alter: bool = False, in_place: bool = True): + """ + Method to alter all values of the occurences of the key "key". + The provided callback_function is used to alter the scalar values + Args: + document: Might be List of Dicts (or) Dict of Lists (or) + Dict of List of Dicts etc... + key: Key to update the value + callback_function :A callback function which alters a scalar value + HINT: You should be aware that not every element might be of + the same type, please check this in your function! + function_parameters (list): + If the callback_function has additional input arguments except + the scalar value, please specify those in this list. + conversion_function: A conversion function like str() which should be + applied to every found value before it is passed to the + "callback_function" + wild_alter: Find matching elements via wild-match by the given keys + and alter those. + HINT: Keep in mind that the wild-match might return unexpected types! + in_place (bool): + True: modify the dict in place; + False: create a deep copy of the dict and modify it + Defaults to False + Return: + Returns a document that has updated key, value pair. + """ + # check if a list or scalar value is provided and create a list from + # the scalar value + # check the length of the list and provide it to _nested_update + if isinstance(key, list): + key_len = len(key) + else: + key = [key] + key_len = len(key) + + if not in_place: + document = copy.deepcopy(document) + return _nested_alter(document=document, keys=key, + callback_function=callback_function, + function_parameters=function_parameters, + conversion_function=conversion_function, + wild_alter=wild_alter, + in_place=in_place, key_len=key_len) + + +def _call_callback(value_list: list, callback_function, + function_parameters: list, conversion_function): + """ + internal helper to call the callback function + """ + return_list = [] + # loop over all values + for value in value_list: + # apply the conversion function + if conversion_function is not None: + value = conversion_function(value) + # if functions arguments are present, expand the list to variables + # via the magic operator * + if function_parameters: + trans_val = callback_function(value, *function_parameters) + else: + trans_val = callback_function(value) + # append the transformed element to the list + return_list.append(trans_val) + return return_list + + +def _nested_alter(document, keys, callback_function, + function_parameters: list, conversion_function, + wild_alter: bool, in_place: bool, key_len: int): + """ + """ + # return data if no callback_function is provided + if callback_function is None: + warnings.warn("Please provide a callback_function to nested_alter().") + return document + + # iterate over all given keys in the list + for key in keys: + # try to find the key: + findings = nested_lookup(key, document, with_keys=True, wild=wild_alter) + for k, v in findings.items(): + trans_val = _call_callback(v, callback_function, + function_parameters, + conversion_function) + # use the transformed value and apply the update to the key + # (dont treat the lists as elements here) + document = nested_update(document, k, trans_val, + in_place=in_place, + treat_as_element=False) + return document diff --git a/test_lookup_api.py b/test_lookup_api.py index 6f29eb4..ad751a1 100644 --- a/test_lookup_api.py +++ b/test_lookup_api.py @@ -1,6 +1,7 @@ from unittest import TestCase -from nested_lookup import nested_update, nested_delete +from nested_lookup import nested_lookup, nested_update +from nested_lookup import nested_delete, nested_alter class BaseLookUpApi(TestCase): @@ -56,11 +57,51 @@ class BaseLookUpApi(TestCase): } self.sample_data4 = { - "hardware_details": { - "model_name": 'MacBook Pro', - "total_number_of_cores": 0, - "memory": False - } + "modelversion": "1.1.0", + "vorgangsID": "1", + "versorgungsvorschlagDatum": 1510558834978, + "eingangsdatum": 1510558834978, + "plz": 82269, + "vertragsteile": [ + { + "typ": "1", + "beitragsDaten": { + "endalter": 85, + "brutto": 58.76, + "netto": 58.76, + "zahlungsrhythmus": "MONATLICH", + "plz": 86899 + }, + "beginn": 1512082800000, + "lebenslang": "True", + "ueberschussverwendung": { + "ueberschussverwendung": "2", + "indexoption": "3" + }, + "deckung": [ + { + "typ": "2", + "art": "1", + "leistung": { + "value": 7500242424.0, + "einheit": "2" + }, + "leistungsRhythmus": "1" + } + ], + "zuschlagNachlass": [] + }, + { + "typ": "1", + "beitragsDaten": { + "endalter": 85, + "brutto": 0.6, + "netto": 0.6, + "zahlungsrhythmus": "1" + }, + "zuschlagNachlass": [] + } + ] } @@ -95,52 +136,9 @@ class TestNestedDelete(BaseLookUpApi): result, nested_delete(self.sample_data3, 'monitoring_zones') ) - def test_sample_data4(self): - result1 = { - "hardware_details": { - "model_name": 'MacBook Pro', - "memory": False - } - } - self.assertEqual( - result1, nested_delete(self.sample_data4, 'total_number_of_cores') - ) - result2 = { - "hardware_details": { - "model_name": 'MacBook Pro', - "total_number_of_cores": 0 - } - } - self.assertEqual( - result2, nested_delete(self.sample_data4, 'memory') - ) - - def test_nested_delete_in_place_false(self): - """ - nested_delete with in_place argument set to 'False' - should mutate and return a copy of the original document - """ - before_id = id(self.sample_data1) - result = nested_delete( - self.sample_data1, 'build_version', in_place=False) - after_id = id(result) - # the object ids should _not_ match. - self.assertNotEqual(before_id, after_id) - - def test_nested_delete_in_place_true(self): - """ - nested_delete with in_place argument set to 'True' - should mutate and return the original document - """ - before_id = id(self.sample_data1) - result = nested_delete( - self.sample_data1, 'build_version', in_place=True) - after_id = id(result) - # the object ids should match. - self.assertEqual(before_id, after_id) - class TestNestedUpdate(BaseLookUpApi): + def test_sample_data1(self): result = { "build_version": "Test1", @@ -155,22 +153,39 @@ class TestNestedUpdate(BaseLookUpApi): result, nested_update(self.sample_data1, 'build_version', 'Test1') ) + def test_sample_data1_list_input_treat_list_as_element_true(self): + result = { + "build_version": ["Test5", "Test6", "Test7"], + "os_details": { + "product_version": '10.13.6', + "build_version": ["Test5", "Test6", "Test7"] + }, + "name": 'Test', + "date": 'YYYY-MM-DD HH:MM:SS' + } + + self.assertEqual( + result, nested_update( + self.sample_data1, + 'build_version', + ["Test5", "Test6", "Test7"], + treat_as_element=True) + ) + def test_nested_update_in_place_false(self): """ - nested_update with in_place argument set to 'False' - should mutate and return a copy of the original document + ested_update should mutate and return a copy of the original document """ before_id = id(self.sample_data1) - result = nested_update( - self.sample_data1, 'build_version', 'Test2', in_place=False) + result = nested_update(self.sample_data1, 'build_version', 'Test2', + in_place=False) after_id = id(result) # the object ids should _not_ match. self.assertNotEqual(before_id, after_id) def test_nested_update_in_place_true(self): """ - nested_update with in_place argument set to 'True' - should mutate and return the original document + nested_update should mutate and return the original document """ before_id = id(self.sample_data1) result = nested_update( @@ -179,6 +194,146 @@ class TestNestedUpdate(BaseLookUpApi): # the object ids should match. self.assertEqual(before_id, after_id) + def test_nested_update_in_place_true_list_input(self): + doc = self.sample_data4 + # get all instances of the given element + findings = nested_lookup("plz", doc, False, True) + # alter those instances + updated_findings = list() + for key, val in findings.items(): + for elem in val: + updated_findings.append(elem + 300) + # update those instances with the altered results + doc_updated = nested_update( + doc, "plz", updated_findings, treat_as_element=False) + elem1 = doc_updated["plz"] # 85269 + # 87199 + elem2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + self.assertEqual(elem1, 82569) + self.assertEqual(elem2, 87199) + + def test_nested_update_in_place_false_list_input(self): + doc = self.sample_data4 + # get all instances of the given element + findings = nested_lookup("plz", doc, False, True) + # alter those instances + updated_findings = list() + for key, val in findings.items(): + for elem in val: + updated_findings.append(elem + 300) + # update those instances with the altered results + doc_updated = nested_update( + doc, "plz", updated_findings, in_place=False, + treat_as_element=False) + elem1 = doc_updated["plz"] + elem2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + self.assertEqual(elem1, 82569) + self.assertEqual(elem2, 87199) + + def test_nested_update_in_place_false_list_input_as_element_false(self): + doc = self.sample_data4 + # get all instances of the given element + list_input = [1, 2, 3, 4, 5] + # update those instances with the altered results + doc_updated = nested_update( + doc, "plz", list_input, in_place=False, treat_as_element=False) + elem1 = doc_updated["plz"] + elem2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + # should not work without specifying "treat_list_as_element = True" + # in nested_update + self.assertNotEqual(elem1, list_input) + self.assertNotEqual(elem2, list_input) + + def test_nested_update_in_place_false_list_input_as_element_true(self): + doc = self.sample_data4 + # get all instances of the given element + list_input = [1, 2, 3, 4, 5] + # update those instances with the altered results + doc_updated = nested_update( + doc, "plz", list_input, in_place=False, treat_as_element=True) + elem1 = doc_updated["plz"] + elem2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + # should not work without specifying "treat_list_as_element = True" + # in nested_update + self.assertEqual(elem1, list_input) + self.assertEqual(elem2, list_input) + + def test_nested_update_in_place_true_list_input_as_element_false(self): + doc = self.sample_data4 + # get all instances of the given element + list_input = [1, 2, 3, 4, 5] + # update those instances with the altered results + doc_updated = nested_update( + doc, "plz", list_input, in_place=True, treat_as_element=False) + elem1 = doc_updated["plz"] + elem2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + # should not work without specifying "treat_list_as_element = True" + # in nested_update + self.assertNotEqual(elem1, list_input) + self.assertNotEqual(elem2, list_input) + + def test_nested_update_in_place_true_list_input_as_element_true(self): + doc = self.sample_data4 + # get all instances of the given element + list_input = [1, 2, 3, 4, 5] + # update those instances with the altered results + doc_updated = nested_update( + doc, "plz", list_input, in_place=True, treat_as_element=True) + elem1 = doc_updated["plz"] + elem2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + # should not work without specifying "treat_list_as_element = True" + # in nested_update + self.assertEqual(elem1, list_input) + self.assertEqual(elem2, list_input) + + def test_nested_delete_in_place_false(self): + """ + nested_delete should mutate and return a copy of the original document + """ + before_id = id(self.sample_data1) + result = nested_delete( + self.sample_data1, 'build_version', in_place=False) + after_id = id(result) + # the object ids should _not_ match. + self.assertNotEqual(before_id, after_id) + + def test_nested_delete_in_place_true(self): + """nested_delete should mutate and return the original document""" + before_id = id(self.sample_data1) + result = nested_delete( + self.sample_data1, 'build_version', in_place=True) + after_id = id(result) + # the object ids should match. + self.assertEqual(before_id, after_id) + + def test_nested_update_taco_for_example(self): + document = [ + {'taco': 42}, + {'salsa': [{'burrito': {'taco': 69}}]} + ] + + updated_document = nested_update( + document, "taco", [100, 200], treat_as_element=False) + + self.assertEqual(updated_document[0]["taco"], 100) + # The multi-update version only works for scalar input, + # if you need to adress a list of dicts, you have to + # manually iterate over those and pass them to nested_update + # one by one + self.assertNotEqual( + updated_document[1]["salsa"][0]["burrito"]["taco"], 200) + + def test_nested_update_raise_error(self): + doc = self.sample_data4 + # get all instances of the given element + list_input = 1 + # update those instances with the altered results + self.assertRaises( + Exception, + nested_update, doc, "plz", list_input, + in_place=True, treat_as_element=False + ) + def test_sample_data2(self): result = { "hardware_details": { @@ -217,28 +372,264 @@ class TestNestedUpdate(BaseLookUpApi): ) def test_sample_data4(self): - result1 = { - "hardware_details": { - "model_name": 'MacBook Pro', - "total_number_of_cores": 1, - "memory": False - } + result = { + "modelversion": { + 'key1': ['value1'], + 'key2': 'value2' + }, + "vorgangsID": "1", + "versorgungsvorschlagDatum": 1510558834978, + "eingangsdatum": 1510558834978, + "plz": 82269, + "vertragsteile": [ + { + "typ": "1", + "beitragsDaten": { + "endalter": 85, + "brutto": 58.76, + "netto": 58.76, + "zahlungsrhythmus": "MONATLICH", + "plz": 86899 + }, + "beginn": 1512082800000, + "lebenslang": "True", + "ueberschussverwendung": { + "ueberschussverwendung": "2", + "indexoption": "3" + }, + "deckung": [ + { + "typ": "2", + "art": "1", + "leistung": { + "value": 7500242424.0, + "einheit": "2" + }, + "leistungsRhythmus": "1" + } + ], + "zuschlagNachlass": [] + }, + { + "typ": "1", + "beitragsDaten": { + "endalter": 85, + "brutto": 0.6, + "netto": 0.6, + "zahlungsrhythmus": "1" + }, + "zuschlagNachlass": [] + } + ] } self.assertEqual( - result1, nested_update( - self.sample_data4, key='total_number_of_cores', - value=1 + result, nested_update( + self.sample_data4, 'modelversion', + { + 'key1': ['value1'], + 'key2': 'value2' + } ) ) - result2 = { - "hardware_details": { - "model_name": 'MacBook Pro', - "total_number_of_cores": 0, - "memory": True - } + + +class TestNestedAlter(BaseLookUpApi): + + def test_nested_alter_in_place_true(self): + + # callback functions + def callback(data): + return str(data) + "###" + + doc_updated = nested_alter( + self.sample_data4, "vorgangsID", callback, in_place=True) + + vorgangsid = doc_updated["vorgangsID"] + + self.assertEqual(vorgangsid, "1###") + + def test_nested_alter_in_place_false(self): + + # callback functions + def callback(data): + return str(data) + "###" + + doc_updated = nested_alter( + self.sample_data4, "vorgangsID", callback, in_place=False) + + vorgangsid = doc_updated["vorgangsID"] + + # should not work without specifying + # "treat_list_as_element = True" in nested_update + self.assertEqual(vorgangsid, "1###") + + def test_nested_alter_list_input_in_place_true(self): + + # callback functions + def callback(data): + return str(data) + "###" + + doc_updated = nested_alter( + self.sample_data4, ["plz", "vorgangsID"], callback, in_place=True) + + plz1 = doc_updated["plz"] + plz2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + vorgangsid = doc_updated["vorgangsID"] + + # should not work without specifying + # "treat_list_as_element = True" in nested_update + self.assertEqual(plz1, "82269###") + self.assertEqual(plz2, "86899###") + self.assertEqual(vorgangsid, "1###") + + def test_nested_alter_list_input_with_args_in_place_true(self): + + # callback functions + def callback(data, str1, str2): + return str(data) + str1 + str2 + + doc_updated = nested_alter(self.sample_data4, [ + "plz", "vorgangsID"], + callback, + function_parameters=["abc", "def"], + in_place=True + ) + + plz1 = doc_updated["plz"] + plz2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + vorgangsid = doc_updated["vorgangsID"] + + # should not work without specifying + # "treat_list_as_element = True" in nested_update + self.assertEqual(plz1, "82269abcdef") + self.assertEqual(plz2, "86899abcdef") + self.assertEqual(vorgangsid, "1abcdef") + + def test_nested_alter_list_input_with_args_in_place_false(self): + + # callback functions + def callback(data, str1, str2): + return str(data) + str1 + str2 + + doc_updated = nested_alter(self.sample_data4, [ + "plz", "vorgangsID"], + callback, + function_parameters=["abc", "def"], + in_place=False + ) + + plz1 = doc_updated["plz"] + plz2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + vorgangsid = doc_updated["vorgangsID"] + + # should not work without specifying + # "treat_list_as_element = True" in nested_update + self.assertEqual(plz1, "82269abcdef") + self.assertEqual(plz2, "86899abcdef") + self.assertEqual(vorgangsid, "1abcdef") + + def test_nested_alter_list_input_in_place_false(self): + + # callback functions + def callback(data): + return str(data) + "###" + + doc_updated = nested_alter( + self.sample_data4, ["plz", "vorgangsID"], callback, in_place=False) + + plz1 = doc_updated["plz"] + plz2 = doc_updated["vertragsteile"][0]["beitragsDaten"]["plz"] + vorgangsid = doc_updated["vorgangsID"] + + # should not work without specifying + # "treat_list_as_element = True" in nested_update + self.assertEqual(plz1, "82269###") + self.assertEqual(plz2, "86899###") + self.assertEqual(vorgangsid, "1###") + + def test_nested_alter_taco_for_example(self): + documents = [ + {'taco': 42}, + {'salsa': [{'burrito': {'taco': 69}}]} + ] + + # write a callback function which processes a scalar value. + # Be aware about the possible types which can be passed to + # the callback functions. + # In this example we can be sure that only int will be passed, + # in production you should check the type because it could be + # anything. + def callback(data): + return data + 10 # (data/100*10) + + # The alter-version only works for scalar input (one dict), + # if you need to adress a list of dicts, you have to + # manually iterate over those and pass them to + # nested_update one by one + out = [] + for elem in documents: + altered_document = nested_alter(elem, "taco", callback) + out.append(altered_document) + + self.maxDiff = None + self.assertEqual(out[0]["taco"], 52) + self.assertEqual(out[1]["salsa"][0]["burrito"]["taco"], 79) + + def test_sample_data4(self): + + result = { + "modelversion": "1.1.0", + "vorgangsID": "1", + "versorgungsvorschlagDatum": 1510558834978, + "eingangsdatum": 1510558834978, + "plz": 82270, + "vertragsteile": [ + { + "typ": "1", + "beitragsDaten": { + "endalter": 85, + "brutto": 58.76, + "netto": 58.76, + "zahlungsrhythmus": "MONATLICH", + "plz": 86900 + }, + "beginn": 1512082800000, + "lebenslang": "True", + "ueberschussverwendung": { + "ueberschussverwendung": "2", + "indexoption": "3" + }, + "deckung": [ + { + "typ": "2", + "art": "1", + "leistung": { + "value": 7500242424.0, + "einheit": "2" + }, + "leistungsRhythmus": "1" + } + ], + "zuschlagNachlass": [] + }, + { + "typ": "1", + "beitragsDaten": { + "endalter": 85, + "brutto": 0.6, + "netto": 0.6, + "zahlungsrhythmus": "1" + }, + "zuschlagNachlass": [] + } + ] } + + # add +1 to all plz + def callback(data): + return data + 1 + + self.maxDiff = None self.assertEqual( - result2, nested_update( - self.sample_data4, key='memory', value=True - ) + result, nested_alter(self.sample_data4, 'plz', callback) )