Skip to content

Commit c509444

Browse files
Merge branch 'main' into david/pii-logs
2 parents d1b29a3 + e8568e9 commit c509444

26 files changed

Lines changed: 820 additions & 888 deletions

File tree

backend/src/main/java/gov/cdc/usds/simplereport/api/converter/HL7Converter.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package gov.cdc.usds.simplereport.api.converter;
22

3+
import static gov.cdc.usds.simplereport.api.Translators.ABNORMAL_SNOMEDS;
4+
import static gov.cdc.usds.simplereport.api.Translators.NORMAL_SNOMEDS;
35
import static gov.cdc.usds.simplereport.api.converter.HL7Constants.APHL_ORG_OID;
46
import static gov.cdc.usds.simplereport.api.converter.HL7Constants.CLIA_NAMING_SYSTEM_OID;
57
import static gov.cdc.usds.simplereport.api.converter.HL7Constants.DEFAULT_COUNTRY;
@@ -66,6 +68,7 @@
6668
import lombok.RequiredArgsConstructor;
6769
import lombok.extern.slf4j.Slf4j;
6870
import org.apache.commons.lang3.StringUtils;
71+
import org.owasp.encoder.Encode;
6972
import org.springframework.boot.info.GitProperties;
7073
import org.springframework.stereotype.Component;
7174

@@ -119,7 +122,8 @@ public ORU_R01 createLabReportMessage(
119122
gitProperties,
120123
processingId,
121124
testEvent.getTestOrderId().toString(),
122-
testEvent.getCorrectionStatus());
125+
testEvent.getCorrectionStatus(),
126+
true);
123127
}
124128

