Skip to content

Commit 39ea7da

Browse files
morsh3dORybak5
andauthored
NIAD-3216: When DiagnosticReport doesn't contain a Specimen reference, we are sending a value of DUMMY (#1326)
* Remove some instances of the word dummy * test adjustments * test adjustments * code refactoring * adding test coverage * refactoring and test * changelog update * code and test refactoring * code and test refactoring --------- Co-authored-by: ORybak5 <[email protected]>
1 parent 12f8e25 commit 39ea7da

15 files changed

+119
-40
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
* The GP2GP Adaptor now populates the ObservationStatement / confidentialityCode field when the .meta.security field of an Uncategorized Data Observation contains NOPAT
1717
* When List.meta.security field contains NOPAT, the GP2GP Adaptor will now populate the CompoundStatement.confidentialityCode
1818

19+
### Fixed
20+
* When DiagnosticReport doesn't contain a Specimen reference, instead of "DUMMY" "NOT-PRESENT" value is used
21+
1922
### Update
2023

2124
* [GP Connect 1.6.1] The GP2GP Adaptor is now able to identify e-referrals by using either `https://fhir.nhs.uk/Id/ubr-number` or `https://fhir.nhs.uk/Id/UBRN` when provided as an identifier system URL.

service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/mapper/diagnosticreport/DiagnosticReportMapper.java

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
@Slf4j
5353
public class DiagnosticReportMapper {
5454

55-
public static final String DUMMY_SPECIMEN_ID_PREFIX = "DUMMY-SPECIMEN-";
55+
public static final String NOT_PRESENT_SPECIMEN_ID_PREFIX = "NOT-PRESENT-SPECIMEN-";
5656
public static final String DUMMY_OBSERVATION_ID_PREFIX = "DUMMY-OBSERVATION-";
5757

5858
private static final Mustache DIAGNOSTIC_REPORT_COMPOUND_STATEMENT_TEMPLATE =
@@ -142,7 +142,7 @@ private List<Specimen> fetchSpecimens(DiagnosticReport diagnosticReport, List<Ob
142142

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

148148
var inputBundleHolder = messageContext.getInputBundleHolder();
@@ -168,41 +168,38 @@ private boolean hasObservationsWithoutSpecimen(List<Observation> observations) {
168168
/**
169169
* For correct display in EMIS, any observation without a specimen must be assigned a dummy specimen.
170170
*/
171-
private List<Observation> assignDummySpecimensToObservationsWithNoSpecimen(
172-
List<Observation> observations, List<Specimen> specimens) {
171+
List<Observation> assignDummySpecimensToObservationsWithNoSpecimen(List<Observation> observations, List<Specimen> specimens) {
173172

174173
List<Observation> filingComments = getFilingComments(observations);
175-
observations = new ArrayList<>(stripFilingComments(observations));
174+
List<Observation> nonFilingObservations = new ArrayList<>(stripFilingComments(observations));
176175

177-
if (!hasObservationsWithoutSpecimen(observations)) {
178-
observations.addAll(filingComments);
179-
return observations;
176+
if (!hasObservationsWithoutSpecimen(nonFilingObservations)) {
177+
nonFilingObservations.addAll(filingComments);
178+
return nonFilingObservations;
180179
}
181180

182181
// The assumption was made that all test results without a specimen will have the same dummy specimen referenced
183-
Specimen dummySpecimen = specimens.stream()
184-
.filter(specimen -> specimen.getId().contains(DUMMY_SPECIMEN_ID_PREFIX))
185-
.toList().getFirst();
182+
Specimen notPresentSpecimen = specimens.stream()
183+
.filter(specimen -> specimen.getId().contains(NOT_PRESENT_SPECIMEN_ID_PREFIX))
184+
.toList().getFirst();
186185

187-
Reference dummySpecimenReference = new Reference(dummySpecimen.getId());
186+
Reference notPresentSpecimenReference = new Reference(notPresentSpecimen.getId());
188187

189-
for (Observation observation : observations) {
190-
if (!observation.hasSpecimen() && !isFilingComment(observation)) {
191-
observation.setSpecimen(dummySpecimenReference);
192-
}
193-
}
188+
nonFilingObservations.stream()
189+
.filter(obs -> !obs.hasSpecimen() && !isFilingComment(obs))
190+
.forEach(obs -> obs.setSpecimen(notPresentSpecimenReference));
194191

195-
observations.addAll(filingComments);
196-
return observations;
192+
nonFilingObservations.addAll(filingComments);
193+
return nonFilingObservations;
197194
}
198195

199-
private Specimen generateDummySpecimen(DiagnosticReport diagnosticReport) {
196+
private Specimen generateNotPresentSpecimen(DiagnosticReport diagnosticReport) {
200197
Specimen specimen = new Specimen();
201198

202-
specimen.setId(DUMMY_SPECIMEN_ID_PREFIX + randomIdGeneratorService.createNewId());
199+
specimen.setId(NOT_PRESENT_SPECIMEN_ID_PREFIX + randomIdGeneratorService.createNewId());
203200

204201
return specimen
205-
.setAccessionIdentifier(new Identifier().setValue("DUMMY"))
202+
.setAccessionIdentifier(new Identifier().setValue("NOT PRESENT"))
206203
.setCollection(new Specimen.SpecimenCollectionComponent().setCollected(new DateTimeType(diagnosticReport.getIssued())))
207204
.setType(new CodeableConcept().setText("UNKNOWN"));
208205
}
@@ -247,7 +244,7 @@ private List<String> getSpecimenIdsWithoutObservation(List<Specimen> specimens,
247244
List<String> nonOrphanSpecimenIDList = new ArrayList<>();
248245
for (Specimen specimen : specimens) {
249246
// Dummy Specimens should not have a dummy observation attached.
250-
if (!specimen.getId().contains(DUMMY_SPECIMEN_ID_PREFIX)) {
247+
if (!specimen.getId().contains(NOT_PRESENT_SPECIMEN_ID_PREFIX)) {
251248
specimenIDList.add(specimen.getId());
252249
}
253250
}

service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/mapper/diagnosticreport/DiagnosticReportMapperTest.java

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package uk.nhs.adaptors.gp2gp.ehr.mapper.diagnosticreport;
22

33
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
5+
import static org.junit.Assert.assertFalse;
46
import static org.junit.jupiter.api.Assertions.assertAll;
57
import static org.mockito.ArgumentMatchers.any;
68
import static org.mockito.Mockito.when;
79
import java.util.ArrayList;
810
import java.util.Collections;
911
import java.util.List;
12+
import java.util.NoSuchElementException;
1013
import java.util.Optional;
1114
import java.util.stream.Stream;
1215

1316
import org.hl7.fhir.dstu3.model.Bundle;
1417
import org.hl7.fhir.dstu3.model.CodeableConcept;
18+
import org.hl7.fhir.dstu3.model.Coding;
1519
import org.hl7.fhir.dstu3.model.DiagnosticReport;
1620
import org.hl7.fhir.dstu3.model.IdType;
1721
import org.hl7.fhir.dstu3.model.Observation;
@@ -54,12 +58,15 @@
5458
@ExtendWith(MockitoExtension.class)
5559
@MockitoSettings(strictness = Strictness.LENIENT)
5660
class DiagnosticReportMapperTest {
61+
5762
private static final String TEST_FILE_DIRECTORY = "/ehr/mapper/diagnosticreport/";
5863

5964
private static final String INPUT_JSON_BUNDLE = "fhir_bundle.json";
6065
private static final String INPUT_JSON_BUNDLE_WITH_FILING_COMMENTS = "fhir_bundle_with_filing_comments.json";
6166

6267
private static final String TEST_ID = "5E496953-065B-41F2-9577-BE8F2FBD0757";
68+
public static final String NOT_PRESENT_SPECIMEN_ID_PREFIX = "NOT-PRESENT-SPECIMEN-";
69+
private static final String COMMENT_NOTE = "37331000000100";
6370

6471
private static final String INPUT_JSON_REQUIRED_DATA = "diagnostic-report-with-required-data.json";
6572
private static final String INPUT_JSON_EMPTY_SPECIMENS = "diagnostic-report-with-empty-specimens.json";
@@ -128,6 +135,75 @@ public void tearDown() {
128135
messageContext.resetMessageContext();
129136
}
130137

138+
@Test
139+
void shouldAssignDummySpecimenOnlyToNonFilingObservationsWithoutSpecimen() {
140+
141+
Observation obsWithoutSpecimen = new Observation();
142+
obsWithoutSpecimen.setId("obs1");
143+
144+
Observation obsWithSpecimen = new Observation();
145+
obsWithSpecimen.setId("obs2");
146+
obsWithSpecimen.setSpecimen(new Reference("real-specimen"));
147+
148+
Observation filingCommentObs = new Observation();
149+
filingCommentObs.setId("obs3");
150+
filingCommentObs.getCode().addCoding(new Coding().setCode(COMMENT_NOTE));
151+
152+
List<Observation> observations = List.of(obsWithoutSpecimen, obsWithSpecimen, filingCommentObs);
153+
154+
Specimen dummySpecimen = new Specimen();
155+
dummySpecimen.setId(NOT_PRESENT_SPECIMEN_ID_PREFIX + "123");
156+
List<Specimen> specimens = List.of(dummySpecimen);
157+
158+
List<Observation> result = mapper.assignDummySpecimensToObservationsWithNoSpecimen(observations, specimens);
159+
160+
assertThat(obsWithoutSpecimen.getSpecimen())
161+
.isNotNull()
162+
.extracting(Reference::getReference)
163+
.isEqualTo(dummySpecimen.getId());
164+
165+
assertThat(obsWithSpecimen.getSpecimen())
166+
.extracting(Reference::getReference)
167+
.isEqualTo("real-specimen");
168+
169+
assertFalse(filingCommentObs.hasSpecimen());
170+
assertThat(result).containsExactlyInAnyOrder(obsWithoutSpecimen, obsWithSpecimen, filingCommentObs);
171+
}
172+
173+
@Test
174+
void shouldThrowIfNoDummySpecimenFound() {
175+
176+
Observation obsWithoutSpecimen = new Observation();
177+
List<Observation> observations = List.of(obsWithoutSpecimen);
178+
179+
List<Specimen> specimens = List.of();
180+
181+
assertThatThrownBy(() -> mapper.assignDummySpecimensToObservationsWithNoSpecimen(observations, specimens))
182+
.isInstanceOf(NoSuchElementException.class);
183+
}
184+
185+
@Test
186+
void shouldAssignDummySpecimenToObservationsWithoutSpecimen() {
187+
188+
Observation obsWithoutSpecimen = new Observation();
189+
Observation obsWithSpecimen = new Observation();
190+
obsWithSpecimen.setSpecimen(new Reference("real-specimen"));
191+
192+
List<Observation> observations = List.of(obsWithoutSpecimen, obsWithSpecimen);
193+
194+
Specimen dummySpecimen = new Specimen();
195+
dummySpecimen.setId("dummy-" + NOT_PRESENT_SPECIMEN_ID_PREFIX);
196+
Specimen realSpecimen = new Specimen();
197+
realSpecimen.setId("real-specimen");
198+
List<Specimen> specimens = List.of(realSpecimen, dummySpecimen);
199+
200+
List<Observation> result = mapper.assignDummySpecimensToObservationsWithNoSpecimen(observations, specimens);
201+
202+
assertThat(result).hasSize(2);
203+
assertThat(result.get(0).getSpecimen().getReference()).contains(dummySpecimen.getId());
204+
assertThat(result.get(1).getSpecimen().getReference()).contains("real-specimen");
205+
}
206+
131207
@ParameterizedTest
132208
@MethodSource("resourceFileParams")
133209
void When_MappingDiagnosticReportJson_Expect_CompoundStatementXmlOutput(String inputJson, String outputXml) {
@@ -250,12 +326,14 @@ void When_DiagnosticReport_With_ObservationEffectivePeriodAndCommentNote_Expect_
250326
}
251327

252328
@Test
253-
void When_DiagnosticReport_With_NoReferencedSpecimenAndFilingCommentWithNoComment_Expect_MatchesSnapshotXml() {
329+
void When_DR_With_NoReferencedSpecimenAndFilingCommentWithNoComment_Expect_MatchesSnapshotXmlIncludesSpecimenRoleWithNotPresentTag() {
254330
final String diagnosticReportFileName = "diagnostic-report-with-no-specimen.json";
255331
final DiagnosticReport diagnosticReport = getDiagnosticReportResourceFromJson(diagnosticReportFileName);
256332
final Bundle bundle = getBundleResourceFromJson(INPUT_JSON_BUNDLE);
257333
final InputBundle inputBundle = new InputBundle(bundle);
258334
final String expectedXml = getXmlStringFromFile(TEST_FILE_DIRECTORY, "diagnostic-report-with-no-specimen.xml");
335+
final List<String> expectedXPaths = Collections.singletonList(
336+
"/component/CompoundStatement/component/CompoundStatement/specimen/specimenRole/id[@extension=\"NOT PRESENT\"]");
259337

260338
when(specimenMapper.mapSpecimenToCompoundStatement(
261339
any(Specimen.class),
@@ -288,6 +366,7 @@ void When_DiagnosticReport_With_NoReferencedSpecimenAndFilingCommentWithNoCommen
288366
final String actualXml = mapper.mapDiagnosticReportToCompoundStatement(diagnosticReport);
289367

290368
assertThat(actualXml).isEqualToIgnoringWhitespace(expectedXml);
369+
assertThatXml(actualXml).containsAllXPaths(expectedXPaths);
291370
}
292371

293372
/**
@@ -307,7 +386,7 @@ void When_DiagnosticReport_Has_SpecimenAndUnlinkedTestResult_Expect_ADummySpecim
307386

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

@@ -408,7 +487,7 @@ void When_DiagnosticReport_Has_SpecimenALinkedTestResultAndAnUnlinkedTestResult_
408487
final String actualXml = mapper.mapDiagnosticReportToCompoundStatement(diagnosticReport);
409488
// This checks that the unlinked observation is given a dummy specimen.
410489
assertThat(actualXml).containsIgnoringWhitespaces(
411-
"<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 "
490+
"<!-- Mapped Specimen with id: NOT-PRESENT-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 "
412491
+ "with linked Observations: Observation/TestResult-WithoutSpecimenReference-->");
413492

414493
}

service/src/test/resources/ehr/mapper/diagnosticreport/diagnostic-report-with-coded-diagnosis.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ Status: unknown</text>
3030
<availabilityTime value="20100225154100"/>
3131
</NarrativeStatement>
3232
</component>
33-
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757-->
33+
<!-- Mapped Specimen with id: NOT-PRESENT-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757-->
3434
</CompoundStatement>
3535
</component>

service/src/test/resources/ehr/mapper/diagnosticreport/diagnostic-report-with-conclusion.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ Status: unknown</text>
3030
<availabilityTime value="20100225154100"/>
3131
</NarrativeStatement>
3232
</component>
33-
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
33+
<!-- Mapped Specimen with id: NOT-PRESENT-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
3434
</CompoundStatement>
3535
</component>

service/src/test/resources/ehr/mapper/diagnosticreport/diagnostic-report-with-extension-id.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ Status: unknown</text>
2121
<availabilityTime value="20100225154100"/>
2222
</NarrativeStatement>
2323
</component>
24-
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
24+
<!-- Mapped Specimen with id: NOT-PRESENT-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
2525
</CompoundStatement>
2626
</component>

service/src/test/resources/ehr/mapper/diagnosticreport/diagnostic-report-with-multiple-coded-diagnosis.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ Status: unknown</text>
3030
<availabilityTime value="20100225154100"/>
3131
</NarrativeStatement>
3232
</component>
33-
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
33+
<!-- Mapped Specimen with id: NOT-PRESENT-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
3434
</CompoundStatement>
3535
</component>

service/src/test/resources/ehr/mapper/diagnosticreport/diagnostic-report-with-multiple-results.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,6 @@ CommentDate:20100225154100
6262
<availabilityTime value="20100225154100"/>
6363
</NarrativeStatement>
6464
</component>
65-
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
65+
<!-- Mapped Specimen with id: NOT-PRESENT-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
6666
</CompoundStatement>
6767
</component>

service/src/test/resources/ehr/mapper/diagnosticreport/diagnostic-report-with-no-specimen.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
</component>
4949
<component typeCode="COMP" contextConductionInd="true">
5050
<CompoundStatement classCode="CLUSTER" moodCode="EVN">
51-
<id root="II-for-Specimen-DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757"/>
51+
<id root="II-for-Specimen-NOT-PRESENT-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757"/>
5252
<code code="123038009" codeSystem="2.16.840.1.113883.2.1.3.2.4.15" displayName="specimen (specimen)"/>
5353
<statusCode code="COMPLETE"/>
5454
<effectiveTime>
@@ -58,7 +58,7 @@
5858
<specimen typeCode="SPC">
5959
<specimenRole classCode="SPEC">
6060
<id root="5E496953-065B-41F2-9577-BE8F2FBD0757"/>
61-
<id root="2.16.840.1.113883.2.1.4.5.2" extension="DUMMY"/>
61+
<id root="2.16.840.1.113883.2.1.4.5.2" extension="NOT PRESENT"/>
6262
<effectiveTime>
6363
<center value="20010330162700"/>
6464
</effectiveTime>

service/src/test/resources/ehr/mapper/diagnosticreport/diagnostic-report-with-participant.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Participants: TEMPLE SOWERBY MEDICAL PRACTICE</text>
3030
<availabilityTime value="20100225154100"/>
3131
</NarrativeStatement>
3232
</component>
33-
<!-- Mapped Specimen with id: DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
33+
<!-- Mapped Specimen with id: NOT-PRESENT-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757 -->
3434
<Participant typeCode="AUT" contextControlCode="OP">
3535
<agentRef classCode="AGNT">
3636
<id root="II-for-Organization/5E496953-065B-41F2-9577-BE8F2FBD0757"/>

0 commit comments

Comments
 (0)