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 c78456f3cd..72649802d1 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 @@ -308,46 +308,107 @@ public static Optional convertToTribalAffiliationExtension(String tri } public static Practitioner convertToPractitioner(Provider provider) { - var practitioner = new Practitioner(); - practitioner.setId(provider.getInternalId().toString()); - practitioner.addName(convertToHumanName(provider.getNameInfo())); - practitioner.addAddress(convertToAddress(provider.getAddress(), DEFAULT_COUNTRY)); - practitioner.addTelecom(convertToContactPoint(ContactPointUse.WORK, provider.getTelephone())); + return convertToPractitioner( + provider.getInternalId().toString(), + provider.getNameInfo(), + provider.getTelephone(), + provider.getAddress(), + DEFAULT_COUNTRY); + } + + public static Practitioner convertToPractitioner( + String id, PersonName name, String telephone, StreetAddress addr, String country) { + var practitioner = + new Practitioner() + .addName(convertToHumanName(name)) + .addAddress(convertToAddress(addr, country)) + .addTelecom(convertToContactPoint(ContactPointUse.WORK, telephone)); + practitioner.setId(id); return practitioner; } public static Organization convertToOrganization(Facility facility) { - var org = new Organization(); - org.setId(facility.getInternalId().toString()); + return convertToOrganization( + facility.getInternalId().toString(), + facility.getFacilityName(), + facility.getCliaNumber(), + facility.getTelephone(), + facility.getEmail(), + facility.getAddress(), + DEFAULT_COUNTRY); + } + + public static Organization convertToOrganization( + String id, + String name, + String clia, + String telephone, + String email, + StreetAddress addr, + String country) { + var org = + new Organization() + .setName(name) + .addAddress(convertToAddress(addr, country)) + .addTelecom(convertToContactPoint(ContactPointUse.WORK, telephone)); + org.addIdentifier() .setUse(IdentifierUse.OFFICIAL) - .setValue(facility.getCliaNumber()) + .setValue(clia) .getType() .addCoding() .setSystem(UNIVERSAL_ID_SYSTEM) .setCode("CLIA"); - org.setName(facility.getFacilityName()); - org.addTelecom(convertToContactPoint(ContactPointUse.WORK, facility.getTelephone())); - org.addTelecom(convertEmailToContactPoint(ContactPointUse.WORK, facility.getEmail())); - org.addAddress(convertToAddress(facility.getAddress(), DEFAULT_COUNTRY)); + + if (email != null && !email.isBlank()) { + org.addTelecom(convertEmailToContactPoint(ContactPointUse.WORK, email)); + } + org.setId(id); return org; } public static Patient convertToPatient(Person person) { - var patient = new Patient(); - patient.setId(person.getInternalId().toString()); - patient.addIdentifier().setValue(person.getInternalId().toString()); - patient.addName(convertToHumanName(person.getNameInfo())); - convertPhoneNumbersToContactPoint(person.getPhoneNumbers()).forEach(patient::addTelecom); - convertEmailsToContactPoint(ContactPointUse.HOME, person.getEmails()) - .forEach(patient::addTelecom); - patient.setGender(convertToAdministrativeGender(person.getGender())); - patient.setBirthDate(convertToDate(person.getBirthDate())); - patient.addAddress(convertToAddress(person.getAddress(), person.getCountry())); - patient.addExtension(convertToRaceExtension(person.getRace())); - patient.addExtension(convertToEthnicityExtension(person.getEthnicity())); - patient.addExtension( - convertToTribalAffiliationExtension(person.getTribalAffiliation()).orElse(null)); + return convertToPatient( + person.getInternalId().toString(), + person.getNameInfo(), + person.getPhoneNumbers(), + person.getEmails(), + person.getGender(), + person.getBirthDate(), + person.getAddress(), + person.getCountry(), + person.getRace(), + person.getEthnicity(), + person.getTribalAffiliation()); + } + + public static Patient convertToPatient( + String id, + PersonName name, + List phoneNumbers, + List emails, + String gender, + LocalDate dob, + StreetAddress address, + String country, + String race, + String ethnicity, + List tribalAffiliations) { + var patient = + new Patient() + .addName(convertToHumanName(name)) + .setGender(convertToAdministrativeGender(gender)) + .setBirthDate(convertToDate(dob)) + .addAddress(convertToAddress(address, country)); + + patient.addExtension(convertToRaceExtension(race)); + patient.addExtension(convertToEthnicityExtension(ethnicity)); + patient.addExtension(convertToTribalAffiliationExtension(tribalAffiliations).orElse(null)); + + patient.setId(id); + patient.addIdentifier().setValue(id); + convertPhoneNumbersToContactPoint(phoneNumbers).forEach(patient::addTelecom); + convertEmailsToContactPoint(ContactPointUse.HOME, emails).forEach(patient::addTelecom); return patient; } @@ -356,13 +417,15 @@ public static Device convertToDevice(@NotNull DeviceType deviceType) { deviceType.getManufacturer(), deviceType.getModel(), deviceType.getInternalId().toString()); } - public static Device convertToDevice( - @NotNull String manufacturer, @NotNull String model, String id) { + public static Device convertToDevice(String manufacturer, @NotNull String model, String id) { var device = new Device() - .setManufacturer(manufacturer) .addDeviceName( new DeviceDeviceNameComponent().setName(model).setType(DeviceNameType.MODELNAME)); + if (manufacturer != null) { + device.setManufacturer(manufacturer); + } + device.setId(id); return device; } @@ -696,6 +759,7 @@ public static Bundle createFhirBundle( return createFhirBundle( convertToPatient(testEvent.getPatient()), convertToOrganization(testEvent.getFacility()), + null, convertToPractitioner(testEvent.getProviderData()), convertToDevice(testEvent.getDeviceType()), convertToSpecimen(testEvent.getSpecimenType()), @@ -714,7 +778,8 @@ public static Bundle createFhirBundle( public static Bundle createFhirBundle( Patient patient, - Organization organization, + Organization testingLab, + Organization orderingFacility, Practitioner practitioner, Device device, Specimen specimen, @@ -726,19 +791,28 @@ public static Bundle createFhirBundle( GitProperties gitProperties, String processingId) { var patientFullUrl = ResourceType.Patient + "/" + patient.getId(); - var organizationFullUrl = ResourceType.Organization + "/" + organization.getId(); + var orderingFacilityFullUrl = + orderingFacility == null + ? null + : ResourceType.Organization + "/" + orderingFacility.getId(); + var testingLabOrganizationFullUrl = ResourceType.Organization + "/" + testingLab.getId(); var practitionerFullUrl = ResourceType.Practitioner + "/" + practitioner.getId(); var specimenFullUrl = ResourceType.Specimen + "/" + specimen.getId(); var serviceRequestFullUrl = ResourceType.ServiceRequest + "/" + serviceRequest.getId(); var diagnosticReportFullUrl = ResourceType.DiagnosticReport + "/" + diagnosticReport.getId(); var deviceFullUrl = ResourceType.Device + "/" + device.getId(); - var practitionerRole = createPractitionerRole(organizationFullUrl, practitionerFullUrl); - var provenance = createProvenance(organizationFullUrl, currentDate); + var practitionerRole = + createPractitionerRole( + orderingFacilityFullUrl == null + ? testingLabOrganizationFullUrl + : orderingFacilityFullUrl, + practitionerFullUrl); + var provenance = createProvenance(testingLabOrganizationFullUrl, currentDate); var provenanceFullUrl = ResourceType.Provenance + "/" + provenance.getId(); var messageHeader = createMessageHeader( - organizationFullUrl, + testingLabOrganizationFullUrl, diagnosticReportFullUrl, provenanceFullUrl, gitProperties, @@ -746,11 +820,11 @@ public static Bundle createFhirBundle( var practitionerRoleFullUrl = ResourceType.PractitionerRole + "/" + practitionerRole.getId(); var messageHeaderFullUrl = ResourceType.MessageHeader + "/" + messageHeader.getId(); - patient.setManagingOrganization(new Reference(organizationFullUrl)); + patient.setManagingOrganization(new Reference(testingLabOrganizationFullUrl)); specimen.setSubject(new Reference(patientFullUrl)); serviceRequest.setSubject(new Reference(patientFullUrl)); - serviceRequest.addPerformer(new Reference(organizationFullUrl)); + serviceRequest.addPerformer(new Reference(testingLabOrganizationFullUrl)); serviceRequest.setRequester(new Reference(practitionerRoleFullUrl)); diagnosticReport.addBasedOn(new Reference(serviceRequestFullUrl)); diagnosticReport.setSubject(new Reference(patientFullUrl)); @@ -761,7 +835,10 @@ public static Bundle createFhirBundle( entryList.add(Pair.of(provenanceFullUrl, provenance)); entryList.add(Pair.of(diagnosticReportFullUrl, diagnosticReport)); entryList.add(Pair.of(patientFullUrl, patient)); - entryList.add(Pair.of(organizationFullUrl, organization)); + entryList.add(Pair.of(testingLabOrganizationFullUrl, testingLab)); + if (orderingFacilityFullUrl != null) { + entryList.add(Pair.of(orderingFacilityFullUrl, orderingFacility)); + } entryList.add(Pair.of(practitionerFullUrl, practitioner)); entryList.add(Pair.of(specimenFullUrl, specimen)); entryList.add(Pair.of(serviceRequestFullUrl, serviceRequest)); @@ -777,7 +854,7 @@ public static Bundle createFhirBundle( var observationFullUrl = ResourceType.Observation + "/" + observation.getId(); observation.setSubject(new Reference(patientFullUrl)); - observation.addPerformer(new Reference(organizationFullUrl)); + observation.addPerformer(new Reference(testingLabOrganizationFullUrl)); observation.setSpecimen(new Reference(specimenFullUrl)); observation.setDevice(new Reference(deviceFullUrl)); @@ -785,15 +862,17 @@ public static Bundle createFhirBundle( entryList.add(Pair.of(observationFullUrl, observation)); }); - aoeObservations.forEach( - observation -> { - var observationFullUrl = ResourceType.Observation + "/" + observation.getId(); + if (aoeObservations != null) { + aoeObservations.forEach( + observation -> { + var observationFullUrl = ResourceType.Observation + "/" + observation.getId(); - observation.setSubject(new Reference(patientFullUrl)); + observation.setSubject(new Reference(patientFullUrl)); - serviceRequest.addSupportingInfo(new Reference(observationFullUrl)); - entryList.add(Pair.of(observationFullUrl, observation)); - }); + serviceRequest.addSupportingInfo(new Reference(observationFullUrl)); + entryList.add(Pair.of(observationFullUrl, observation)); + }); + } var bundle = new Bundle() diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/db/repository/DeviceTypeRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/db/repository/DeviceTypeRepository.java index 36f6b5eb78..384e01d9c2 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/db/repository/DeviceTypeRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/db/repository/DeviceTypeRepository.java @@ -4,10 +4,18 @@ import java.util.Collection; import java.util.List; import java.util.UUID; +import org.springframework.data.jpa.repository.EntityGraph; /** Specification of EternalAuditedEntityRepository for {@link DeviceType} manipulation. */ public interface DeviceTypeRepository extends EternalAuditedEntityRepository { List findAllByInternalIdIn(Collection ids); DeviceType findDeviceTypeByName(String name); + + @EntityGraph( + attributePaths = { + "supportedDiseaseTestPerformed", + "supportedDiseaseTestPerformed.supportedDisease" + }) + DeviceType findDeviceTypeByModelIgnoreCase(String model); } 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 new file mode 100644 index 0000000000..2e5b641652 --- /dev/null +++ b/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java @@ -0,0 +1,326 @@ +package gov.cdc.usds.simplereport.utils; + +import static gov.cdc.usds.simplereport.api.converter.FhirConstants.DEFAULT_COUNTRY; +import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.getIteratorForCsv; +import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.getNextRow; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import com.fasterxml.jackson.databind.MappingIterator; +import gov.cdc.usds.simplereport.api.converter.FhirConverter; +import gov.cdc.usds.simplereport.api.model.errors.CsvProcessingException; +import gov.cdc.usds.simplereport.api.model.filerow.TestResultRow; +import gov.cdc.usds.simplereport.db.model.DeviceTypeDisease; +import gov.cdc.usds.simplereport.db.model.PhoneNumber; +import gov.cdc.usds.simplereport.db.model.auxiliary.PersonName; +import gov.cdc.usds.simplereport.db.model.auxiliary.PhoneType; +import gov.cdc.usds.simplereport.db.model.auxiliary.StreetAddress; +import gov.cdc.usds.simplereport.db.model.auxiliary.TestCorrectionStatus; +import gov.cdc.usds.simplereport.db.repository.DeviceTypeRepository; +import java.io.InputStream; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.DiagnosticReport; +import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.ServiceRequest; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.info.GitProperties; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +public class BulkUploadResultsToFhir { + + private static final String ALPHABET_REGEX = "^[a-zA-Z]+$"; + private final Function, TestResultRow> fileRowConstructor = + TestResultRow::new; + + private final DeviceTypeRepository deviceTypeRepository; + + private final GitProperties gitProperties; + + @Value("${simple-report.processing-mode-code:P}") + private String processingModeCode = "P"; + + final FhirContext ctx = FhirContext.forR4(); + final IParser parser = ctx.newJsonParser(); + + private final Map testResultToSnomedMap = + Map.of( + "Positive".toLowerCase(), "260373001", + "Negative".toLowerCase(), "260415000", + "Detected".toLowerCase(), "260373001", + "Not Detected".toLowerCase(), "260415000", + "Invalid Result".toLowerCase(), "455371000124106"); + + private final Map specimenTypeToSnomedMap = + Map.of( + "Nasal Swab".toLowerCase(), "445297001", + "Nasopharyngeal Swab".toLowerCase(), "258500001", + "Anterior Nares Swab".toLowerCase(), "697989009", // aka nasal + "Throat Swab".toLowerCase(), "258529004", // Oropharyngeal + "Oropharyngeal Swab".toLowerCase(), "258529004", + "Whole Blood".toLowerCase(), "258580003", + "Plasma".toLowerCase(), "119361006", + "Serum".toLowerCase(), "119364003"); + + public List convertToFhirBundles(InputStream csvStream, UUID orgId) { + var futureTestEvents = new ArrayList>(); + final MappingIterator> valueIterator = getIteratorForCsv(csvStream); + + while (valueIterator.hasNext()) { + final Map row; + try { + row = getNextRow(valueIterator); + } catch (CsvProcessingException ex) { + // anything that would land here should have been caught and handled by the file validator + log.error("Unable to parse csv.", ex); + continue; + } + var fileRow = fileRowConstructor.apply(row); + + var future = + CompletableFuture.supplyAsync(() -> convertRowToFhirBundle(fileRow, orgId)) + .thenApply(parser::encodeResourceToString); + futureTestEvents.add(future); + } + + return futureTestEvents.stream() + .map( + future -> { + try { + return future.get(); + } catch (InterruptedException | ExecutionException e) { + log.error("Bulk upload failure to convert to fhir.", e); + Thread.currentThread().interrupt(); + throw new CsvProcessingException("Unable to process file."); + } + }) + .collect(Collectors.toList()); + } + + private Bundle convertRowToFhirBundle(TestResultRow row, UUID orgId) { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("M/d/yyyy[ HH:mm]"); + + var patientAddr = + new StreetAddress( + row.getPatientStreet().value, + row.getPatientStreet2().value, + row.getPatientCity().value, + row.getPatientState().value, + row.getPatientZipCode().value, + row.getPatientCounty().value); + var testingLabAddr = + new StreetAddress( + row.getTestingLabStreet().value, + row.getTestingLabStreet2().value, + row.getTestingLabCity().value, + row.getTestingLabState().value, + row.getTestingLabZipCode().value, + null); + var providerAddr = + new StreetAddress( + row.getOrderingProviderStreet().value, + row.getOrderingFacilityStreet2().value, + row.getOrderingFacilityCity().value, + row.getOrderingFacilityState().value, + row.getOrderingProviderZipCode().value, + null); + var patient = + FhirConverter.convertToPatient( + row.getPatientId().value, + new PersonName( + row.getPatientFirstName().value, + row.getPatientLastName().value, + row.getPatientMiddleName().value, + null), + List.of(new PhoneNumber(PhoneType.MOBILE, row.getPatientPhoneNumber().value)), + List.of(row.getPatientEmail().value), + row.getPatientGender().value, + LocalDate.parse(row.getPatientDob().value, dateTimeFormatter), + patientAddr, + DEFAULT_COUNTRY, + row.getPatientRace().value, + row.getPatientEthnicity().value, + new ArrayList<>()); + var testingLabOrg = + FhirConverter.convertToOrganization( + orgId.toString(), + row.getTestingLabName().value, + row.getTestingLabClia().value, + row.getTestingLabPhoneNumber().value, + null, + testingLabAddr, + DEFAULT_COUNTRY); + + Organization orderingFacility = null; + if (row.getOrderingFacilityStreet().value != null + || row.getOrderingFacilityStreet2().value != null + || row.getOrderingFacilityCity().value != null + || row.getOrderingFacilityState().value != null + || row.getOrderingFacilityZipCode().value != null + || row.getOrderingFacilityName().value != null + || row.getOrderingFacilityPhoneNumber().value != null) { + var orderingFacilityAddr = + new StreetAddress( + row.getOrderingFacilityStreet().value, + row.getOrderingFacilityStreet2().value, + row.getOrderingFacilityCity().value, + row.getOrderingFacilityState().value, + row.getOrderingFacilityZipCode().value, + null); + orderingFacility = + FhirConverter.convertToOrganization( + UUID.randomUUID().toString(), + row.getOrderingFacilityName().value, + row.getTestingLabClia().value, + row.getOrderingFacilityPhoneNumber().value, + null, + orderingFacilityAddr, + DEFAULT_COUNTRY); + } + + var practitioner = + FhirConverter.convertToPractitioner( + row.getOrderingProviderId().value, + new PersonName( + row.getOrderingProviderFirstName().value, + row.getOrderingProviderLastName().value, + row.getOrderingProviderMiddleName().value, + null), + row.getOrderingProviderPhoneNumber().value, + providerAddr, + DEFAULT_COUNTRY); + + var additionalDeviceData = + deviceTypeRepository.findDeviceTypeByModelIgnoreCase(row.getEquipmentModelName().value); + + String equipmentUid = null; + String testKitNameId = null; + String manufacturer = null; + UUID deviceId = UUID.randomUUID(); + if (additionalDeviceData != null) { + var diseaseCode = row.getTestPerformedCode().value; + Optional deviceTypeDisease = + additionalDeviceData.getSupportedDiseaseTestPerformed().stream() + .filter(code -> Objects.equals(code.getSupportedDisease().getLoinc(), diseaseCode)) + .findFirst(); + manufacturer = additionalDeviceData.getManufacturer(); + equipmentUid = deviceTypeDisease.map(DeviceTypeDisease::getEquipmentUid).orElse(null); + testKitNameId = deviceTypeDisease.map(DeviceTypeDisease::getTestkitNameId).orElse(null); + deviceId = deviceTypeDisease.map(DeviceTypeDisease::getInternalId).orElse(deviceId); + } + + var device = + FhirConverter.convertToDevice( + manufacturer, row.getEquipmentModelName().value, deviceId.toString()); + + var specimen = + FhirConverter.convertToSpecimen( + getSpecimenTypeSnomed(row.getSpecimenType().value), + getDescriptionValue(row.getSpecimenType().value), + null, + null, + UUID.randomUUID().toString()); + + var observation = + List.of( + FhirConverter.convertToObservation( + row.getTestPerformedCode().value, + null, + getTestResultSnomed(row.getTestResult().value), + mapTestResultStatusToSRValue(row.getTestResultStatus().value), + null, + UUID.randomUUID().toString(), + getDescriptionValue(row.getTestResult().value), + testKitNameId, + equipmentUid)); + + var serviceRequest = + FhirConverter.convertToServiceRequest( + ServiceRequest.ServiceRequestStatus.ACTIVE, + row.getTestPerformedCode().value, + UUID.randomUUID().toString()); + + var testDate = LocalDate.parse(row.getTestResultDate().value, dateTimeFormatter); + var diagnosticReport = + FhirConverter.convertToDiagnosticReport( + mapTestResultStatusToFhirValue(row.getTestResultStatus().value), + row.getTestPerformedCode().value, + UUID.randomUUID().toString(), + Date.from(testDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), + new Date()); + + return FhirConverter.createFhirBundle( + patient, + testingLabOrg, + orderingFacility, + practitioner, + device, + specimen, + observation, + null, + serviceRequest, + diagnosticReport, + new Date(), + gitProperties, + processingModeCode); + } + + private String getTestResultSnomed(String input) { + if (input.matches(ALPHABET_REGEX)) { + return testResultToSnomedMap.get(input.toLowerCase()); + } + return input; + } + + private String getSpecimenTypeSnomed(String input) { + if (input.matches(ALPHABET_REGEX)) { + return specimenTypeToSnomedMap.get(input.toLowerCase()); + } + return input; + } + + private String getDescriptionValue(String input) { + if (input.matches(ALPHABET_REGEX)) { + return input; + } + return null; // vs empty string? + } + + private DiagnosticReport.DiagnosticReportStatus mapTestResultStatusToFhirValue(String input) { + switch (input) { + case "C": + return DiagnosticReport.DiagnosticReportStatus.CORRECTED; + case "F": + default: + return DiagnosticReport.DiagnosticReportStatus.FINAL; + } + } + + private TestCorrectionStatus mapTestResultStatusToSRValue(String input) { + switch (input) { + case "C": + return TestCorrectionStatus.CORRECTED; + case "F": + default: + return TestCorrectionStatus.ORIGINAL; + } + } +} 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 dbb8f37460..31577fa316 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 @@ -1142,6 +1142,7 @@ void createFhirBundle_TestEvent_valid() { createFhirBundle( patient, organization, + null, practitioner, device, specimen, diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhirTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhirTest.java new file mode 100644 index 0000000000..af054506a3 --- /dev/null +++ b/backend/src/test/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhirTest.java @@ -0,0 +1,63 @@ +package gov.cdc.usds.simplereport.utils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import gov.cdc.usds.simplereport.db.repository.DeviceTypeRepository; +import java.io.InputStream; +import java.time.Instant; +import java.util.Date; +import java.util.UUID; +import java.util.stream.Collectors; +import org.hl7.fhir.r4.model.Bundle; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.boot.info.GitProperties; + +public class BulkUploadResultsToFhirTest { + private static GitProperties gitProperties; + private static DeviceTypeRepository repo; + private static final Instant commitTime = (new Date(1675891986000L)).toInstant(); + final FhirContext ctx = FhirContext.forR4(); + final IParser parser = ctx.newJsonParser(); + + BulkUploadResultsToFhir sut = new BulkUploadResultsToFhir(repo, gitProperties); + + @BeforeAll + public static void init() { + gitProperties = mock(GitProperties.class); + repo = spy(DeviceTypeRepository.class); + + when(gitProperties.getCommitTime()).thenReturn(commitTime); + when(gitProperties.getShortCommitId()).thenReturn("short-commit-id"); + } + + @Test + void convertExistingCsv_success() { + InputStream input = loadCsv("testResultUpload/test-results-upload-valid.csv"); + var serializedBundles = sut.convertToFhirBundles(input, UUID.randomUUID()); + + var first = serializedBundles.stream().findFirst().get(); + var deserializedBundle = (Bundle) parser.parseResource(first); + var resourceUrls = + deserializedBundle.getEntry().stream() + .map(Bundle.BundleEntryComponent::getFullUrl) + .collect(Collectors.toList()); + + verify(repo, times(1)).findDeviceTypeByModelIgnoreCase(anyString()); + assertThat(serializedBundles).hasSize(1); + assertThat(deserializedBundle.getEntry()).hasSize(13); + assertThat(resourceUrls).hasSize(13); + } + + private InputStream loadCsv(String csvFile) { + return BulkUploadResultsToFhirTest.class.getClassLoader().getResourceAsStream(csvFile); + } +}