125129
/**
@@ -143,6 +147,8 @@ public ORU_R01 createLabReportMessage(
143147
* parameter is not the same as the message control id in MSH-10 which is randomly generated.
144148
* @param correctionStatus used to populate OBR-25 Result Status and OBX-11 Observation Result
145149
* Status
150+
* @param isLegacyReport identifies lab reports from legacy SimpleReport 1.0 which can safely
151+
* assume values for abnormal flags in OBX-8
146152
* @return ORU_R01 message
147153
* @throws DataTypeException if the HAPI package encounters a problem with the validity of a
148154
* primitive data type
@@ -158,7 +164,8 @@ public ORU_R01 createLabReportMessage(
158164
GitProperties gitProperties,
159165
String processingId,
160166
String orderId,
161-
TestCorrectionStatus correctionStatus)
167+
TestCorrectionStatus correctionStatus,
168+
boolean isLegacyReport)
162169
throws DataTypeException, IllegalArgumentException {
163170
// Lab reports from single entry and bulk upload should always have order id already populated.
164171
// Single entry would use the test event's TestOrderId which is always required. Bulk upload
@@ -234,7 +241,8 @@ public ORU_R01 createLabReportMessage(
234241
performingFacility,
235242
specimenInput.getCollectionDate(),
236243
obxTestDetails.get(obxIndex),
237-
correctionStatus);
244+
correctionStatus,
245+
isLegacyReport);
238246
}
239247

240248
// "Only a single specimen can be associated with the OBR." HL7 allows for multiple specimens
@@ -864,6 +872,8 @@ void populateObservationRequest(
864872
* @param specimenCollectionDate Used to populate the time of the observation
865873
* @param testDetail The test result data
866874
* @param testCorrectionStatus used to populate OBR-25 Result Status and OBX-11 Observation Result
875+
* @param isLegacyReport identifies lab reports from legacy SimpleReport 1.0 which can safely
876+
* assume values for abnormal flags in OBX-8
867877
* @throws DataTypeException if the HL7 package encounters a primitive validity error in setValue
868878
* @throws IllegalArgumentException if non-ordinal result type is provided
869879
*/
@@ -873,7 +883,8 @@ void populateObservationResult(
873883
FacilityReportInput performingFacility,
874884
Date specimenCollectionDate,
875885
TestDetailsInput testDetail,
876-
TestCorrectionStatus testCorrectionStatus)
886+
TestCorrectionStatus testCorrectionStatus,
887+
boolean isLegacyReport)
877888
throws DataTypeException, IllegalArgumentException {
878889
obx.getObx1_SetIDOBX().setValue(String.valueOf(sequenceNumber));
879890

@@ -898,7 +909,23 @@ void populateObservationResult(
898909
throw new IllegalArgumentException("Non-ordinal result types are not currently supported");
899910
}
900911

901-
// TODO: determine how we should programmatically set OBX 8 - Abnormal flags
912+
// TODO: determine how we should programmatically set OBX 8 - Abnormal flags for SR 2.0
913+
if (isLegacyReport && testDetail.getResultType() == ResultScaleType.ORDINAL) {
914+
String resultValueSnomed = testDetail.getResultValue();
915+
916+
// HL7 implementation guide indicates OBX-8 should be a CWE data type (Coded with Exceptions),
917+
// but the HAPI dependency handles it as an IS data type (Coded Value for User-Defined Tables)
918+
IS abnormalFlag = obx.getObx8_AbnormalFlags(0);
919+
920+
// HL7 table 0078 Abnormal flags
921+
if (ABNORMAL_SNOMEDS.containsKey(resultValueSnomed)) {
922+
abnormalFlag.setValue("A"); // abnormal for non-numeric results
923+
} else if (NORMAL_SNOMEDS.containsKey(resultValueSnomed)) {
924+
abnormalFlag.setValue("N"); // normal for non-numeric results
925+
} else {
926+
log.info("Unsupported SNOMED result code for OBX-8: {}", Encode.forJava(resultValueSnomed));
927+
}
928+
}
902929

903930
// OBX-11 uses HL7 table 0085 Observation result status codes interpretation
904931
String resultStatus;

backend/src/main/java/gov/cdc/usds/simplereport/service/TestOrderService.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,7 @@ public AddTestResultResponse addMultiplexResult(
381381
boolean deliveryStatus =
382382
deliveryStatuses.isEmpty() || deliveryStatuses.stream().anyMatch(status -> status);
383383

384-
applicationEventPublisher.publishEvent(new ReportTestEventToRSEvent(savedOrder.getTestEvent()));
385-
386-
if (featureFlagsConfig.isAimsReportingEnabled()) {
387-
applicationEventPublisher.publishEvent(new ReportToAIMSEvent(savedOrder.getTestEvent()));
388-
}
384+
applicationEventPublisher.publishEvent(new ReportToAIMSEvent(savedOrder.getTestEvent()));
389385

390386
return new AddTestResultResponse(savedOrder, deliveryStatus);
391387
}
@@ -595,11 +591,7 @@ public TestEvent correctTest(
595591
order.setCorrectionStatus(TestCorrectionStatus.REMOVED);
596592
_testOrderRepo.save(order);
597593

598-
applicationEventPublisher.publishEvent(new ReportTestEventToRSEvent(newRemoveEvent));
599-
600-
if (featureFlagsConfig.isAimsReportingEnabled()) {
601-
applicationEventPublisher.publishEvent(new ReportToAIMSEvent(newRemoveEvent));
602-
}
594+
applicationEventPublisher.publishEvent(new ReportToAIMSEvent(newRemoveEvent));
603595

604596
return newRemoveEvent;
605597
}

backend/src/main/java/gov/cdc/usds/simplereport/service/TestResultUploadService.java

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@
1717
import gov.cdc.usds.simplereport.db.model.TestResultUpload;
1818
import gov.cdc.usds.simplereport.db.model.UploadDiseaseDetails;
1919
import gov.cdc.usds.simplereport.db.model.auxiliary.AimsSubmissionSummary;
20-
import gov.cdc.usds.simplereport.db.model.auxiliary.FHIRBundleRecord;
2120
import gov.cdc.usds.simplereport.db.model.auxiliary.HL7BatchMessage;
2221
import gov.cdc.usds.simplereport.db.model.auxiliary.Pipeline;
2322
import gov.cdc.usds.simplereport.db.model.auxiliary.ResultUploadErrorSource;
2423
import gov.cdc.usds.simplereport.db.model.auxiliary.SubmissionSummary;
25-
import gov.cdc.usds.simplereport.db.model.auxiliary.UniversalSubmissionSummary;
2624
import gov.cdc.usds.simplereport.db.model.auxiliary.UploadStatus;
2725
import gov.cdc.usds.simplereport.db.repository.ResultUploadErrorRepository;
2826
import gov.cdc.usds.simplereport.db.repository.TestResultUploadRepository;
@@ -117,18 +115,10 @@ public List<TestResultUpload> processResultCSV(InputStream csvStream) throws Exc
117115
}
118116

