Skip to content

Commit dde2dff

Browse files
committed
Remove PID from id sync lambda
1 parent 9768338 commit dde2dff

File tree

11 files changed

+105
-174
lines changed

11 files changed

+105
-174
lines changed

.github/pull_request_template.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Add any other relevant notes or explanations here. **Remove this line if you hav
1919

2020
:information_source: This section is to be filled in by the **reviewer**.
2121

22-
- [ ] I have reviewed the changes in this PR and they fill all or part of the acceptance criteria of the ticket, and the code is in a mergeable state.
22+
- [ ] I have reviewed the changes in this PR and they fill all of the acceptance criteria of the ticket.
2323
- [ ] If there were infrastructure, operational, or build changes, I have made sure there is sufficient evidence that the changes will work.
24-
- [ ] I have ensured the changelog has been updated by the submitter, if necessary.
24+
- [ ] If there were changes that are outside of the regular release processes e.g. account infrastructure to setup, manual setup for external API integrations, secrets to set, then I have checked that the developer has flagged this to the Tech Lead as release steps.
25+
- [ ] I have checked that no Personal Identifiable Data (PID) is logged as part of the changes.

lambdas/id_sync/src/id_sync.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,30 +26,21 @@ def handler(event_data: Dict[str, Any], _context) -> Dict[str, Any]:
2626
logger.info("id_sync processing event with %d records", len(records))
2727

2828
results = []
29-
nhs_numbers = []
3029
error_count = 0
3130

3231
for record in records:
3332
result = process_record(record)
3433
results.append(result)
3534

36-
if "nhs_number" in result:
37-
nhs_numbers.append(result["nhs_number"])
38-
3935
if result.get("status") == "error":
4036
error_count += 1
4137

4238
if error_count > 0:
4339
raise IdSyncException(
4440
message=f"Processed {len(records)} records with {error_count} errors",
45-
nhs_numbers=nhs_numbers,
4641
)
4742

48-
response = {
49-
"status": "success",
50-
"message": f"Successfully processed {len(records)} records",
51-
"nhs_numbers": nhs_numbers,
52-
}
43+
response = {"status": "success", "message": f"Successfully processed {len(records)} records"}
5344

5445
logger.info("id_sync handler completed: %s", response)
5546
return response

lambdas/id_sync/src/ieds_db_operations.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,22 @@ def get_ieds_table():
2323

2424
def ieds_update_patient_id(old_id: str, new_id: str, items_to_update: list | None = None) -> dict:
2525
"""Update the patient ID (new NHS number) in the IEDS table."""
26-
logger.info(f"ieds_update_patient_id. Update patient ID from {old_id} to {new_id}")
2726
if not old_id or not new_id or not old_id.strip() or not new_id.strip():
28-
return make_status("Old ID and New ID cannot be empty", old_id, "error")
27+
return make_status("Old ID and New ID cannot be empty", status="error")
2928

3029
if old_id == new_id:
31-
return make_status(f"No change in patient ID: {old_id}", old_id)
30+
return make_status("No change in patient ID")
3231

3332
try:
34-
logger.info(f"Updating patient ID in IEDS from {old_id} to {new_id}")
35-
3633
if items_to_update is None:
3734
logger.info("Getting items to update in IEDS table...")
3835
items_to_update = get_items_from_patient_id(old_id)
3936
else:
4037
logger.info("Using provided items_to_update list, size=%d", len(items_to_update))
4138

4239
if not items_to_update:
43-
logger.warning(f"No items found to update for patient ID: {old_id}")
44-
return make_status(f"No items found to update for patient ID: {old_id}", old_id)
40+
logger.warning("No items found to update for patient NHS Number")
41+
return make_status("No items found to update for patient ID")
4542

4643
logger.info(f"Items to update: {len(items_to_update)}")
4744

