Skip to content

Commit 2d1b23f

Browse files
authored
VED-329: Delta-Record-Fix (#548)
fix recorded date field
1 parent 686e3fc commit 2d1b23f

File tree

5 files changed

+44
-26
lines changed

5 files changed

+44
-26
lines changed

backend/src/models/fhir_immunization_pre_validators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ def pre_validate_recorded(self, values: dict) -> dict:
519519
"""
520520
try:
521521
recorded = values["recorded"]
522-
PreValidation.for_date_time(recorded, "recorded")
522+
PreValidation.for_date_time(recorded, "recorded",strict_timezone=False)
523523
except KeyError:
524524
pass
525525

backend/src/models/utils/pre_validator_utils.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def for_date(field_value: str, field_location: str):
9898
) from value_error
9999

100100
@staticmethod
101-
def for_date_time(field_value: str, field_location: str):
101+
def for_date_time(field_value: str, field_location: str, strict_timezone: bool = True):
102102
"""
103103
Apply pre-validation to a datetime field to ensure that it is a string (JSON dates must be written as strings)
104104
containing a valid datetime. Note that partial dates are valid for FHIR, but are not allowed for this API.
@@ -120,9 +120,11 @@ def for_date_time(field_value: str, field_location: str):
120120
"- 'YYYY-MM-DDThh:mm:ss.f' — Full date and time with milliseconds (any level of precision)"
121121
"- 'YYYY-MM-DDThh:mm:ss%z' — Full date and time with timezone (e.g. +00:00 or +01:00)"
122122
"- 'YYYY-MM-DDThh:mm:ss.f%z' — Full date and time with milliseconds and timezone"
123-
"Only '+00:00' and '+01:00' are accepted as valid timezone offsets."
124-
f"Note that partial dates are not allowed for {field_location} in this service."
125123
)
124+
125+
if strict_timezone:
126+
error_message += "Only '+00:00' and '+01:00' are accepted as valid timezone offsets.\n"
127+
error_message += f"Note that partial dates are not allowed for {field_location} in this service."
126128

127129
allowed_suffixes = {"+00:00", "+01:00", "+0000", "+0100",}
128130

@@ -136,7 +138,7 @@ def for_date_time(field_value: str, field_location: str):
136138
try:
137139
fhir_date = datetime.strptime(field_value, fmt)
138140

139-
if fhir_date.tzinfo is not None:
141+
if strict_timezone and fhir_date.tzinfo is not None:
140142
if not any(field_value.endswith(suffix) for suffix in allowed_suffixes):
141143
raise ValueError(error_message)
142144
return fhir_date.isoformat()

backend/tests/test_immunization_pre_validator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ def test_pre_validate_practitioner_name_family(self):
645645

646646
def test_pre_validate_recorded(self):
647647
"""Test pre_validate_recorded accepts valid values and rejects invalid values"""
648-
ValidatorModelTests.test_date_time_value(self, field_location="recorded")
648+
ValidatorModelTests.test_date_time_value(self, field_location="recorded", is_occurrence_date_time=False)
649649

650650
def test_pre_validate_primary_source(self):
651651
"""Test pre_validate_primary_source accepts valid values and rejects invalid values"""