119117
if (content.length > 0) {
120-
if (featureFlagsConfig.isAimsReportingEnabled()) {
121-
CompletableFuture<AimsSubmissionSummary> aimsSubmission =
122-
submitResultsToAIMS(new ByteArrayInputStream(content), org, submissionId);
118+
CompletableFuture<AimsSubmissionSummary> aimsSubmission =
119+
submitResultsToAIMS(new ByteArrayInputStream(content), org, submissionId);
123120

124-
processUniversalPipelineResponse(aimsSubmission, Pipeline.AIMS)
125-
.ifPresent(uploadSummary::add);
126-
}
127-
128-
CompletableFuture<UniversalSubmissionSummary> universalSubmission =
129-
submitResultsToUniversalPipeline(new ByteArrayInputStream(content), org, submissionId);
130-
131-
processUniversalPipelineResponse(universalSubmission, Pipeline.UNIVERSAL)
121+
processUniversalPipelineResponse(aimsSubmission, Pipeline.AIMS)
132122
.ifPresent(uploadSummary::add);
133123
}
134124
} catch (IOException e) {
@@ -204,30 +194,6 @@ public TokenResponse getRSAuthToken() {
204194
return _client.fetchAccessToken(requestBody);
205195
}
206196

207-
private CompletableFuture<UniversalSubmissionSummary> submitResultsToUniversalPipeline(
208-
ByteArrayInputStream content, Organization org, UUID submissionId)
209-
throws CsvProcessingException {
210-
// send to report stream
211-
return CompletableFuture.supplyAsync(
212-
withMDC(
213-
() -> {
214-
long start = System.currentTimeMillis();
215-
// convert csv to fhir and serialize to json
216-
FHIRBundleRecord fhirBundleWithMeta =
217-
fhirConverter.convertToFhirBundles(content, org.getInternalId());
218-
UploadResponse response;
219-
try {
220-
response = uploadBundleAsFhir(fhirBundleWithMeta.serializedBundle());
221-
} catch (JsonProcessingException e) {
222-
throw new CsvProcessingException("Unable to parse Report Stream response.");
223-
}
224-
log.info("FHIR submitted in {} milliseconds", System.currentTimeMillis() - start);
225-
226-
return new UniversalSubmissionSummary(
227-
submissionId, org, response, fhirBundleWithMeta.metadata());
228-
}));
229-
}
230-
231197
private CompletableFuture<AimsSubmissionSummary> submitResultsToAIMS(
232198
ByteArrayInputStream content, Organization org, UUID submissionId)
233199
throws CsvProcessingException {

backend/src/main/java/gov/cdc/usds/simplereport/service/UniversalReportService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ public String processLabReport(
5858
gitProperties,
5959
aimsProcessingModeCode,
6060
String.valueOf(UUID.randomUUID()),
61-
TestCorrectionStatus.ORIGINAL);
61+
TestCorrectionStatus.ORIGINAL,
62+
false);
6263
return parser.encode(message);
6364
} catch (HL7Exception e) {
6465
log.error("Encountered an error converting the form data to HL7");

backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToHL7.java

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ private String convertRowToHL7(TestResultRow row) throws HL7Exception {
163163
gitProperties,
164164
aimsProcessingModeCode,
165165
testId,
166-
testStatus);
166+
testStatus,
167+
true);
167168

168169
return parser.encode(labReportMessage);
169170
}
@@ -217,21 +218,49 @@ private FacilityReportInput getPerformingFacilityInput(TestResultRow row) {
217218
.build();
218219
}
219220