@@ -55,22 +52,18 @@ def ieds_update_patient_id(old_id: str, new_id: str, items_to_update: list | Non
5552
logger.info(f"All batches complete. Total batches: {total_batches}, All successful: {all_batches_successful}")
5653

5754
if all_batches_successful:
58-
return make_status(
59-
f"IEDS update, patient ID: {old_id}=>{new_id}. {len(items_to_update)} updated {total_batches}.",
60-
old_id,
61-
)
55+
return make_status(f"IEDS update. {len(items_to_update)} item(s) updated in {total_batches} batch(es).")
6256
else:
6357
return make_status(
64-
f"Failed to update some batches for patient ID: {old_id}",
65-
old_id,
66-
"error",
58+
"Failed to update some batches for patient ID",
59+
status="error",
6760
)
6861

6962
except Exception as e:
7063
logger.exception("Error updating patient ID")
7164
logger.info("Error details: %s", e)
7265
raise IdSyncException(
73-
message=f"Error updating patient Id from :{old_id} to {new_id}",
66+
message="Error updating patient ID",
7467
nhs_numbers=[old_id, new_id],
7568
exception=e,
7669
)
@@ -82,16 +75,15 @@ def get_items_from_patient_id(id: str) -> list:
8275
Delegates actual paging to the internal helper `_paginate_items_for_patient_pk`.
8376
Raises IdSyncException on error.
8477
"""
85-
logger.info("Getting items for patient id: %s", id)
8678
patient_pk = f"Patient#{id}"
8779
try:
8880
return paginate_items_for_patient_pk(patient_pk)
8981
except IdSyncException:
9082
raise
9183
except Exception as e:
92-
logger.exception("Error querying items for patient PK: %s", patient_pk)
84+
logger.exception("Error querying items for patient PK")
9385
raise IdSyncException(
94-
message=f"Error querying items for patient PK: {patient_pk}",
86+
message="Error querying items for patient PK",
9587
nhs_numbers=[patient_pk],
9688
exception=e,
9789
)

lambdas/id_sync/src/pds_details.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import tempfile
6+
from typing import Optional
67

78
from common.authentication import AppRestrictedAuth, Service
89
from common.cache import Cache
@@ -18,8 +19,6 @@
1819
# Get Patient details from external service PDS using NHS number from MNS notification
1920
def pds_get_patient_details(nhs_number: str) -> dict:
2021
try:
21-
logger.info(f"get patient details. nhs_number: {nhs_number}")
22-
2322
cache = Cache(directory=safe_tmp_dir)
2423
authenticator = AppRestrictedAuth(
2524
service=Service.PDS,
@@ -31,26 +30,27 @@ def pds_get_patient_details(nhs_number: str) -> dict:
3130
patient = pds_service.get_patient_details(nhs_number)
3231
return patient
3332
except Exception as e:
34-
msg = f"Error getting PDS patient details for {nhs_number}"
33+
msg = "Error retrieving patient details from PDS"
3534
logger.exception(msg)
3635
raise IdSyncException(message=msg, exception=e)
3736

3837

3938
# Extract Patient identifier value from PDS patient details
40-
def pds_get_patient_id(nhs_number: str) -> str:
39+
def pds_get_patient_id(nhs_number: str) -> Optional[str]:
4140
"""
4241
Get PDS patient ID from NHS number.
4342
:param nhs_number: NHS number of the patient
4443
:return: PDS patient ID
4544
"""
4645
try:
4746
patient_details = pds_get_patient_details(nhs_number)
47+
4848
if not patient_details:
4949
return None
5050

5151
return patient_details["identifier"][0]["value"]
5252

5353
except Exception as e:
54-
msg = f"Error getting PDS patient ID for {nhs_number}"
54+
msg = "Error retrieving patient details from PDS"
5555
logger.exception(msg)
5656
raise IdSyncException(message=msg, exception=e)

lambdas/id_sync/src/record_processor.py

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import ast
21
import json
32
from typing import Any, Dict
43

@@ -13,75 +12,66 @@
1312

1413

1514
def process_record(event_record: Dict[str, Any]) -> Dict[str, Any]:
16-
logger.info("process_record. Processing record: %s", event_record)
15+
logger.info("Processing record with SQS messageId: %s", event_record.get("messageId"))
1716
body_text = event_record.get("body", "")
1817

19-
# convert body to json (try JSON first, then fall back to Python literal)
20-
if isinstance(body_text, str):
21-
try:
22-
body = json.loads(body_text)
23-
except json.JSONDecodeError:
24-
try:
25-
body = ast.literal_eval(body_text)
26-
except (ValueError, SyntaxError):
27-
logger.error("Failed to parse body: %s", body_text)
28-
return {"status": "error", "message": "Invalid body format"}
29-
else:
30-
body = body_text
18+
try:
19+
body = json.loads(body_text)
20+
except json.JSONDecodeError:
21+
logger.error("Failed to parse body: %s", body_text)
22+
return {"status": "error", "message": "Invalid body format"}
3123

3224
nhs_number = body.get("subject")
33-
logger.info("process record NHS number: %s", nhs_number)
34-
if nhs_number:
35-
return process_nhs_number(nhs_number)
25+
logger.info("Processing MNS event with id: %s", body.get("id"))
26+
27+
if not nhs_number:
28+
logger.info("No NHS number found in event record")
29+
return {"status": "error", "message": "No NHS number found in event record"}
3630

37-
logger.info("No NHS number found in event record")
38-
return {"status": "error", "message": "No NHS number found in event record"}
31+
return process_nhs_number(nhs_number)
3932

4033

4134
def process_nhs_number(nhs_number: str) -> Dict[str, Any]:
42-
# get patient details from PDS
4335
new_nhs_number = pds_get_patient_id(nhs_number)
4436

4537
if not new_nhs_number:
46-
return make_status("No patient ID found for NHS number", nhs_number)
38+
return make_status("No patient ID found for NHS number")
4739

4840
if new_nhs_number == nhs_number:
49-
return make_status("No update required", nhs_number)
41+
return make_status("No update required")
5042

51-
logger.info("Update patient ID from %s to %s", nhs_number, new_nhs_number)
43+
logger.info("NHS Number has changed. Performing updates on relevant IEDS records")
5244

5345
try:
5446
# Fetch PDS Patient resource and IEDS resources for the old NHS number
5547
pds_patient_resource, ieds_resources = fetch_pds_and_ieds_resources(nhs_number)
5648
except Exception as e:
5749
logger.exception("process_nhs_number: failed to fetch demographic details: %s", e)
58-
return make_status(str(e), nhs_number, "error")
50+
return make_status(str(e), status="error")
5951

6052
logger.info(
6153
"Fetched IEDS resources. IEDS count: %d",
6254
len(ieds_resources) if ieds_resources else 0,
6355
)
6456

6557
if not ieds_resources:
66-
logger.info("No IEDS records returned for NHS number: %s", nhs_number)
67-
return make_status(f"No records returned for ID: {nhs_number}", nhs_number)
58+
logger.info("No IEDS records returned for NHS number")
59+
return make_status("No records returned for NHS Number")
6860

6961
# Compare demographics from PDS to each IEDS item, keep only matching records
7062
matching_records = []
7163
discarded_count = 0
7264
for detail in ieds_resources:
73-
logger.info("Processing IEDS record: %s", detail)
7465
if demographics_match(pds_patient_resource, detail):
7566
matching_records.append(detail)
7667
else:
7768
discarded_count += 1
7869

7970
if not matching_records:
8071
logger.info("No records matched PDS demographics: %d", discarded_count)
81-
return make_status("No records matched PDS demographics; update skipped", nhs_number)
72+
return make_status("No records matched PDS demographics; update skipped")
8273

8374
response = ieds_update_patient_id(nhs_number, new_nhs_number, items_to_update=matching_records)
84-
response["nhs_number"] = nhs_number
8575
# add counts for observability
8676
response["matched"] = len(matching_records)
8777
response["discarded"] = discarded_count
@@ -90,23 +80,16 @@ def process_nhs_number(nhs_number: str) -> Dict[str, Any]:
9080

9181
# Function to fetch PDS Patient details and IEDS Immunisation records.
9282
def fetch_pds_and_ieds_resources(nhs_number: str):
93-
logger.info("fetch_pds_and_ieds_resources: fetching for %s", nhs_number)
9483
try:
9584
pds = pds_get_patient_details(nhs_number)
9685
except Exception as e:
97-
logger.exception(
98-
"fetch_pds_and_ieds_resources: failed to fetch PDS details for %s",
99-
nhs_number,
100-
)
86+
logger.exception("fetch_pds_and_ieds_resources: failed to fetch PDS details")
10187
raise RuntimeError("Failed to fetch PDS details") from e
10288

10389
try:
10490
ieds = get_items_from_patient_id(nhs_number)
10591
except Exception as e:
106-
logger.exception(
107-
"fetch_pds_and_ieds_resources: failed to fetch IEDS items for %s",
108-
nhs_number,
109-
)
92+
logger.exception("fetch_pds_and_ieds_resources: failed to fetch IEDS items")
11093
raise RuntimeError("Failed to fetch IEDS items") from e
11194

11295
return pds, ieds
@@ -147,13 +130,7 @@ def normalize_strings(item: Any) -> str | None:
147130
# Retrieve patient resource from PDS
148131
pds_name = normalize_strings(extract_normalized_name_from_patient(pds_details))
149132
pds_gender = normalize_strings(pds_details.get("gender"))
150-
pds_birth = normalize_strings(pds_details.get("birthDate"))
151-
logger.debug(
152-
"demographics_match: demographics match for name=%s, gender=%s, birthDate=%s",
153-
pds_name,
154-
pds_gender,
155-
pds_birth,
156-
)
133+
pds_dob = normalize_strings(pds_details.get("birthDate"))
157134

158135
# Retrieve patient resource from IEDS item
159136
patient = extract_patient_resource_from_item(ieds_item)
@@ -164,24 +141,24 @@ def normalize_strings(item: Any) -> str | None:
164141
# normalize patient fields from IEDS
165142
ieds_name = normalize_strings(extract_normalized_name_from_patient(patient))
166143
ieds_gender = normalize_strings(patient.get("gender"))
167-
ieds_birth = normalize_strings(patient.get("birthDate"))
144+
ieds_dob = normalize_strings(patient.get("birthDate"))
168145

169146
# All required fields must be present
170-
if not all([pds_name, pds_gender, pds_birth, ieds_name, ieds_gender, ieds_birth]):
147+
if not all([pds_name, pds_gender, pds_dob, ieds_name, ieds_gender, ieds_dob]):
171148
logger.debug("demographics_match: missing required demographics")
172149
return False
173150

174151
# Compare fields
175-
if pds_birth != ieds_birth:
176-
logger.debug("demographics_match: birthDate mismatch %s != %s", pds_birth, ieds_birth)
152+
if pds_dob != ieds_dob:
153+
logger.debug("demographics_match: birthDate mismatch between the patient resources")
177154
return False
178155

179156
if pds_gender != ieds_gender:
180-
logger.debug("demographics_match: gender mismatch %s != %s", pds_gender, ieds_gender)
157+
logger.debug("demographics_match: gender mismatch between the patient resources")
181158
return False
182159

183160
if pds_name != ieds_name:
184-
logger.debug("demographics_match: name mismatch %s != %s", pds_name, ieds_name)
161+
logger.debug("demographics_match: name mismatch between the patient resources")
185162
return False
186163

187164
return True

lambdas/id_sync/src/utils.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
from typing import Any, Dict
22

33

4-
def make_status(msg: str, nhs_number: str | None = None, status: str = "success") -> Dict[str, Any]:
5-
"""Return a simple status dict used by record processing for observability.
6-
7-
If `nhs_number` is None the key is omitted which keeps the output shape
8-
compatible with callers that expect only a status/message.
9-
"""
10-
result = {"status": status, "message": msg}
11-
if nhs_number is not None:
12-
result["nhs_number"] = nhs_number
13-
return result
4+
def make_status(msg: str, status: str = "success") -> Dict[str, Any]:
5+
"""Return a simple status dict used by record processing for observability."""
6+
return {"status": status, "message": msg}

0 commit comments

Comments
 (0)