backend/tests/utils/pre_validation_test_utils.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -328,11 +328,29 @@ def test_date_time_value(
328328
* Invalid date time string formats
329329
* Invalid date-times
330330
"""
331+
expected_error_message = (
332+
f"{field_location} must be a valid datetime in one of the following formats:"
333+
"- 'YYYY-MM-DD' — Full date only"
334+
"- 'YYYY-MM-DDThh:mm:ss' — Full date and time without milliseconds"
335+
"- 'YYYY-MM-DDThh:mm:ss.f' — Full date and time with milliseconds (any level of precision)"
336+
"- 'YYYY-MM-DDThh:mm:ss%z' — Full date and time with timezone (e.g. +00:00 or +01:00)"
337+
"- 'YYYY-MM-DDThh:mm:ss.f%z' — Full date and time with milliseconds and timezone"
338+
)
339+
340+
if is_occurrence_date_time:
341+
expected_error_message += "Only '+00:00' and '+01:00' are accepted as valid timezone offsets.\n"
342+
expected_error_message += f"Note that partial dates are not allowed for {field_location} in this service."
343+
valid_datetime_formats = ValidValues.for_date_times_strict_timezones
344+
invalid_datetime_formats = InvalidValues.for_date_time_string_formats_for_strict_timezone
345+
else:
346+
# For recorded, skip values that are valid ISO with non-restricted timezone
347+
valid_datetime_formats = ValidValues.for_date_times_relaxed_timezones
348+
invalid_datetime_formats = InvalidValues.for_date_time_string_formats_for_relaxed_timezone
331349

332350
valid_json_data = deepcopy(test_instance.json_data)
333351

334352
# Test that valid data is accepted
335-
test_valid_values_accepted(test_instance, valid_json_data, field_location, ValidValues.for_date_times)
353+
test_valid_values_accepted(test_instance, valid_json_data, field_location, valid_datetime_formats)
336354

337355
# Set list of invalid data types to test
338356
invalid_data_types_for_strings = InvalidDataTypes.for_strings
@@ -349,19 +367,8 @@ def test_date_time_value(
349367
expected_error_message=f"{field_location} must be a string",
350368
)
351369

352-
expected_error_message = (
353-
f"{field_location} must be a valid datetime in one of the following formats:"
354-
"- 'YYYY-MM-DD' — Full date only"
355-
"- 'YYYY-MM-DDThh:mm:ss' — Full date and time without milliseconds"
356-
"- 'YYYY-MM-DDThh:mm:ss.f' — Full date and time with milliseconds (any level of precision)"
357-
"- 'YYYY-MM-DDThh:mm:ss%z' — Full date and time with timezone (e.g. +00:00 or +01:00)"
358-
"- 'YYYY-MM-DDThh:mm:ss.f%z' — Full date and time with milliseconds and timezone"
359-
"Only '+00:00' and '+01:00' are accepted as valid timezone offsets."
360-
f"Note that partial dates are not allowed for {field_location} in this service."
361-
)
362-
363370
# Test invalid date time string formats
364-
for invalid_occurrence_date_time in InvalidValues.for_date_time_string_formats:
371+
for invalid_occurrence_date_time in invalid_datetime_formats:
365372
test_invalid_values_rejected(
366373
test_instance,
367374
valid_json_data,

backend/tests/utils/values_for_tests.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class ValidValues:
3131

3232
nhs_number = "9990548609"
3333

34-
for_date_times = [
34+
for_date_times_strict_timezones = [
3535
"2000-01-01", # Full date only
3636
"2000-01-01T00:00:00+00:00", # Time and offset all zeroes
3737
"2000-01-01T10:34:27", # Date with Time only
@@ -44,6 +44,12 @@ class ValidValues:
4444
"1933-12-31T11:11:11.111111+00:00", # DateTime with milliseconds to 6 decimal places
4545
]
4646

47+
for_date_times_relaxed_timezones = for_date_times_strict_timezones + [
48+
"2000-01-01T00:00:00+05:00", # Time and offset all zeroes
49+
"1933-12-31T11:11:11-01:00", # Negative offset (with hours and minutes not 0)
50+
"1933-12-31T11:11:11.1-05:00", # DateTime with milliseconds to 1 decimal place
51+
]
52+
4753
for_strings_with_any_length_chars = (
4854
"This is a really long string with more than 100 characters to test whether the validator is working well!! "
4955
)
@@ -281,7 +287,7 @@ class InvalidValues:
281287
]
282288

283289
# Strings which are not in acceptable date time format
284-
for_date_time_string_formats = [
290+
for_date_time_string_formats_for_relaxed_timezone = [
285291
"", # Empty string
286292
"invalid", # Invalid format
287293
"20000101", # Date digits only (i.e. without hypens)
@@ -290,10 +296,6 @@ class InvalidValues:
290296
"2000", # Year only
291297
"2000-01", # Year and month only
292298
"2000-01-01T00:00:00+00", # Date and time with GMT timezone offset only in hours
293-
"2000-01-01T00:00:00-00:00", # Date and time with negative GMT timezone offset
294-
"2000-01-01T00:00:00-01:00", # Date and time with negative GMT timezone offset
295-
"2000-01-01T00:00:00-05:00", # Date and time with negative offset asides from GMT and BST
296-
"2000-01-01T00:00:00+05:00", # Date and time with offset asides from GMT and BST
297299
"2000-01-01T00:00:00+01", # Date and time with BST timezone offset only in hours
298300
"12000-01-01T00:00:00+00:00", # Extra character at start of string
299301
"2000-01-01T00:00:00+00:001", # Extra character at end of string
@@ -302,7 +304,6 @@ class InvalidValues:
302304
"2000-01-0122:22:22+00:00.000", # Missing T (with milliseconds)
303305
"2000-01-01T222222+00:00", # Missing time colons
304306
"2000-01-01T22:22:2200:00", # Missing timezone indicator
305-
"2000-01-01T22:22:22-0100", # Missing timezone colon
306307
"2000-01-01T22:22:22-01", # Timezone hours only
307308
"99-01-01T00:00:00+00:00", # Missing century (i.e. only 2 digits for year)
308309
"01-01-2000T00:00:00+00:00", # Date in wrong order (DD-MM-YYYY)
@@ -323,6 +324,14 @@ class InvalidValues:
323324
"2000-01-01T00:00:00+00:60", # Timezone minute 60
324325
]
325326

327+
for_date_time_string_formats_for_strict_timezone = for_date_time_string_formats_for_relaxed_timezone + [
328+
"2000-01-01T22:22:22-0100", # Missing timezone colon
329+
"2000-01-01T00:00:00-01:00", # Date and time with negative GMT timezone offset
330+
"2000-01-01T00:00:00-05:00", # Date and time with negative offset asides from GMT and BST
331+
"2000-01-01T00:00:00+05:00", # Date and time with offset asides from GMT and BST
332+
"2000-01-01T00:00:00-00:00", # Date and time with negative GMT timezone offset
333+
]
334+
326335
for_lists_of_strings_of_length_1 = [[1], [False], [["Test1"]]]
327336

328337
for_lists_of_dicts_of_length_1 = [[1], [False], [["Invalid"]], ["Invalid"]]

0 commit comments

Comments
 (0)