221+
/**
222+
* Uses legacy logic from sending FHIR for populating empty ordering facility fields with testing
223+
* lab fields. This isn't ideal, so we'll need to decide on a more thorough approach in the
224+
* future.
225+
*/
220226
private FacilityReportInput getOrderingFacilityInput(TestResultRow row) {
221-
String orderingFacilityPhoneNumber = row.getOrderingFacilityPhoneNumber().getValue();
227+
String orderingFacilityStreet =
228+
StringUtils.defaultIfEmpty(
229+
row.getOrderingFacilityStreet().getValue(), row.getTestingLabStreet().getValue());
222230

223-
if (StringUtils.isEmpty(orderingFacilityPhoneNumber)) {
224-
orderingFacilityPhoneNumber = row.getTestingLabPhoneNumber().getValue();
225-
}
231+
String orderingFacilityStreet2 =
232+
StringUtils.defaultIfEmpty(
233+
row.getOrderingFacilityStreet2().getValue(), row.getTestingLabStreet2().getValue());
234+
235+
String orderingFacilityCity =
236+
StringUtils.defaultIfEmpty(
237+
row.getOrderingFacilityCity().getValue(), row.getTestingLabCity().getValue());
238+
239+
String orderingFacilityState =
240+
StringUtils.defaultIfEmpty(
241+
row.getOrderingFacilityState().getValue(), row.getTestingLabState().getValue());
242+
243+
String orderingFacilityZipCode =
244+
StringUtils.defaultIfEmpty(
245+
row.getOrderingFacilityZipCode().getValue(), row.getTestingLabZipCode().getValue());
246+
247+
String orderingFacilityName =
248+
StringUtils.defaultIfEmpty(
249+
row.getOrderingFacilityName().getValue(), row.getTestingLabName().getValue());
250+
251+
String orderingFacilityPhoneNumber =
252+
StringUtils.defaultIfEmpty(
253+
row.getOrderingFacilityPhoneNumber().getValue(),
254+
row.getTestingLabPhoneNumber().getValue());
226255

227256
return FacilityReportInput.builder()
228-
.name(row.getOrderingFacilityName().getValue())
257+
.name(orderingFacilityName)
229258
.clia(row.getTestingLabClia().getValue())
230-
.street(row.getOrderingFacilityStreet().getValue())
231-
.streetTwo(row.getOrderingFacilityStreet2().getValue())
232-
.city(row.getOrderingFacilityCity().getValue())
233-
.state(row.getOrderingFacilityState().getValue())
234-
.zipCode(row.getOrderingFacilityZipCode().getValue())
259+
.street(orderingFacilityStreet)
260+
.streetTwo(orderingFacilityStreet2)
261+
.city(orderingFacilityCity)
262+
.state(orderingFacilityState)
263+
.zipCode(orderingFacilityZipCode)
235264
.phone(orderingFacilityPhoneNumber)
236265
.build();
237266
}

backend/src/test/java/gov/cdc/usds/simplereport/api/converter/HL7ConverterTest.java

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package gov.cdc.usds.simplereport.api.converter;
22

3+
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.DETECTED_SNOMED;
4+
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.NOT_DETECTED_SNOMED;
35
import static gov.cdc.usds.simplereport.api.converter.HL7Constants.SENDING_FACILITY_FAKE_AGGREGATE_CLIA;
46
import static gov.cdc.usds.simplereport.api.converter.HL7Constants.SENDING_FACILITY_NAMESPACE;
57
import static gov.cdc.usds.simplereport.api.converter.HL7Constants.SIMPLE_REPORT_ORG_OID;
@@ -245,7 +247,8 @@ void createLabReportMessage_encodesWithoutException() {
245247
gitProperties,
246248
"T",
247249
uuidGenerator.randomUUID().toString(),
248-
TestCorrectionStatus.ORIGINAL);
250+
TestCorrectionStatus.ORIGINAL,
251+
true);
249252

250253
Parser parser = hapiContext.getPipeParser();
251254
parser.encode(message);
@@ -272,7 +275,8 @@ void createLabReportMessage_orc3_matches_obr3() throws DataTypeException {
272275
gitProperties,
273276
"T",
274277
uuidGenerator.randomUUID().toString(),
275-
TestCorrectionStatus.ORIGINAL);
278+
TestCorrectionStatus.ORIGINAL,
279+
true);
276280

