from copy import deepcopy def merge_update(_old, _new): for k, v in _new.items(): if isinstance(v, dict): r = merge_update(_old.get(k, {}), v) _old[k] = r else: if isinstance(_old, dict): _old[k] = _new[k] else: _old = _new return _old def merge_add(_old, _new): for k, v in _new.items(): if isinstance(v, dict): r = merge_add(_old.get(k, {}), v) if k in _old: _old[k].update(r) else: _old[k] = r else: _old[k] = _new[k] return _old def merge_delete(_old, _new): for k, v in list(_new.items()): if isinstance(v, dict) and v: if k in _old and isinstance(_old[k], dict): merge_delete(_old[k], v) else: if k in _old: del _old[k] return _old def merge_full(_old, _diff): _is_diff = False res = _old if "update" in _diff: res = merge_update(res, _diff['update']) _is_diff = True if "update_add" in _diff: res = merge_update(res, _diff['update_add']) _is_diff = True if "add" in _diff: res = merge_add(res, _diff['add']) _is_diff = True if "delete" in _diff: res = merge_delete(res, _diff['delete']) _is_diff = True if not _is_diff and not _old: res = _diff return res def _legacy_assertions() -> None: # Merge result = merge_add({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}} result = merge_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'d': 'icecream'}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}, 'd':'icecream'} result = merge_add({'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}, {'d': 'icecream'}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'f': 'coffee'}, 'd':'icecream'} result = merge_add({'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}, 'd': 'icecream'}, {'d': 'icecream'}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'f': 'coffee'}, 'd':'icecream'} result = merge_delete({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}, 'd': 'icecream'}, {'d': 'icecream'}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}} result = merge_delete({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'c': {'f': 'coffee'}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'coke'}} result = merge_delete({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'c': {'e': 'coke'}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'f': 'coffee'}} # merge_full # the no-jaydiff case result = merge_full({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}} # combine_upd_add = False result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'add': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, {'update': {'c': 'pepsi'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'}, {'delete': {'d': 'icecream'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, {'add': {'d': 'icecream'}, 'delete': {'b': 'fries'}, 'update': {'c': 'coke'}}) assert result == {'a': 'hamburger', 'd': 'icecream', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'c': 'pepsi', 'd': 'icecream'}, {'add': {'b': 'fries'}, 'delete': {'d': 'icecream'}, 'update': {'c': 'coke'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}}, {'add': {'c': {'f': 'coffee'}}, 'delete': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}}}) assert result == {'c': {'f': 'coffee'}} result = merge_full({'a': 'hamburger', 'c': 'pepsi', 'd': 'icecream'}, {'add': {'b': 'fries'}, 'delete': {'d': 'icecream'}, 'update': {'c': 'coke'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, {}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, {'update': {'c': 'pepsi'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, {'delete': {'c': 'pepsi'}}) assert result == {'a': 'hamburger', 'b': 'fries'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, {'update': {'c': 'coke'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}}) assert result == {'a': 'hamburger', 'b': 'fries'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': {'e': 'coke'}}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}}) assert result == {'c': {'e': 'coke'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}, 'update': {'a': 'hotdog', 'b': 'potatoes'}}) assert result == {'a':'hotdog', 'b':'potatoes'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}, 'update': {'c': {'e': 'pepsi'}}}) assert result == {'c': {'e': 'pepsi'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}, 'update': {'a': 'hotdog'}}) assert result == {'a':'hotdog', 'b':'fries'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'update': {'c': {'e': 'pepsi'}}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'pepsi', 'f': 'coffee'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'add': {'d': 'icecream'}, 'delete': {'c': None}, 'update': {'a': 'hotdog', 'b': 'potatoes'}}) assert result == {'a':'hotdog', 'b':'potatoes', 'd': 'icecream'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'add': {'c': {'g': 'tea', 'h': 'cake'}}, 'delete': {'a': 'hamburger', 'c': {'f': 'coffee'}}, 'update': {'b': 'potatoes', 'c': {'e': 'pepsi'}}}) assert result == {'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}, 'b':'potatoes'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}, 'update': {'a': 'hotdog'}}) assert result == {'a':'hotdog', 'b':'fries'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'update': {'c': {'e': 'pepsi'}}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'pepsi', 'f': 'coffee'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'add': {'c': {'h': 'cake'}}, 'update': {'c': {'e': 'pepsi'}}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'pepsi', 'f': 'coffee', 'h': 'cake'}} # ToDo: merge_full # combine_upd_add = True result = merge_full({}, {'update_add': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, {'update_add': {'c': 'coke'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'}, {'delete': {'d': 'icecream'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, {'delete': {'b': 'fries'}, 'update_add': {'c': 'coke', 'd': 'icecream'}}) assert result == {'a': 'hamburger', 'd': 'icecream', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'c': 'pepsi', 'd': 'icecream'}, {'delete': {'d': 'icecream'}, 'update_add': {'b': 'fries', 'c': 'coke'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}}, {'update_add': {'c': {'f': 'coffee'}}, 'delete': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}}}) assert result == {'c': {'f': 'coffee'}} result = merge_full({'a': 'hamburger', 'c': 'pepsi', 'd': 'icecream'}, {'delete': {'d': 'icecream'}, 'update_add': {'b': 'fries', 'c': 'coke'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, {}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, {'update_add': {'c': 'pepsi'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, {'delete': {'c': 'pepsi'}}) assert result == {'a': 'hamburger', 'b': 'fries'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, {'update_add': {'c': 'coke'}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': 'coke'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}}) assert result == {'a': 'hamburger', 'b': 'fries'} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': {'e': 'coke'}}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}}) assert result == {'c': {'e': 'coke'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}, 'update_add': {'a': 'hotdog', 'b': 'potatoes'}}) assert result == {'a':'hotdog', 'b':'potatoes'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}, 'update_add': {'c': {'e': 'pepsi'}}}) assert result == {'c': {'e': 'pepsi'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}, 'update_add': {'a': 'hotdog'}}) assert result == {'a':'hotdog', 'b':'fries'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'update_add': {'c': {'e': 'pepsi'}}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'pepsi', 'f': 'coffee'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}, 'update_add': {'a': 'hotdog', 'b': 'potatoes', 'd': 'icecream'}}) assert result == {'a':'hotdog', 'b':'potatoes', 'd': 'icecream'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'a': 'hamburger', 'c': {'f': 'coffee'}}, 'update_add': {'b': 'potatoes', 'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}}}) assert result == {'b':'potatoes', 'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'delete': {'c': None}, 'update_add': {'a': 'hotdog'}}) assert result == {'a':'hotdog', 'b':'fries'} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'update_add': {'c': {'e': 'pepsi'}}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'pepsi', 'f': 'coffee'}} result = merge_full({'a':'hamburger', 'b':'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, {'update_add': {'c': {'e': 'pepsi', 'h': 'cake'}}}) assert result == {'a':'hamburger', 'b':'fries', 'c': {'e': 'pepsi', 'f': 'coffee', 'h': 'cake'}} result = merge_full({}, {'add': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}} result = merge_full({}, {'add': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': {'coffee': {'h': 'black'}}}}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': {'coffee': {'h': 'black'}}}} result = merge_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': {'coffee': {'h': 'black'}}}}, {'add': {'c': {'f': {'coffee': {'j': 'sugar'}}}}}) assert result == {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': {'coffee': {'h': 'black', 'j': 'sugar'}}}} old = {'charging': { 'batteryStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:34Z', 'cruisingRangeElectric_km': 192}}, 'chargingStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:34Z'}}, 'chargingSettings': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:30Z'}}, 'plugStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:29Z'}}}} update = {'charging': { 'batteryStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:35:46Z', 'cruisingRangeElectric_km': 191}}, 'chargingStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:35:46Z'}}}} add = {'charging': { 'batteryStatus': {'value': {'currentSOC_pct': 55}}}, 'fuelStatus': {'rangeStatus': {'value': {'primaryEngine': {'currentSOC_pct': 55}}}}, 'measurements': {'fuelLevelStatus': {'value': {'currentSOC_pct': 55}}}} delete = {'charging': { 'chargingSettings': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:30Z'}}, 'plugStatus': {'value': {'carCapturedTimestamp': '2024-10-19T05:34:29Z'}}}} result = merge_full(old, {'update':update, 'add':add, 'delete':delete}) assert result == old if __name__ == '__main__': _legacy_assertions()