Skip to content

Commit 5648c7e

Browse files
committed
refactor(version-history): FTRS-1627 update DeepDiff usage in version history and simplify logging
1 parent b49c906 commit 5648c7e

File tree

6 files changed

+137
-176
lines changed

6 files changed

+137
-176
lines changed

application/packages/python/ftrs_data_layer/logbase.py

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -296,36 +296,27 @@ class VersionHistoryLogBase(LogBase):
296296
level=DEBUG,
297297
message="Processing stream record: entity={entity_name}, record_id={record_id}, field={field_name}, event_type={event_type}",
298298
)
299-
VH_PROCESSOR_004 = LogReference(
300-
level=DEBUG,
301-
message="Change detected: old_value={old_value}, new_value={new_value}",
302-
)
303-
VH_PROCESSOR_005 = LogReference(
304-
level=DEBUG, message="Writing version history item to table: {version_item}"
305-
)
306299
VH_PROCESSOR_006 = LogReference(
307300
level=DEBUG,
308301
message=(
309302
"Extracted stream record data: entity_name={entity_name}, "
310303
"record_id={record_id}, field_name={field_name}, "
311-
"has_old_image={has_old_image}, has_new_image={has_new_image}, keys={keys}"
304+
"has_old_image={has_old_image}, has_new_image={has_new_image}"
312305
),
313306
)
314307
VH_PROCESSOR_007 = LogReference(
315308
level=DEBUG,
316309
message=(
317310
"Resolved event configuration: event_name={event_name}, "
318311
"change_type={change_type}, old_val_present={old_val_present}, "
319-
"new_val_present={new_val_present}, old_val={old_val}, "
320-
"new_val={new_val}, old_type={old_type}, new_type={new_type}"
312+
"new_val_present={new_val_present}"
321313
),
322314
)
323315
VH_PROCESSOR_008 = LogReference(
324316
level=DEBUG,
325317
message=(
326318
"Computed field delta: field_name={field_name}, "
327-
"delta_keys={delta_keys}, has_diff={has_diff}, "
328-
"delta={delta}, values_equal={values_equal}"
319+
"delta_keys={delta_keys}, has_changes={has_changes}"
329320
),
330321
)
331322
VH_PROCESSOR_009 = LogReference(
@@ -339,40 +330,13 @@ class VersionHistoryLogBase(LogBase):
339330
level=DEBUG,
340331
message="Prepared version history identifiers: entity_id={entity_id}, timestamp={timestamp}",
341332
)
342-
VH_PROCESSOR_013 = LogReference(
343-
level=DEBUG,
344-
message=(
345-
"Skipping UPDATE - simple values are equal: "
346-
"entity={entity_name}/{record_id}/{field_name}, "
347-
"old={old_val}, new={new_val}"
348-
),
349-
)
350-
VH_PROCESSOR_014 = LogReference(
351-
level=DEBUG,
352-
message=(
353-
"Proceeding with UPDATE - simple values differ: "
354-
"old={old_val}, new={new_val}"
355-
),
356-
)
357333
VH_PROCESSOR_015 = LogReference(
358-
level=DEBUG,
334+
level=INFO,
359335
message=(
360-
"Skipping UPDATE - complex values have empty diff: "
336+
"Skipping UPDATE - no changes detected: "
361337
"entity={entity_name}/{record_id}/{field_name}"
362338
),
363339
)
364-
VH_PROCESSOR_016 = LogReference(
365-
level=DEBUG,
366-
message="Proceeding with UPDATE - complex values have diff: {diff}",
367-
)
368-
VH_PROCESSOR_017 = LogReference(
369-
level=DEBUG,
370-
message=(
371-
"Extracting values: field_name={field_name}, "
372-
"is_document_field={is_document_field}, "
373-
"system_fields_excluded={system_fields_count}"
374-
),
375-
)
376340

377341

378342
class UtilsLogBase(LogBase):

