Skip to content

Commit 7a8ed05

Browse files
committed
recorded date
1 parent f90c55f commit 7a8ed05

File tree

5 files changed

+65
-10
lines changed

5 files changed

+65
-10
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_recorded_date_time(recorded, "recorded")
523523
except KeyError:
524524
pass
525525

backend/src/models/utils/pre_validator_utils.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,18 @@ def for_date(field_value: str, field_location: str):
9696
raise ValueError(
9797
f'{field_location} must be a valid date string in the format "YYYY-MM-DD"'
9898
) from value_error
99+
100+
@staticmethod
101+
def for_recorded_date_time(field_value: str, field_location: str):
102+
"""
103+
Relaxed validator for the 'recorded' field.
104+
Accepts any timezone or no timezone.
105+
"""
106+
return PreValidation.for_date_time(field_value, field_location, strict_timezone=False)
107+
99108

100109
@staticmethod
101-
def for_date_time(field_value: str, field_location: str):
110+
def for_date_time(field_value: str, field_location: str, strict_timezone: bool = True):
102111
"""
103112
Apply pre-validation to a datetime field to ensure that it is a string (JSON dates must be written as strings)
104113
containing a valid datetime. Note that partial dates are valid for FHIR, but are not allowed for this API.
@@ -120,9 +129,11 @@ def for_date_time(field_value: str, field_location: str):
120129
"- 'YYYY-MM-DDThh:mm:ss.f' — Full date and time with milliseconds (any level of precision)"
121130
"- 'YYYY-MM-DDThh:mm:ss%z' — Full date and time with timezone (e.g. +00:00 or +01:00)"
122131
"- '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."
125132
)
133+
134+
if strict_timezone:
135+
error_message += "Only '+00:00' and '+01:00' are accepted as valid timezone offsets.\n"
136+
error_message += f"Note that partial dates are not allowed for {field_location} in this service."
126137

127138
allowed_suffixes = {"+00:00", "+01:00", "+0000", "+0100",}
128139

@@ -136,7 +147,7 @@ def for_date_time(field_value: str, field_location: str):
136147
try:
137148
fhir_date = datetime.strptime(field_value, fmt)
138149

139-
if fhir_date.tzinfo is not None:
150+
if strict_timezone and fhir_date.tzinfo is not None:
140151
if not any(field_value.endswith(suffix) for suffix in allowed_suffixes):
141152
raise ValueError(error_message)
142153
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: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,12 +356,22 @@ def test_date_time_value(
356356
"- 'YYYY-MM-DDThh:mm:ss.f' — Full date and time with milliseconds (any level of precision)"
357357
"- 'YYYY-MM-DDThh:mm:ss%z' — Full date and time with timezone (e.g. +00:00 or +01:00)"
358358
"- '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."
361359
)
362360

361+
if is_occurrence_date_time:
362+
expected_error_message += "Only '+00:00' and '+01:00' are accepted as valid timezone offsets.\n"
363+
expected_error_message += f"Note that partial dates are not allowed for {field_location} in this service."
364+
365+
if is_occurrence_date_time:
366+
invalid_datetime_formats = InvalidValues.for_date_time_string_formats
367+
invalid_datetime_values = InvalidValues.for_date_times
368+
else:
369+
# For recorded, skip values that are valid ISO with non-restricted timezone
370+
invalid_datetime_formats = InvalidValues.for_date_time_string_formats_for_recorded
371+
invalid_datetime_values = InvalidValues.for_date_times_for_recorded
372+
363373
# Test invalid date time string formats
364-
for invalid_occurrence_date_time in InvalidValues.for_date_time_string_formats:
374+
for invalid_occurrence_date_time in invalid_datetime_formats:
365375
test_invalid_values_rejected(
366376
test_instance,
367377
valid_json_data,
@@ -371,7 +381,7 @@ def test_date_time_value(
371381
)
372382

373383
# Test invalid date times
374-
for invalid_occurrence_date_time in InvalidValues.for_date_times:
384+
for invalid_occurrence_date_time in invalid_datetime_values:
375385
test_invalid_values_rejected(
376386
test_instance,
377387
valid_json_data,

backend/tests/utils/values_for_tests.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,40 @@ class InvalidValues:
323323
"2000-01-01T00:00:00+00:60", # Timezone minute 60
324324
]
325325

326+
for_date_time_string_formats_for_recorded = [
327+
"", # Empty string
328+
"invalid", # Invalid format
329+
"20000101", # Date digits only (i.e. without hypens)
330+
"20000101000000", # Date and time digits only
331+
"200001010000000000", # Date, time and timezone digits only
332+
"2000", # Year only
333+
"2000-01", # Year and month only
334+
"2000-01-01T00:00:00+00", # Date and time with GMT timezone offset only in hours
335+
"2000-01-01T00:00:00+01", # Date and time with BST timezone offset only in hours
336+
"12000-01-01T00:00:00+00:00", # Extra character at start of string
337+
"2000-01-01T00:00:00+00:001", # Extra character at end of string
338+
"12000-01-02T00:00:00-01:001", # Extra characters at start and end of string
339+
"2000-01-0122:22:22+00:00", # Missing T
340+
"2000-01-0122:22:22+00:00.000", # Missing T (with milliseconds)
341+
"2000-01-01T222222+00:00", # Missing time colons
342+
"2000-01-01T22:22:2200:00", # Missing timezone indicator
343+
"2000-01-01T22:22:22-01", # Timezone hours only
344+
"99-01-01T00:00:00+00:00", # Missing century (i.e. only 2 digits for year)
345+
"01-01-2000T00:00:00+00:00", # Date in wrong order (DD-MM-YYYY)
346+
]
347+
348+
for_date_times_for_recorded = [
349+
"2000-00-01T00:00:00+00:00", # Month 00
350+
"2000-13-01T00:00:00+00:00", # Month 13
351+
"2000-01-32T00:00:00+00:00", # Day 32
352+
"2000-02-30T00:00:00+00:00", # Invalid month and day combination (30th February)
353+
"2000-01-01T24:00:00+00:00", # Hour 24
354+
"2000-01-01T00:60:00+00:00", # Minute 60
355+
"2000-01-01T00:00:60+00:00", # Second 60
356+
"2000-01-01T00:00:00+00:60", # Timezone minute 60
357+
]
358+
359+
326360
for_lists_of_strings_of_length_1 = [[1], [False], [["Test1"]]]
327361

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

0 commit comments

Comments
 (0)