277281
EI commonOrderFillerOrderNumber =
278282
message.getPATIENT_RESULT().getORDER_OBSERVATION().getORC().getOrc3_FillerOrderNumber();
@@ -834,7 +838,7 @@ void populateObservationResult_valid() throws DataTypeException {
834838
"87949-4",
835839
"Chlamydia trachomatis DNA [Presence] in Tissue by NAA with probe detection",
836840
ResultScaleType.ORDINAL,
837-
"260373001",
841+
DETECTED_SNOMED,
838842
Date.from(testResultDate),
839843
"");
840844

@@ -844,7 +848,8 @@ void populateObservationResult_valid() throws DataTypeException {
844848
performingFacility,
845849
Date.from(specimenCollectionDate),
846850
testDetail,
847-
TestCorrectionStatus.ORIGINAL);
851+
TestCorrectionStatus.ORIGINAL,
852+
false);
848853

849854
assertThat(observationResult.getObx3_ObservationIdentifier().getCe1_Identifier().getValue())
850855
.isEqualTo(testDetail.getTestOrderLoinc());
@@ -864,6 +869,68 @@ void populateObservationResult_valid() throws DataTypeException {
864869
.isEqualTo(expectedTestResultDate);
865870
}
866871

872+
@Test
873+
void populateObservationResult_legacyReport_abnormalFlagFor_positiveResults()
874+
throws DataTypeException {
875+
OBX observationResult =
876+
new ORU_R01().getPATIENT_RESULT().getORDER_OBSERVATION(0).getOBSERVATION().getOBX();
877+
878+
FacilityReportInput performingFacility = TestDataBuilder.createFacilityReportInput();
879+
TestDetailsInput testDetail =
880+
new TestDetailsInput(
881+
"105629000",
882+
"87949-4",
883+
"Chlamydia trachomatis DNA [Presence] in Tissue by NAA with probe detection",
884+
"87949-4",
885+
"Chlamydia trachomatis DNA [Presence] in Tissue by NAA with probe detection",
886+
ResultScaleType.ORDINAL,
887+
DETECTED_SNOMED,
888+
new Date(),
889+
"");
890+
891+
hl7Converter.populateObservationResult(
892+
observationResult,
893+
1,
894+
performingFacility,
895+
new Date(),
896+
testDetail,
897+
TestCorrectionStatus.ORIGINAL,
898+
true);
899+
900+
assertThat(observationResult.getObx8_AbnormalFlags(0).getValue()).isEqualTo("A");
901+
}
902+
903+
@Test
904+
void populateObservationResult_legacyReport_normalFlagFor_nonPositiveResults()
905+
throws DataTypeException {
906+
OBX observationResult =
907+
new ORU_R01().getPATIENT_RESULT().getORDER_OBSERVATION(0).getOBSERVATION().getOBX();
908+
909+
FacilityReportInput performingFacility = TestDataBuilder.createFacilityReportInput();
910+
TestDetailsInput testDetail =
911+
new TestDetailsInput(
912+
"105629000",
913+
"87949-4",
914+
"Chlamydia trachomatis DNA [Presence] in Tissue by NAA with probe detection",
915+
"87949-4",
916+
"Chlamydia trachomatis DNA [Presence] in Tissue by NAA with probe detection",
917+
ResultScaleType.ORDINAL,
918+
NOT_DETECTED_SNOMED,
919+
new Date(),
920+
"");
921+
922+
hl7Converter.populateObservationResult(
923+
observationResult,
924+
1,
925+
performingFacility,
926+
new Date(),
927+
testDetail,
928+
TestCorrectionStatus.ORIGINAL,
929+
true);
930+
931+
assertThat(observationResult.getObx8_AbnormalFlags(0).getValue()).isEqualTo("N");
932+
}
933+
867934
@Test
868935
void populateObservationResult_throwsExceptionFor_nonOrdinalResultTypes() {
869936
OBX observationResult =
@@ -892,7 +959,8 @@ void populateObservationResult_throwsExceptionFor_nonOrdinalResultTypes() {
892959
performingFacility,
893960
Date.from(STATIC_INSTANT),
894961
testDetail,
895-
TestCorrectionStatus.ORIGINAL));
962+
TestCorrectionStatus.ORIGINAL,
963+
true));
896964
assertThat(exception.getMessage())
897965
.isEqualTo("Non-ordinal result types are not currently supported");
898966
}

0 commit comments

Comments
 (0)