services/data-migration/src/version_history/stream_processor.py

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def _extract_record_metadata(
5353
new_image = record.dynamodb.new_image
5454

5555
record_id = keys.get("id")
56-
# Field name for composite key: "document" for gp org entities, may differ for pharmacy
56+
# Field name for composite key: defaults to "document" for gp org entities, may differ for pharmacy service types
5757
field_name = keys.get("field", "document")
5858
event_name = record.get("eventName", "MODIFY")
5959

@@ -95,13 +95,6 @@ def _extract_field_values(
9595
old_value = old_image.get(field_name) if old_image else None
9696
new_value = new_image.get(field_name) if new_image else None
9797

98-
LOGGER.log(
99-
VersionHistoryLogBase.VH_PROCESSOR_017,
100-
field_name=field_name,
101-
is_document_field=(field_name == "document"),
102-
system_fields_count=len(SYSTEM_FIELDS),
103-
)
104-
10598
return old_value, new_value
10699

107100

@@ -125,10 +118,6 @@ def _determine_change_type(
125118
change_type=change_type,
126119
old_val_present=old_value is not None,
127120
new_val_present=new_value is not None,
128-
old_val=old_value,
129-
new_val=new_value,
130-
old_type=type(old_value).__name__,
131-
new_type=type(new_value).__name__,
132121
)
133122

134123
return change_type
@@ -145,20 +134,15 @@ def _should_skip_update(
145134
if change_type != "UPDATE":
146135
return False
147136

148-
# Check for empty diff in complex values
149-
if not field_delta.get("diff"):
137+
# Check for empty diff (no changes detected by DeepDiff)
138+
if not field_delta:
150139
LOGGER.log(
151140
VersionHistoryLogBase.VH_PROCESSOR_015,
152141
entity_name=entity_name,
153142
record_id=record_id,
154143
field_name=field_name,
155144
)
156145
return True
157-
else:
158-
LOGGER.log(
159-
VersionHistoryLogBase.VH_PROCESSOR_016,
160-
diff=field_delta.get("diff"),
161-
)
162146

163147
return False
164148

@@ -216,7 +200,6 @@ def process_stream_record(
216200
field_name=field_name,
217201
has_old_image=old_image is not None,
218202
has_new_image=new_image is not None,
219-
keys=keys,
220203
)
221204

222205
LOGGER.log(
@@ -234,23 +217,15 @@ def process_stream_record(
234217
LOGGER.log(
235218
VersionHistoryLogBase.VH_PROCESSOR_008,
236219
field_name=field_name,
237-
delta_keys=list(field_delta.keys()),
238-
has_diff="diff" in field_delta,
239-
delta=field_delta,
240-
values_equal=field_delta.get("old") == field_delta.get("new"),
220+
delta_keys=list(field_delta.keys()) if field_delta else [],
221+
has_changes=bool(field_delta),
241222
)
242223

243224
if _should_skip_update(
244225
change_type, field_delta, entity_name, record_id, field_name
245226
):
246227
return
247228

248-
LOGGER.log(
249-
VersionHistoryLogBase.VH_PROCESSOR_004,
250-
old_value=old_value,
251-
new_value=new_value,
252-
)
253-
254229
version_item = _create_version_item(
255230
entity_name,
256231
record_id,
@@ -261,11 +236,6 @@ def process_stream_record(
261236
field_name,
262237
)
263238

264-
LOGGER.log(
265-
VersionHistoryLogBase.VH_PROCESSOR_005,
266-
version_item=version_item,
267-
)
268-
269239
version_history_table.put_item(Item=version_item)
270240

271241
LOGGER.log(

services/data-migration/src/version_history/utils.py

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,31 +53,22 @@ def extract_changed_by(new_image: Dict[str, Any]) -> Dict[str, str]:
5353

5454
def compute_field_delta(old_value: Any, new_value: Any) -> Dict[str, Any]: # noqa: ANN401
5555
"""
56-
Compute a structured delta between old and new field values.
56+
Compute a structured delta between old and new field values using DeepDiff.
5757
58-
For simple values (str, int, bool, None), returns {"old": old_value, "new": new_value}.
59-
For complex values (dict, list), uses DeepDiff to compute a detailed delta.
58+
Returns DeepDiff's native structure which provides detailed change information:
59+
- values_changed: {"root['field']": {"old_value": ..., "new_value": ...}}
60+
- dictionary_item_added: {"root['field']": value}
61+
- dictionary_item_removed: {"root['field']": value}
62+
- iterable_item_added: {"root[index]": value}
63+
- iterable_item_removed: {"root[index]": value}
6064
6165
Args:
6266
old_value: Previous field value
6367
new_value: New field value
6468
6569
Returns:
66-
Dictionary with delta information:
67-
- For simple values: {"old": <value>, "new": <value>}
68-
- For complex values: {"old": <value>, "new": <value>, "diff": <deepdiff_json>}
70+
Dictionary with DeepDiff structure. Empty dict if values are identical.
6971
"""
70-
# Simple types: return old/new
71-
if not isinstance(old_value, (dict, list)) and not isinstance(
72-
new_value, (dict, list)
73-
):
74-
return {"old": old_value, "new": new_value}
75-
76-
# Type mismatch: return old/new
77-
if type(old_value) is not type(new_value):
78-
return {"old": old_value, "new": new_value}
79-
80-
# Complex types: compute detailed diff
8172
diff = DeepDiff(
8273
old_value,
8374
new_value,
@@ -86,5 +77,4 @@ def compute_field_delta(old_value: Any, new_value: Any) -> Dict[str, Any]: # no
8677
ignore_order=True,
8778
)
8879

89-
diff_json = json.loads(diff.to_json())
90-
return {"old": old_value, "new": new_value, "diff": diff_json}
80+
return json.loads(diff.to_json())

services/data-migration/tests/unit/version_history/test_stream_processor.py

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,21 @@ def test_process_organisation_document_field_change(
3333
assert item["change_type"] == "UPDATE"
3434
assert "document" in item["changed_fields"]
3535

36-
# Verify that the document field contains a diff of the changes
36+
# Verify that the document field contains DeepDiff structure
3737
document_delta = item["changed_fields"]["document"]
38-
assert "diff" in document_delta
39-
assert "old" in document_delta
40-
assert "new" in document_delta
41-
42-
# Verify system fields were excluded from the values
43-
old_val = document_delta["old"]
44-
new_val = document_delta["new"]
45-
assert "id" not in old_val
46-
assert "field" not in old_val
47-
assert "created" not in old_val
48-
assert "lastUpdated" not in old_val
49-
assert "name" in old_val
50-
assert old_val["name"] == "Old Practice Name"
51-
assert new_val["name"] == "New Practice Name"
38+
assert isinstance(document_delta, dict)
39+
40+
# Should show the name field change
41+
assert "values_changed" in document_delta
42+
assert "root['name']" in document_delta["values_changed"]
43+
assert (
44+
document_delta["values_changed"]["root['name']"]["old_value"]
45+
== "Old Practice Name"
46+
)
47+
assert (
48+
document_delta["values_changed"]["root['name']"]["new_value"]
49+
== "New Practice Name"
50+
)
5251

5352
assert item["changed_by"]["type"] == "app"
5453
assert item["changed_by"]["value"] == "INTERNAL001"
@@ -173,18 +172,22 @@ def test_process_document_field_handles_insert_event(
173172
item = call_args.kwargs["Item"]
174173

175174
assert item["change_type"] == "CREATE"
176-
expected_changed_fields = {
177-
"document": {
178-
"old": None,
179-
"new": {
180-
"name": "New Practice Name",
181-
"active": True,
182-
"identifier_ODS_ODSCode": "B98765",
183-
"type": "GP Practice",
184-
},
185-
}
186-
}
187-
assert item["changed_fields"] == expected_changed_fields
175+
# For CREATE, DeepDiff compares None to new value
176+
document_delta = item["changed_fields"]["document"]
177+
assert "values_changed" in document_delta or "type_changes" in document_delta
178+
# Verify it captured the new document
179+
if "type_changes" in document_delta:
180+
# DeepDiff reports None -> dict as type change
181+
assert document_delta["type_changes"]["root"]["old_value"] is None
182+
new_val = document_delta["type_changes"]["root"]["new_value"]
183+
assert new_val["name"] == "New Practice Name"
184+
assert new_val["active"] is True
185+
assert new_val["identifier_ODS_ODSCode"] == "B98765"
186+
assert new_val["type"] == "GP Practice"
187+
else:
188+
assert document_delta["values_changed"]["root"]["old_value"] is None
189+
new_val = document_delta["values_changed"]["root"]["new_value"]
190+
assert new_val["name"] == "New Practice Name"
188191
assert item["changed_by"]["type"] == "app"
189192
assert item["changed_by"]["value"] == "INTERNAL001"
190193

@@ -238,16 +241,20 @@ def test_process_document_field_handles_remove_event(
238241
item = call_args.kwargs["Item"]
239242

240243
assert item["change_type"] == "DELETE"
241-
expected_changed_fields = {
242-
"document": {
243-
"old": {
244-
"name": "Old Location Name",
245-
"status": "active",
246-
"physicalType": "building",
247-
},
248-
"new": None,
249-
}
250-
}
251-
assert item["changed_fields"] == expected_changed_fields
244+
# For DELETE, DeepDiff compares old value to None
245+
document_delta = item["changed_fields"]["document"]
246+
assert "values_changed" in document_delta or "type_changes" in document_delta
247+
# Verify it captured the old document
248+
if "type_changes" in document_delta:
249+
# DeepDiff reports dict -> None as type change
250+
old_val = document_delta["type_changes"]["root"]["old_value"]
251+
assert old_val["name"] == "Old Location Name"
252+
assert old_val["status"] == "active"
253+
assert old_val["physicalType"] == "building"
254+
assert document_delta["type_changes"]["root"]["new_value"] is None
255+
else:
256+
old_val = document_delta["values_changed"]["root"]["old_value"]
257+
assert old_val["name"] == "Old Location Name"
258+
assert document_delta["values_changed"]["root"]["new_value"] is None
252259
assert item["changed_by"]["type"] == "user"
253260
assert item["changed_by"]["value"] == "user-456"

0 commit comments

Comments
 (0)