Skip to content

Commit 1a5601a

Browse files
edhall-nhsdlzhry2nhs
authored andcommitted
VED-948: Data Quality Skeleton (#1059)
* Move delta converter to shared code * Refactor and move tests into shared * Move and rename converter files and tests * Add basic structure for Data Quality Checker
1 parent a9c5253 commit 1a5601a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+580
-188
lines changed

lambdas/delta_backend/src/delta.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
from common.aws_dynamodb import get_dynamodb_table
1111
from common.clients import STREAM_NAME, get_sqs_client, logger
1212
from common.log_firehose import send_log_to_firehose
13-
from converter import Converter
14-
from mappings import ActionFlag, EventName, Operation
13+
from common.models.fhir_converter.converter import Converter
14+
from common.models.fhir_converter.mappings import ActionFlag, EventName, Operation
1515

1616
failure_queue_url = os.environ["AWS_SQS_QUEUE_URL"]
1717
delta_table_name = os.environ["DELTA_TABLE_NAME"]

lambdas/delta_backend/tests/test_convert.py

Lines changed: 3 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import json
21
import os
32
import unittest
4-
from copy import deepcopy
53
from datetime import datetime
64
from unittest.mock import patch
75

86
from boto3 import resource as boto3_resource
97
from moto import mock_aws
108

11-
from mappings import ActionFlag, EventName, Operation
12-
from utils_for_converter_tests import ErrorValuesForTests, ValuesForTests
9+
from common.models.fhir_converter.mappings import ActionFlag, EventName, Operation
10+
from utils import ValuesForTests
1311

1412
MOCK_ENV_VARS = {
1513
"AWS_SQS_QUEUE_URL": "https://sqs.eu-west-2.amazonaws.com/123456789012/test-queue",
@@ -19,7 +17,7 @@
1917
}
2018
request_json_data = ValuesForTests.json_data
2119
with patch.dict("os.environ", MOCK_ENV_VARS):
22-
from delta import Converter, handler
20+
from delta import handler
2321

2422

2523
@patch.dict("os.environ", MOCK_ENV_VARS, clear=True)
@@ -135,74 +133,6 @@ def assert_dynamodb_record(
135133
expires_at = unfiltered_items[0]["ExpiresAt"]
136134
self.assertEqual(expires_at - date_time, expected_seconds)
137135

138-
def test_fhir_converter_json_direct_data(self):
139-
"""it should convert fhir json data to flat json"""
140-
json_data = json.dumps(ValuesForTests.json_data)
141-
142-
fhir_converter = Converter(json_data)
143-
FlatFile = fhir_converter.run_conversion()
144-
145-
flatJSON = json.dumps(FlatFile)
146-
expected_imms_value = deepcopy(ValuesForTests.expected_imms2) # UPDATE is currently the default action-flag
147-
expected_imms = json.dumps(expected_imms_value)
148-
self.assertEqual(flatJSON, expected_imms)
149-
150-
errorRecords = fhir_converter.get_error_records()
151-
152-
self.assertEqual(len(errorRecords), 0)
153-
154-
def test_fhir_converter_json_error_scenario_reporting_on(self):
155-
"""it should convert fhir json data to flat json - error scenarios"""
156-
error_test_cases = [
157-
ErrorValuesForTests.missing_json,
158-
ErrorValuesForTests.json_dob_error,
159-
]
160-
161-
for test_case in error_test_cases:
162-
json_data = json.dumps(test_case)
163-
164-
fhir_converter = Converter(json_data)
165-
fhir_converter.run_conversion()
166-
167-
errorRecords = fhir_converter.get_error_records()
168-
169-
# Check if bad data creates error records
170-
self.assertTrue(len(errorRecords) > 0)
171-
172-
def test_fhir_converter_json_error_scenario_reporting_off(self):
173-
"""it should convert fhir json data to flat json - error scenarios"""
174-
error_test_cases = [
175-
ErrorValuesForTests.missing_json,
176-
ErrorValuesForTests.json_dob_error,
177-
]
178-
179-
for test_case in error_test_cases:
180-
json_data = json.dumps(test_case)
181-
182-
fhir_converter = Converter(json_data, report_unexpected_exception=False)
183-
fhir_converter.run_conversion()
184-
185-
errorRecords = fhir_converter.get_error_records()
186-
187-
# Check if bad data creates error records
188-
self.assertTrue(len(errorRecords) == 0)
189-
190-
def test_fhir_converter_json_incorrect_data_scenario_reporting_on(self):
191-
"""it should convert fhir json data to flat json - error scenarios"""
192-
193-
with self.assertRaises(ValueError):
194-
fhir_converter = Converter(None)
195-
errorRecords = fhir_converter.get_error_records()
196-
self.assertTrue(len(errorRecords) > 0)
197-
198-
def test_fhir_converter_json_incorrect_data_scenario_reporting_off(self):
199-
"""it should convert fhir json data to flat json - error scenarios"""
200-
201-
with self.assertRaises(ValueError):
202-
fhir_converter = Converter(None, report_unexpected_exception=False)
203-
errorRecords = fhir_converter.get_error_records()
204-
self.assertTrue(len(errorRecords) == 0)
205-
206136
def test_handler_imms_convert_to_flat_json(self):
207137
"""Test that the Imms field contains the correct flat JSON data for CREATE, UPDATE, and DELETE operations."""
208138
expected_action_flags = [

lambdas/delta_backend/tests/test_delta.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
from botocore.exceptions import ClientError
88

99
import delta
10+
from common.models.fhir_converter.mappings import ActionFlag, EventName, Operation
1011
from delta import (
1112
handler,
1213
process_record,
1314
send_message,
1415
)
15-
from mappings import ActionFlag, EventName, Operation
16-
from utils_for_converter_tests import RecordConfig, ValuesForTests
16+
from utils import RecordConfig, ValuesForTests
1717

1818
TEST_QUEUE_URL = "https://sqs.eu-west-2.amazonaws.com/123456789012/test-queue"
1919
os.environ["AWS_SQS_QUEUE_URL"] = TEST_QUEUE_URL

lambdas/delta_backend/tests/utils_for_converter_tests.py renamed to lambdas/delta_backend/tests/utils.py

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from decimal import Decimal
44
from typing import List
55

6-
from mappings import EventName, Operation
6+
from common.models.fhir_converter.mappings import EventName, Operation
77

88

99
class RecordConfig:
@@ -306,44 +306,6 @@ def get_expected_imms(expected_action_flag):
306306
"CONVERSION_ERRORS": [],
307307
}
308308

309-
expected_imms2 = {
310-
"NHS_NUMBER": "9000000009",
311-
"PERSON_FORENAME": "Sam",
312-
"PERSON_SURNAME": "Trailor",
313-
"PERSON_DOB": "19650228",
314-
"PERSON_GENDER_CODE": "0",
315-
"PERSON_POSTCODE": "EC1A 1BB",
316-
"DATE_AND_TIME": "20210207T13281700",
317-
"SITE_CODE": "B0C4P",
318-
"SITE_CODE_TYPE_URI": "https://fhir.nhs.uk/Id/ods-organization-code",
319-
"UNIQUE_ID": "ACME-vacc123456",
320-
"UNIQUE_ID_URI": "https://supplierABC/identifiers/vacc",
321-
"ACTION_FLAG": "UPDATE",
322-
"PERFORMING_PROFESSIONAL_FORENAME": "Florence",
323-
"PERFORMING_PROFESSIONAL_SURNAME": "Nightingale",
324-
"RECORDED_DATE": "20210207",
325-
"PRIMARY_SOURCE": "TRUE",
326-
"VACCINATION_PROCEDURE_CODE": "13246814444444",
327-
"VACCINATION_PROCEDURE_TERM": "Test Value string 123456 COVID vaccination",
328-
"DOSE_SEQUENCE": "1",
329-
"VACCINE_PRODUCT_CODE": "39114911000001105",
330-
"VACCINE_PRODUCT_TERM": "COVID-19 Vaccine Vaxzevria (ChAdOx1 S [recombinant]) not less than 2.5x100,000,000 infectious units/0.5ml dose suspension for injection multidose vials (AstraZeneca UK Ltd) (product)",
331-
"VACCINE_MANUFACTURER": "AstraZeneca Ltd",
332-
"BATCH_NUMBER": "4120Z001",
333-
"EXPIRY_DATE": "20210702",
334-
"SITE_OF_VACCINATION_CODE": "368208006",
335-
"SITE_OF_VACCINATION_TERM": "Left upper arm structure (body structure)",
336-
"ROUTE_OF_VACCINATION_CODE": "78421000",
337-
"ROUTE_OF_VACCINATION_TERM": "Intramuscular route (qualifier value)",
338-
"DOSE_AMOUNT": "0.5",
339-
"DOSE_UNIT_CODE": "ml",
340-
"DOSE_UNIT_TERM": "milliliter",
341-
"INDICATION_CODE": "443684005",
342-
"LOCATION_CODE": "EC1111",
343-
"LOCATION_CODE_TYPE_URI": "https://fhir.nhs.uk/Id/ods-organization-code",
344-
"CONVERSION_ERRORS": [],
345-
}
346-
347309

348310
class ErrorValuesForTests:
349311
json_dob_error = {

lambdas/shared/src/common/data_quality/__init__.py

Whitespace-only changes.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from dataclasses import dataclass
2+
3+
from common.data_quality.completeness import DataQualityCompletenessChecker
4+
from common.data_quality.validator import DataQualityValidator
5+
from common.models.fhir_converter.converter import Converter
6+
from common.models.fhir_converter.mappings import ActionFlag
7+
8+
9+
@dataclass
10+
class DataQualityOutput:
11+
incomplete_fields: dict[str, list[str]]
12+
invalid_fields: list[str]
13+
timeliness: dict[str, int]
14+
15+
16+
class DataQualityChecker:
17+
"""Runs data quality checks against an Immunisation and creates a Data Quality Output object"""
18+
19+
def __init__(
20+
self,
21+
immunisation: dict,
22+
action_flag: ActionFlag,
23+
completeness_checker: DataQualityCompletenessChecker,
24+
data_quality_validator: DataQualityValidator,
25+
is_batch_csv: bool,
26+
):
27+
self.immunisation = immunisation
28+
self.fhir_converter = Converter(fhir_data=immunisation, action_flag=action_flag)
29+
self.completeness_checker = completeness_checker
30+
self.data_quality_validator = data_quality_validator
31+
self.is_batch_csv = is_batch_csv
32+
33+
def run_checks(self) -> DataQualityOutput:
34+
if not self.is_batch_csv:
35+
self.immunisation = self.fhir_converter.run_conversion()
36+
37+
return DataQualityOutput(
38+
incomplete_fields=self._check_completeness(),
39+
invalid_fields=self._check_validity(),
40+
timeliness=self._check_timeliness(),
41+
)
42+
43+
def _check_completeness(self) -> dict[str, list[str]]:
44+
pass
45+
46+
def _check_validity(self) -> list[str]:
47+
pass
48+
49+
def _check_timeliness(self) -> dict[str, int]:
50+
pass
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class DataQualityCompletenessChecker:
2+
def check_completeness(self, immunisation: dict) -> dict[str, list[str]]:
3+
pass

lambdas/shared/src/common/data_quality/reporter.py

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class DataQualityValidator:
2+
pass

lambdas/shared/src/common/models/fhir_converter/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)