diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/converter/ConvertToObservationProps.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/converter/ConvertToObservationProps.java index b8646837f4..56680e1009 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/converter/ConvertToObservationProps.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/converter/ConvertToObservationProps.java @@ -1,6 +1,7 @@ package gov.cdc.usds.simplereport.api.converter; import gov.cdc.usds.simplereport.db.model.auxiliary.TestCorrectionStatus; +import java.util.Date; import lombok.Builder; import lombok.Getter; @@ -17,4 +18,5 @@ public class ConvertToObservationProps { private String testkitNameId; private String equipmentUid; private String deviceModel; + private Date issued; } diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/converter/FhirConverter.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/converter/FhirConverter.java index d475e57b2d..8f21bb22e2 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/converter/FhirConverter.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/converter/FhirConverter.java @@ -33,6 +33,7 @@ import static gov.cdc.usds.simplereport.api.converter.FhirConstants.UNIVERSAL_ID_SYSTEM; import static gov.cdc.usds.simplereport.api.converter.FhirConstants.YESNO_CODE_SYSTEM; +import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; @@ -489,7 +490,8 @@ public List convertToObservation( Set results, DeviceType deviceType, TestCorrectionStatus correctionStatus, - String correctionReason) { + String correctionReason, + Date resultDate) { return results.stream() .map( result -> { @@ -515,7 +517,8 @@ public List convertToObservation( correctionReason, testkitNameId, equipmentUid, - deviceType.getModel()); + deviceType.getModel(), + resultDate); }) .collect(Collectors.toList()); } @@ -534,7 +537,8 @@ public Observation convertToObservation( String correctionReason, String testkitNameId, String equipmentUid, - String deviceModel) { + String deviceModel, + Date resultDate) { if (result != null && result.getDisease() != null) { return convertToObservation( @@ -550,6 +554,7 @@ public Observation convertToObservation( .testkitNameId(testkitNameId) .equipmentUid(equipmentUid) .deviceModel(deviceModel) + .issued(resultDate) .build()); } return null; @@ -582,6 +587,9 @@ public Observation convertToObservation(ConvertToObservationProps props) { .addInterpretation() .addCoding(convertToAbnormalFlagInterpretation(props.getResultCode())); + observation.setIssued(props.getIssued()); + observation.getIssuedElement().setTimeZoneZulu(true).setPrecision(TemporalPrecisionEnum.SECOND); + return observation; } @@ -793,7 +801,7 @@ public DiagnosticReport convertToDiagnosticReport( var diagnosticReport = new DiagnosticReport() .setStatus(status) - .setEffective(new DateTimeType(dateTested)) + .setEffective(new DateTimeType(dateTested).setTimeZoneZulu(true)) .setIssued(dateUpdated); // Allows EffectiveDateTimeType to be mocked during tests @@ -834,7 +842,8 @@ public Bundle createFhirBundle( testEvent.getResults(), testEvent.getDeviceType(), testEvent.getCorrectionStatus(), - testEvent.getReasonForCorrection())) + testEvent.getReasonForCorrection(), + testEvent.getDateTested())) .aoeObservations( convertToAOEObservations( testEvent.getInternalId().toString(), testEvent.getSurveyData())) diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java b/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java index caf1b3b8af..e545d35cc8 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java @@ -25,8 +25,10 @@ import gov.cdc.usds.simplereport.service.ResultsUploaderDeviceValidationService; import java.io.InputStream; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -127,6 +129,17 @@ private Bundle convertRowToFhirBundle(TestResultRow row, UUID orgId) { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("M/d/yyyy[ HH:mm]"); var testEventId = row.getAccessionNumber().getValue(); + + LocalDateTime testResultDate; + TemporalAccessor temporalAccessor = + dateTimeFormatter.parseBest( + row.getTestResultDate().getValue(), LocalDateTime::from, LocalDate::from); + if (temporalAccessor instanceof LocalDateTime) { + testResultDate = (LocalDateTime) temporalAccessor; + } else { + testResultDate = ((LocalDate) temporalAccessor).atStartOfDay(); + } + var patientAddr = new StreetAddress( row.getPatientStreet().getValue(), @@ -309,6 +322,9 @@ private Bundle convertRowToFhirBundle(TestResultRow row, UUID orgId) { .testkitNameId(testKitNameId) .equipmentUid(equipmentUid) .deviceModel(row.getEquipmentModelName().getValue()) + .issued( + Date.from( + testResultDate.atZone(zoneIdGenerator.getSystemZoneId()).toInstant())) .build())); LocalDate symptomOnsetDate = null; @@ -336,13 +352,12 @@ private Bundle convertRowToFhirBundle(TestResultRow row, UUID orgId) { testOrderLoinc, uuidGenerator.randomUUID().toString()); - var testDate = LocalDate.parse(row.getTestResultDate().getValue(), dateTimeFormatter); var diagnosticReport = fhirConverter.convertToDiagnosticReport( mapTestResultStatusToFhirValue(row.getTestResultStatus().getValue()), testPerformedCode, testEventId, - Date.from(testDate.atStartOfDay(zoneIdGenerator.getSystemZoneId()).toInstant()), + Date.from(testResultDate.atZone(zoneIdGenerator.getSystemZoneId()).toInstant()), dateGenerator.newDate()); return fhirConverter.createFhirBundle( diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/api/converter/FhirConverterTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/api/converter/FhirConverterTest.java index c2e7cbc1e1..dbc03fabd2 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/api/converter/FhirConverterTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/api/converter/FhirConverterTest.java @@ -609,6 +609,7 @@ void convertToObservation_Strings_valid() { .testkitNameId("testKitName") .equipmentUid("equipmentUid") .deviceModel("modelName") + .issued(Date.from(Instant.parse("2023-06-10T23:15:00.00Z"))) .build()); assertThat(actual.getId()).isEqualTo("id-123"); @@ -625,6 +626,7 @@ void convertToObservation_Strings_valid() { .isEqualTo("resultCode"); assertThat(actual.getNote()).isEmpty(); assertThat(actual.getMethod().getCodingFirstRep().getDisplay()).isEqualTo("modelName"); + assertThat(actual.getIssued()).isEqualTo("2023-06-10T23:15:00.00Z"); } @Test @@ -642,7 +644,8 @@ void convertToObservation_Result_valid() { null, "testkitName", "equipmentUid", - "modelName"); + "modelName", + Date.from(Instant.parse("2023-06-10T23:15:00.00Z"))); assertThat(actual.getId()).isEqualTo(internalId.toString()); assertThat(actual.getStatus().getDisplay()).isEqualTo(ObservationStatus.FINAL.getDisplay()); @@ -657,6 +660,7 @@ void convertToObservation_Result_valid() { .isEqualTo("260373001"); assertThat(actual.getNote()).isEmpty(); assertThat(actual.getMethod().getCodingFirstRep().getDisplay()).isEqualTo("modelName"); + assertThat(actual.getIssued()).isEqualTo("2023-06-10T23:15:00.00Z"); } @Test @@ -674,7 +678,8 @@ void convertToObservation_Result_correction() { "Oopsy Daisy", "testkitName", "equipmentUid", - "modelName"); + "modelName", + new Date()); assertThat(actual.getId()).isEqualTo(internalId.toString()); assertThat(actual.getStatus().getDisplay()).isEqualTo(ObservationStatus.CORRECTED.getDisplay()); @@ -697,7 +702,8 @@ void convertToObservation_Result_removeWithoutReason() { null, "testkitName", "equipmentUid", - "modelName"); + "modelName", + new Date()); assertThat(actual.getId()).isEqualTo(internalId.toString()); assertThat(actual.getStatus().getDisplay()) @@ -716,7 +722,8 @@ void convertToObservation_Result_null() { null, "testkitName", "equipmentUid", - "modelName"); + "modelName", + new Date()); assertThat(actual).isNull(); } @@ -733,7 +740,8 @@ void convertToObservation_Result_nullDisease() { null, "testkitName", "equipmentUid", - "modelName"); + "modelName", + new Date()); assertThat(actual).isNull(); } @@ -765,7 +773,11 @@ void convertToObservation_Result_matchesJson() throws IOException { var actual = fhirConverter.convertToObservation( - Set.of(covidResult, fluResult), device, TestCorrectionStatus.ORIGINAL, null); + Set.of(covidResult, fluResult), + device, + TestCorrectionStatus.ORIGINAL, + null, + Date.from(Instant.parse("2023-06-10T23:15:00.00Z"))); assertThat(actual).hasSize(2); String covidSerialized = @@ -815,7 +827,11 @@ void convertToObservation_Result_correctionMatchesJson() throws IOException { var actual = fhirConverter.convertToObservation( - Set.of(result), device, TestCorrectionStatus.CORRECTED, "woops"); + Set.of(result), + device, + TestCorrectionStatus.CORRECTED, + "woops", + Date.from(Instant.parse("2023-06-10T23:15:00.00Z"))); String actualSerialized = parser.encodeResourceToString(actual.get(0)); var expectedSerialized1 = @@ -936,7 +952,8 @@ void convertToDiagnosticReport_TestEvent_matchesJson() throws IOException { StandardCharsets.UTF_8); expectedSerialized = expectedSerialized.replace( - "$EFFECTIVE_DATE_TIME_TESTED", new DateTimeType(date).getValueAsString()); + "$EFFECTIVE_DATE_TIME_TESTED", + new DateTimeType(date).setTimeZoneZulu(true).getValueAsString()); JSONAssert.assertEquals(expectedSerialized, actualSerialized, true); } @@ -953,8 +970,8 @@ void convertToDiagnosticReport_Strings_valid() { assertThat(actual.getCode().getCoding()).hasSize(1); assertThat(actual.getCode().getCodingFirstRep().getSystem()).isEqualTo("http://loinc.org"); assertThat(actual.getCode().getCodingFirstRep().getCode()).isEqualTo("95422-2"); - assertThat(((DateTimeType) actual.getEffective()).getAsV3()) - .isEqualTo(new DateTimeType(date).getAsV3()); + assertThat(((DateTimeType) actual.getEffective()).getValueAsString()) + .isEqualTo(new DateTimeType(date).setTimeZoneZulu(true).getValueAsString()); assertThat((actual.getIssued())).isEqualTo(date); } @@ -1461,7 +1478,8 @@ void createFhirBundle_TestEvent_matchesJson() throws IOException { expectedSerialized = expectedSerialized.replace("$PROVENANCE_ID", provenanceId); expectedSerialized = expectedSerialized.replace( - "$EFFECTIVE_DATE_TIME_TESTED", new DateTimeType(dateTested).getValueAsString()); + "$EFFECTIVE_DATE_TIME_TESTED", + new DateTimeType(dateTested).setTimeZoneZulu(true).getValueAsString()); expectedSerialized = expectedSerialized.replace( "$PROVENANCE_RECORDED_DATE", diff --git a/backend/src/test/resources/fhir/bundle.json b/backend/src/test/resources/fhir/bundle.json index 782bbc6a6d..b85863096b 100644 --- a/backend/src/test/resources/fhir/bundle.json +++ b/backend/src/test/resources/fhir/bundle.json @@ -450,7 +450,8 @@ } } ] - } + }, + "issued": "$EFFECTIVE_DATE_TIME_TESTED" } }, { @@ -522,7 +523,8 @@ } } ] - } + }, + "issued": "$EFFECTIVE_DATE_TIME_TESTED" } }, { @@ -594,7 +596,8 @@ } } ] - } + }, + "issued": "$EFFECTIVE_DATE_TIME_TESTED" } }, { diff --git a/backend/src/test/resources/fhir/observationCorrection.json b/backend/src/test/resources/fhir/observationCorrection.json index 7b24fc1d0b..45ed6bebed 100644 --- a/backend/src/test/resources/fhir/observationCorrection.json +++ b/backend/src/test/resources/fhir/observationCorrection.json @@ -51,5 +51,6 @@ { "text": "Corrected Result: woops" } - ] + ], + "issued": "2023-06-10T23:15:00Z" } diff --git a/backend/src/test/resources/fhir/observationCovid.json b/backend/src/test/resources/fhir/observationCovid.json index 960f245b35..0a38414f6c 100644 --- a/backend/src/test/resources/fhir/observationCovid.json +++ b/backend/src/test/resources/fhir/observationCovid.json @@ -51,5 +51,6 @@ } } ] - } + }, + "issued": "2023-06-10T23:15:00Z" } diff --git a/backend/src/test/resources/fhir/observationFlu.json b/backend/src/test/resources/fhir/observationFlu.json index ad107c49af..81c1ce0942 100644 --- a/backend/src/test/resources/fhir/observationFlu.json +++ b/backend/src/test/resources/fhir/observationFlu.json @@ -51,5 +51,6 @@ } } ] - } + }, + "issued": "2023-06-10T23:15:00Z" } diff --git a/backend/src/test/resources/testResultUpload/test-results-upload-valid-as-fhir.json b/backend/src/test/resources/testResultUpload/test-results-upload-valid-as-fhir.json index d1509be675..ea150170ed 100644 --- a/backend/src/test/resources/testResultUpload/test-results-upload-valid-as-fhir.json +++ b/backend/src/test/resources/testResultUpload/test-results-upload-valid-as-fhir.json @@ -116,7 +116,7 @@ "subject": { "reference": "Patient/1234" }, - "effectiveDateTime": "2021-12-20T00:00:00Z", + "effectiveDateTime": "2021-12-20T14:00:00Z", "issued": "2023-05-24T19:33:06.472Z", "specimen": [ { @@ -486,7 +486,8 @@ }, "device": { "reference": "Device/00000000-0000-0000-0000-000000000003" - } + }, + "issued": "2021-12-20T14:00:00Z" } }, {