Skip to content

Commit

Permalink
NIAD-3217 - Send Observation (Test Result) when they do not have a …
Browse files Browse the repository at this point in the history
…`Specimen` attached to them (#963)



---------

Co-authored-by: Alex-Nita <[email protected]>
Co-authored-by: Steven McCullagh <[email protected]>
Co-authored-by: Alex-Nita <[email protected]>
Co-authored-by: Adrian Clay <[email protected]>
  • Loading branch information
5 people authored Nov 15, 2024
1 parent 117e72a commit a8ce62e
Show file tree
Hide file tree
Showing 16 changed files with 203 additions and 48 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

## Added

* When mapping a `DiagnosticReport` which contains a `TestResult` without a `Specimen` attached to it, a new `Specimen` is
created to allow the `TestResult` to be mapped correctly.
* When mapping a `DocumentReference` which contains a `NOPAT` `meta.security` or `NOPAT` `securityLabel` tag the resultant XML for that resource
will contain a `NOPAT` `confidentialityCode` element.
* When mapping `AllergyIntolerances` which contain a `NOPAT` `meta.security` tag the resultant XML for that resource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
import static uk.nhs.adaptors.gp2gp.ehr.mapper.CommentType.LABORATORY_RESULT_COMMENT;
import static uk.nhs.adaptors.gp2gp.ehr.mapper.diagnosticreport.ObservationMapper.NARRATIVE_STATEMENT_TEMPLATE;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -73,13 +75,19 @@ public class DiagnosticReportMapper {
private final ConfidentialityService confidentialityService;

public String mapDiagnosticReportToCompoundStatement(DiagnosticReport diagnosticReport) {
List<Specimen> specimens = fetchSpecimens(diagnosticReport);
List<Observation> observations = fetchObservations(diagnosticReport);
List<Specimen> specimens = fetchSpecimens(diagnosticReport, observations);
final IdMapper idMapper = messageContext.getIdMapper();
markObservationsAsProcessed(idMapper, observations);

List<Observation> observationsExcludingFilingComments = assignDummySpecimensToObservationsWithNoSpecimen(
observations.stream()
.filter(Predicate.not(DiagnosticReportMapper::isFilingComment))
.toList(),
specimens);

String mappedSpecimens = specimens.stream()
.map(specimen -> specimenMapper.mapSpecimenToCompoundStatement(specimen, observations, diagnosticReport))
.map(specimen -> specimenMapper.mapSpecimenToCompoundStatement(specimen, observationsExcludingFilingComments, diagnosticReport))
.collect(Collectors.joining());

String reportLevelNarrativeStatements = prepareReportLevelNarrativeStatements(diagnosticReport, observations);
Expand Down Expand Up @@ -113,21 +121,60 @@ private String fetchExtensionId(List<Identifier> identifiers) {
.orElse(StringUtils.EMPTY);
}

private List<Specimen> fetchSpecimens(DiagnosticReport diagnosticReport) {
if (!diagnosticReport.hasSpecimen()) {
return Collections.singletonList(generateDefaultSpecimen(diagnosticReport));
private List<Specimen> fetchSpecimens(DiagnosticReport diagnosticReport, List<Observation> observations) {

List<Specimen> specimens = new ArrayList<>();

// At least one specimen is required to exist for any DiagnosticReport, according to the mim
if (!diagnosticReport.hasSpecimen() || hasObservationsWithoutSpecimen(observations)) {
specimens.add(generateDummySpecimen(diagnosticReport));
}

var inputBundleHolder = messageContext.getInputBundleHolder();
return diagnosticReport.getSpecimen()
List<Specimen> nonDummySpecimens = diagnosticReport.getSpecimen()
.stream()
.map(specimenReference -> inputBundleHolder.getResource(specimenReference.getReferenceElement()))
.flatMap(Optional::stream)
.map(Specimen.class::cast)
.collect(Collectors.toList());

specimens.addAll(nonDummySpecimens);

return specimens;

}

private boolean hasObservationsWithoutSpecimen(List<Observation> observations) {
return observations
.stream()
.filter(observation -> !isFilingComment(observation))
.anyMatch(observation -> !observation.hasSpecimen());
}

private List<Observation> assignDummySpecimensToObservationsWithNoSpecimen(
List<Observation> observations, List<Specimen> specimens) {

if (!hasObservationsWithoutSpecimen(observations)) {
return observations;
}

// The assumption was made that all test results without a specimen will have the same dummy specimen referenced
Specimen dummySpecimen = specimens.stream()
.filter(specimen -> specimen.getId().contains(DUMMY_SPECIMEN_ID_PREFIX))
.toList().getFirst();

Reference dummySpecimenReference = new Reference(dummySpecimen.getId());

for (Observation observation : observations) {
if (!observation.hasSpecimen() && !isFilingComment(observation)) {
observation.setSpecimen(dummySpecimenReference);
}
}

return observations;
}

private Specimen generateDefaultSpecimen(DiagnosticReport diagnosticReport) {
private Specimen generateDummySpecimen(DiagnosticReport diagnosticReport) {
Specimen specimen = new Specimen();

specimen.setId(DUMMY_SPECIMEN_ID_PREFIX + randomIdGeneratorService.createNewId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -149,7 +148,6 @@ private String mapObservationsAssociatedWithSpecimen(Specimen specimen, List<Obs
}

return observationsAssociatedWithSpecimen.stream()
.filter(Predicate.not(DiagnosticReportMapper::isFilingComment))
.map(observationMapper::mapObservationToCompoundStatement)
.collect(Collectors.joining());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -76,7 +77,6 @@ class DiagnosticReportMapperTest {
private static final String INPUT_JSON_MULTIPLE_CODED_DIAGNOSIS = "diagnostic-report-with-multiple-coded-diagnosis.json";
private static final String INPUT_JSON_EXTENSION_ID = "diagnostic-report-with-extension-id.json";
private static final String INPUT_JSON_URN_OID_EXTENSION_ID = "diagnostic-report-with-urn-oid-extension-id.json";
private static final String INPUT_JSON_UNRELATED_TEST_RESULT = "diagnostic-report-with-one-specimen-and-one-unrelated-observation.json";

private static final String OUTPUT_XML_REQUIRED_DATA = "diagnostic-report-with-required-data.xml";
private static final String OUTPUT_XML_STATUS_NARRATIVE = "diagnostic-report-with-status-narrative.xml";
Expand All @@ -88,7 +88,6 @@ class DiagnosticReportMapperTest {
private static final String OUTPUT_XML_MULTIPLE_CODED_DIAGNOSIS = "diagnostic-report-with-multiple-coded-diagnosis.xml";
private static final String OUTPUT_XML_EXTENSION_ID = "diagnostic-report-with-extension-id.xml";
private static final String OUTPUT_XML_MULTIPLE_RESULTS = "diagnostic-report-with-multiple-results.xml";
private static final String OUTPUT_XML_UNRELATED_TEST_RESULT = "diagnostic-report-with-one-specimen-and-one-unrelated-observation.xml";

@Mock
private CodeableConceptCdMapper codeableConceptCdMapper;
Expand Down Expand Up @@ -293,6 +292,44 @@ void When_DiagnosticReport_With_NoReferencedSpecimenAndFilingCommentWithNoCommen
assertThat(actualXml).isEqualToIgnoringWhitespace(expectedXml);
}

/**
* A Diagnosis Report may have an Observation (Test Result) and Specimen. If the result and specimen are not
* linked then we need to create a dummy specimen linked to the result.
*/
@Test
void When_DiagnosticReport_Has_SpecimenAndUnlinkedTestResult_Expect_ADummySpecimenLinkedToTestResult() {
final String diagnosticReportFileName = "diagnostic-report-with-one-specimen-and-one-unrelated-observation.json";
final DiagnosticReport diagnosticReport = getDiagnosticReportResourceFromJson(diagnosticReportFileName);
final Bundle bundle = getBundleResourceFromJson(INPUT_JSON_BUNDLE);
final InputBundle inputBundle = new InputBundle(bundle);

when(messageContext.getInputBundleHolder()).thenReturn(inputBundle);

final String actualXml = mapper.mapDiagnosticReportToCompoundStatement(diagnosticReport);

// This checks that the unlinked test result is given a dummy specimen.
assertThat(actualXml).containsIgnoringWhitespaces(
"<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 "
+ "with linked Observations: Observation/TestResult-WithoutSpecimenReference-->");
}

@Test
void When_DiagnosticReport_Has_SpecimenALinkedTestResultAndAnUnlinkedTestResult_Expect_ASpecimenOnAllTestResults() {
final String diagnosticReportFileName =
"diagnostic-report-with-one-specimen-one-linked-observation-and-one-unlinked-observation.json";
final DiagnosticReport diagnosticReport = getDiagnosticReportResourceFromJson(diagnosticReportFileName);
final Bundle bundle = getBundleResourceFromJson(INPUT_JSON_BUNDLE);
final InputBundle inputBundle = new InputBundle(bundle);
when(messageContext.getInputBundleHolder()).thenReturn(inputBundle);

final String actualXml = mapper.mapDiagnosticReportToCompoundStatement(diagnosticReport);
// This checks that the unlinked test result is given a dummy specimen.
assertThat(actualXml).containsIgnoringWhitespaces(
"<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 "
+ "with linked Observations: Observation/TestResult-WithoutSpecimenReference-->");

}

private Bundle getBundleResourceFromJson(String filename) {
final String filePath = TEST_FILE_DIRECTORY + filename;
return FileParsingUtility.parseResourceFromJsonFile(filePath, Bundle.class);
Expand All @@ -318,8 +355,7 @@ private static Stream<Arguments> resourceFileParams() {
Arguments.of(INPUT_JSON_CODED_DIAGNOSIS, OUTPUT_XML_CODED_DIAGNOSIS),
Arguments.of(INPUT_JSON_MULTIPLE_CODED_DIAGNOSIS, OUTPUT_XML_MULTIPLE_CODED_DIAGNOSIS),
Arguments.of(INPUT_JSON_EXTENSION_ID, OUTPUT_XML_EXTENSION_ID),
Arguments.of(INPUT_JSON_URN_OID_EXTENSION_ID, OUTPUT_XML_EXTENSION_ID),
Arguments.of(INPUT_JSON_UNRELATED_TEST_RESULT, OUTPUT_XML_UNRELATED_TEST_RESULT)
Arguments.of(INPUT_JSON_URN_OID_EXTENSION_ID, OUTPUT_XML_EXTENSION_ID)
);
}

Expand All @@ -341,7 +377,23 @@ private Answer<String> mockIdForReference() {
private Answer<String> mockSpecimenMapping() {
return invocation -> {
Specimen specimen = invocation.getArgument(0);
return String.format("<!-- Mapped Specimen with id: %s -->", specimen.getId());
List<Observation> observations = invocation.getArgument(1);

List<String> linkedObservations = new ArrayList<>();

for (Observation observation : observations) {
if (observation.getSpecimen().getReference() != null
&& observation.getSpecimen().getReference().equals(specimen.getId())) {
linkedObservations.add(observation.getId());
}
}

if (linkedObservations.isEmpty()) {
return String.format("<!-- Mapped Specimen with id: %s -->", specimen.getId());
}
return String.format("<!-- Mapped Specimen with id: %s with linked Observations: %s-->",
specimen.getId(),
String.join(",", linkedObservations));
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ Status: unknown</text>
<availabilityTime value="20100225154100"/>
</NarrativeStatement>
</component>
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 with linked Observations: DUMMY-OBSERVATION-5E496953-065B-41F2-9577-BE8F2FBD0757-->
</CompoundStatement>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ Status: unknown</text>
<availabilityTime value="20100225154100"/>
</NarrativeStatement>
</component>
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 with linked Observations: DUMMY-OBSERVATION-5E496953-065B-41F2-9577-BE8F2FBD0757-->
</CompoundStatement>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ Status: unknown</text>
<availabilityTime value="20100225154100"/>
</NarrativeStatement>
</component>
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 with linked Observations: DUMMY-OBSERVATION-5E496953-065B-41F2-9577-BE8F2FBD0757-->
</CompoundStatement>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ Status: unknown</text>
<availabilityTime value="20100225154100"/>
</NarrativeStatement>
</component>
<!-- Mapped Specimen with id: Specimen/96B93E28-293D-46E7-B4C2-D477EEBF7098-SPEC-0 --><!-- Mapped Specimen with id: Specimen/96B93E28-293D-46E7-B4C2-D477EEBF7098-SPEC-1 --><!-- Mapped Specimen with id: Specimen/96B93E28-293D-46E7-B4C2-D477EEBF7098-SPEC-2 -->
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 with linked Observations: DUMMY-OBSERVATION-5E496953-065B-41F2-9577-BE8F2FBD0757-->
<!-- Mapped Specimen with id: Specimen/96B93E28-293D-46E7-B4C2-D477EEBF7098-SPEC-0 -->
<!-- Mapped Specimen with id: Specimen/96B93E28-293D-46E7-B4C2-D477EEBF7098-SPEC-1 -->
<!-- Mapped Specimen with id: Specimen/96B93E28-293D-46E7-B4C2-D477EEBF7098-SPEC-2 -->
</CompoundStatement>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ Status: unknown</text>
<availabilityTime value="20100225154100"/>
</NarrativeStatement>
</component>
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 with linked Observations: DUMMY-OBSERVATION-5E496953-065B-41F2-9577-BE8F2FBD0757-->
</CompoundStatement>
</component>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"resourceType": "DiagnosticReport",
"id": "96B93E28-293D-46E7-B4C2-D477EEBF7098",
"meta": {
"profile": [
"https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-DiagnosticReport-1"
]
},
"identifier": [
{
"system": "https://EMISWeb/A82038",
"value": "96B93E28-293D-46E7-B4C2-D477EEBF7098"
}
],
"status": "unknown",
"category": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0074",
"code": "PAT",
"display": "Pathology (gross & histopath, not surgical)"
}
]
},
"code": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "721981007",
"display": "Diagnostic studies report"
}
]
},
"subject": {
"reference": "Patient/DAED5527-1985-45D9-993E-C5FF51F36828"
},
"issued": "2010-02-25T15:41:00+00:00",
"specimen": [{
"reference": "Specimen/96B93E28-293D-46E7-B4C2-D477EEBF7098-SPEC-0"
}],
"result": [
{ "reference": "Observation/B7F05EA7-A1A4-48C0-9C4C-CDB5768796B2" },
{ "reference": "Observation/TestResult-WithoutSpecimenReference" }
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Status: unknown</text>
<availabilityTime value="20100225154100"/>
</NarrativeStatement>
</component>
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 with linked Observations: DUMMY-OBSERVATION-5E496953-065B-41F2-9577-BE8F2FBD0757-->
<!-- Mapped Specimen with id: Specimen/96B93E28-293D-46E7-B4C2-D477EEBF7098-SPEC-0 -->
</CompoundStatement>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Participants: TEMPLE SOWERBY MEDICAL PRACTICE</text>
<availabilityTime value="20100225154100"/>
</NarrativeStatement>
</component>
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 with linked Observations: DUMMY-OBSERVATION-5E496953-065B-41F2-9577-BE8F2FBD0757-->
<Participant typeCode="AUT" contextControlCode="OP">
<agentRef classCode="AGNT">
<id root="II-for-Organization/5E496953-065B-41F2-9577-BE8F2FBD0757"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ Status: unknown</text>
<availabilityTime value="20100225154100"/>
</NarrativeStatement>
</component>
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 with linked Observations: DUMMY-OBSERVATION-5E496953-065B-41F2-9577-BE8F2FBD0757-->
</CompoundStatement>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ Received Date: 2010-02-24 15:41
<availabilityTime value="20201012143344"/>
</NarrativeStatement>
</component>
<!-- Mapped Observation with id: Observation/B7F05EA7-A1A4-48C0-9C4C-CDB5768796B2 -->
<!-- Mapped Observation with id: Observation/B7F05EA7-A1A4-48C0-9C4C-CDB5768796B2 --><!-- Mapped Observation with id: Observation/B7F05EA7-A1A4-48C0-9C4C-CDB5768796B2-COMM -->
</CompoundStatement>
</component>
Loading

0 comments on commit a8ce62e

Please sign in to comment.