"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
This commit is contained in:
parent
0e8723b822
commit
4f37d265c8
5 changed files with 670 additions and 91 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -5,4 +5,6 @@ __pycache__*
|
|||
build/*
|
||||
dist/*
|
||||
nested_lookup.egg-info/*
|
||||
.cache/
|
||||
.cache
|
||||
.vscode
|
||||
git_commit.sh
|
||||
32
README.rst
32
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue