Skip to content

Commit 26dfdf1

Browse files
authored
NIAD-3213 - Failure to Process DiagnosticReport with a comment-less Filing Comment and Absence of Specimen (#950)
* [NIAD-3213] Add failing test which reproduces the exception message * [NIAD-3213] Change hasCommentNote visibility - add to filter * [NIAD-3213] Address PR comment #950 (comment) * [NIAD-3213] Address PR comment #950 (comment) * [NIAD-3213] Address PR comment #950 (comment) * [NIAD-3213] Address PR comment #950 (comment) * [NIAD-3213] Address PR comment #950 (comment) * [NIAD-3213] Address PR comment #950 (comment) * [NIAD-3213] Address checkstyle violations * [NIAD-3213] Address checkstyle violations
1 parent f2a94ef commit 26dfdf1

File tree

9 files changed

+303
-11
lines changed

9 files changed

+303
-11
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ private void buildNarrativeStatementForObservationTimes(
212212
InstantType diagnosticReportIssued) {
213213
observations.stream()
214214
.filter(observation -> observation.hasEffectiveDateTimeType() || observation.hasEffectivePeriod())
215-
.filter(this::hasCommentNote)
215+
.filter(DiagnosticReportMapper::isFilingComment)
216216
.findFirst()
217217
.map(observation -> buildNarrativeStatementForDiagnosticReport(
218218
diagnosticReportIssued,
@@ -242,7 +242,7 @@ private void buildNarrativeStatementForObservationComments(
242242

243243
var narrativeStatementObservationComments = observations.stream()
244244
.filter(Observation::hasCode)
245-
.filter(this::hasCommentNote)
245+
.filter(DiagnosticReportMapper::isFilingComment)
246246
.filter(Observation::hasComment)
247247
.map(observation -> buildNarrativeStatementForDiagnosticReport(
248248
issuedElement,
@@ -256,7 +256,16 @@ private void buildNarrativeStatementForObservationComments(
256256
reportLevelNarrativeStatements.append(narrativeStatementObservationComments);
257257
}
258258

259-
private boolean hasCommentNote(Observation observation) {
259+
/**
260+
* See the
261+
* <a href="https://simplifier.net/guide/gpconnect-data-model/Home/FHIR-Assets/All-assets/Profiles/Profile--CareConnect-GPC-Observation-1?version=current">
262+
* GP Connect 1.6.2
263+
* </a>
264+
* specification for more details on filing comments.
265+
* @param observation The Observation to check.
266+
* @return True if the Observation is a filing comment, otherwise false.
267+
*/
268+
static boolean isFilingComment(Observation observation) {
260269
return observation.getCode().hasCoding()
261270
&& observation.getCode().getCoding().stream()
262271
.filter(Coding::hasCode)

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
import org.hl7.fhir.dstu3.model.DiagnosticReport;
1919
import org.hl7.fhir.dstu3.model.Duration;
2020
import org.hl7.fhir.dstu3.model.Observation;
21-
import org.hl7.fhir.dstu3.model.PrimitiveType;
2221
import org.hl7.fhir.dstu3.model.Practitioner;
22+
import org.hl7.fhir.dstu3.model.PrimitiveType;
2323
import org.hl7.fhir.dstu3.model.Resource;
2424
import org.hl7.fhir.dstu3.model.ResourceType;
2525
import org.hl7.fhir.dstu3.model.SimpleQuantity;
@@ -43,6 +43,7 @@
4343
import java.util.List;
4444
import java.util.Objects;
4545
import java.util.Optional;
46+
import java.util.function.Predicate;
4647
import java.util.stream.Collectors;
4748
import java.util.stream.Stream;
4849

@@ -141,13 +142,15 @@ private String mapObservationsAssociatedWithSpecimen(Specimen specimen, List<Obs
141142
if (dummySpecimenOrObservationExists(specimen, observations)) {
142143
observationsAssociatedWithSpecimen = observations;
143144
} else {
145+
// Implicitly filtering out filing comments.
144146
observationsAssociatedWithSpecimen = observations.stream()
145147
.filter(Observation::hasSpecimen)
146148
.filter(observation -> observation.getSpecimen().getReference().equals(specimen.getId()))
147149
.collect(Collectors.toList());
148150
}
149151

150152
return observationsAssociatedWithSpecimen.stream()
153+
.filter(Predicate.not(DiagnosticReportMapper::isFilingComment))
151154
.map(observationMapper::mapObservationToCompoundStatement)
152155
.collect(Collectors.joining());
153156
}

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

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,25 @@
3232
import org.mockito.quality.Strictness;
3333
import org.mockito.stubbing.Answer;
3434

35-
import uk.nhs.adaptors.gp2gp.utils.ConfidentialityCodeUtility;
36-
import uk.nhs.adaptors.gp2gp.utils.FileParsingUtility;
37-
import uk.nhs.adaptors.gp2gp.common.service.ConfidentialityService;
38-
import uk.nhs.adaptors.gp2gp.common.service.RandomIdGeneratorService;
3935
import uk.nhs.adaptors.gp2gp.ehr.mapper.AgentDirectory;
4036
import uk.nhs.adaptors.gp2gp.ehr.mapper.CodeableConceptCdMapper;
4137
import uk.nhs.adaptors.gp2gp.ehr.mapper.IdMapper;
4238
import uk.nhs.adaptors.gp2gp.ehr.mapper.InputBundle;
4339
import uk.nhs.adaptors.gp2gp.ehr.mapper.MessageContext;
4440
import uk.nhs.adaptors.gp2gp.ehr.mapper.ParticipantMapper;
41+
import uk.nhs.adaptors.gp2gp.ehr.mapper.StructuredObservationValueMapper;
42+
import uk.nhs.adaptors.gp2gp.common.service.ConfidentialityService;
43+
import uk.nhs.adaptors.gp2gp.common.service.RandomIdGeneratorService;
4544
import uk.nhs.adaptors.gp2gp.utils.CodeableConceptMapperMockUtil;
45+
import uk.nhs.adaptors.gp2gp.utils.ConfidentialityCodeUtility;
46+
import uk.nhs.adaptors.gp2gp.utils.FileParsingUtility;
4647
import uk.nhs.adaptors.gp2gp.utils.ResourceTestFileUtils;
4748

4849
import static org.mockito.ArgumentMatchers.anyList;
4950
import static uk.nhs.adaptors.gp2gp.utils.ConfidentialityCodeUtility.NOPAT_HL7_CONFIDENTIALITY_CODE;
5051
import static uk.nhs.adaptors.gp2gp.utils.ConfidentialityCodeUtility.getNopatConfidentialityCodeXpathSegment;
5152
import static uk.nhs.adaptors.gp2gp.utils.XmlAssertion.assertThatXml;
53+
import static uk.nhs.adaptors.gp2gp.utils.XmlParsingUtility.getXmlStringFromFile;
5254

5355
@ExtendWith(MockitoExtension.class)
5456
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -248,6 +250,44 @@ void When_DiagnosticReport_With_ObservationEffectivePeriodAndCommentNote_Expect_
248250
assertThatXml(actualXml).containsAllXPaths(expectedXPaths);
249251
}
250252

253+
@Test
254+
void When_DiagnosticReport_With_NoReferencedSpecimenAndFilingCommentWithNoComment_Expect_MatchesSnapshotXml() {
255+
final String diagnosticReportFileName = "diagnostic-report-with-no-specimen.json";
256+
final DiagnosticReport diagnosticReport = getDiagnosticReportResourceFromJson(diagnosticReportFileName);
257+
final Bundle bundle = getBundleResourceFromJson(INPUT_JSON_BUNDLE);
258+
final InputBundle inputBundle = new InputBundle(bundle);
259+
final String expectedXml = getXmlStringFromFile(TEST_FILE_DIRECTORY, "diagnostic-report-with-no-specimen.xml");
260+
261+
when(specimenMapper.mapSpecimenToCompoundStatement(any(), anyList(), any()))
262+
.thenCallRealMethod();
263+
264+
when(messageContext.getInputBundleHolder()).thenReturn(inputBundle);
265+
266+
mapper = new DiagnosticReportMapper(
267+
messageContext,
268+
new SpecimenMapper(
269+
messageContext,
270+
new ObservationMapper(
271+
messageContext,
272+
new StructuredObservationValueMapper(),
273+
codeableConceptCdMapper,
274+
new ParticipantMapper(),
275+
new MultiStatementObservationHolderFactory(messageContext, randomIdGeneratorService),
276+
confidentialityService
277+
),
278+
randomIdGeneratorService,
279+
confidentialityService
280+
),
281+
new ParticipantMapper(),
282+
randomIdGeneratorService,
283+
confidentialityService
284+
);
285+
286+
final String actualXml = mapper.mapDiagnosticReportToCompoundStatement(diagnosticReport);
287+
288+
assertThat(actualXml).isEqualToIgnoringWhitespace(expectedXml);
289+
}
290+
251291
private Bundle getBundleResourceFromJson(String filename) {
252292
final String filePath = TEST_FILE_DIRECTORY + filename;
253293
return FileParsingUtility.parseResourceFromJsonFile(filePath, Bundle.class);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@ public void When_MappingDefaultSpecimenWithDefaultObservation_Expect_DefaultXmlO
127127
}
128128

129129
@Test
130-
void When_MappingDefaultSpecimenWithObservations_Expect_DefaultSpecimenAndObservationsXmlOutput() {
130+
void When_MappingDefaultSpecimenWithObservation_Expect_DefaultSpecimenAndObservationXmlOutput() {
131131
final Specimen specimen = getDefaultSpecimen();
132132
final String expectedXml = ResourceTestFileUtils.getFileContent(
133-
SPECIMEN_TEST_FILES_DIRECTORY + "expected_output_default_specimen_with_observations.xml");
133+
SPECIMEN_TEST_FILES_DIRECTORY + "expected_output_default_specimen_with_observation.xml");
134134

135135
when(idMapper.getOrNew(any(ResourceType.class), any(IdType.class)))
136136
.thenReturn(ID_FROM_ID_MAPPER);

service/src/test/java/uk/nhs/adaptors/gp2gp/utils/XmlParsingUtility.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,10 @@ public static boolean xpathMatchFound(String xmlString, String xPathExpression)
6464

6565
return nodeList.getLength() > 0;
6666
}
67+
68+
public static String getXmlStringFromFile(String directory, String filename) {
69+
return ResourceTestFileUtils.getFileContent(
70+
directory + filename
71+
);
72+
}
6773
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"resourceType": "DiagnosticReport",
3+
"id": "461839A4-9B28-46D4-BC27-297C46F4F80E",
4+
"meta": {
5+
"profile": [
6+
"https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-DiagnosticReport-1"
7+
]
8+
},
9+
"identifier": [
10+
{
11+
"system": "https://EMISWeb/A82038",
12+
"value": "E42C3D8C36174C8B88CC65AA70E09C62461839A49B2846D4BC27297C46F4F80E"
13+
},
14+
{
15+
"system": "2.16.840.1.113883.2.1.4.5.5",
16+
"value": "1019/HA3701201C/200103301627"
17+
}
18+
],
19+
"status": "unknown",
20+
"category": {
21+
"coding": [
22+
{
23+
"system": "http://hl7.org/fhir/v2/0074",
24+
"code": "PAT",
25+
"display": "Pathology (gross & histopath, not surgical)"
26+
}
27+
]
28+
},
29+
"code": {
30+
"coding": [
31+
{
32+
"system": "http://snomed.info/sct",
33+
"code": "721981007",
34+
"display": "Diagnostic studies report"
35+
}
36+
]
37+
},
38+
"subject": {
39+
"reference": "Patient/DAED5527-1985-45D9-993E-C5FF51F36828"
40+
},
41+
"context": {
42+
"reference": "Encounter/D0E4D145-F0A7-463C-BEF6-A768CAE0D735"
43+
},
44+
"issued": "2001-03-30T16:27:00+01:00",
45+
"performer": [
46+
{
47+
"actor": {
48+
"reference": "Organization/5E496953-065B-41F2-9577-BE8F2FBD0757"
49+
}
50+
}
51+
],
52+
"result": [
53+
{
54+
"reference": "Observation/FILING-COMMENT-WITH-NO-COMMENT"
55+
},
56+
{
57+
"reference": "Observation/6E30A9E3-FF9A-4868-8FCD-7DAC26A16E78"
58+
}
59+
]
60+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<component typeCode="COMP">
2+
<CompoundStatement classCode="CLUSTER" moodCode="EVN">
3+
<id root="II-for-DiagnosticReport-DiagnosticReport/461839A4-9B28-46D4-BC27-297C46F4F80E"/>
4+
<id extension="1019/HA3701201C/200103301627" root="2.16.840.1.113883.2.1.4.5.5"/>
5+
<code code="16488004" codeSystem="2.16.840.1.113883.2.1.3.2.4.15" displayName="laboratory reporting">
6+
<originalText>Filed Report</originalText>
7+
</code>
8+
<statusCode code="COMPLETE"/>
9+
<effectiveTime>
10+
<center nullFlavor="NI"/>
11+
</effectiveTime>
12+
<availabilityTime value="20010330162700"/>
13+
<component typeCode="COMP" contextConductionInd="true">
14+
<NarrativeStatement classCode="OBS" moodCode="EVN">
15+
<id root="5E496953-065B-41F2-9577-BE8F2FBD0757"/>
16+
<text mediaType="text/x-h7uk-pmip">CommentType:LABORATORY RESULT COMMENT(E141)
17+
CommentDate:20010330162700
18+
19+
Status: unknown
20+
</text>
21+
<statusCode code="COMPLETE"/>
22+
<availabilityTime value="20010330162700"/>
23+
</NarrativeStatement>
24+
</component>
25+
<component typeCode="COMP" contextConductionInd="true">
26+
<NarrativeStatement classCode="OBS" moodCode="EVN">
27+
<id root="5E496953-065B-41F2-9577-BE8F2FBD0757"/>
28+
<text mediaType="text/x-h7uk-pmip">CommentType:AGGREGATE COMMENT SET
29+
CommentDate:20010330162700
30+
31+
Filing Date: 2020-12-15 15:17:04
32+
</text>
33+
<statusCode code="COMPLETE"/>
34+
<availabilityTime value="20010330162700"/>
35+
</NarrativeStatement>
36+
</component>
37+
<component typeCode="COMP" contextConductionInd="true">
38+
<NarrativeStatement classCode="OBS" moodCode="EVN">
39+
<id root="5E496953-065B-41F2-9577-BE8F2FBD0757"/>
40+
<text mediaType="text/x-h7uk-pmip">CommentType:AGGREGATE COMMENT SET
41+
CommentDate:20010330162700
42+
43+
Participants: TEMPLE SOWERBY MEDICAL PRACTICE
44+
</text>
45+
<statusCode code="COMPLETE"/>
46+
<availabilityTime value="20010330162700"/>
47+
</NarrativeStatement>
48+
</component>
49+
<component typeCode="COMP" contextConductionInd="true">
50+
<CompoundStatement classCode="CLUSTER" moodCode="EVN">
51+
<id root="II-for-Specimen-DUMMY-SPECIMEN-5E496953-065B-41F2-9577-BE8F2FBD0757"/>
52+
<code code="123038009" codeSystem="2.16.840.1.113883.2.1.3.2.4.15" displayName="specimen (specimen)"/>
53+
<statusCode code="COMPLETE"/>
54+
<effectiveTime>
55+
<center nullFlavor="NI"/>
56+
</effectiveTime>
57+
<availabilityTime value="20010330162700"/>
58+
<specimen typeCode="SPC">
59+
<specimenRole classCode="SPEC">
60+
<id root="5E496953-065B-41F2-9577-BE8F2FBD0757"/>
61+
<id root="2.16.840.1.113883.2.1.4.5.2" extension="DUMMY"/>
62+
<effectiveTime>
63+
<center value="20010330162700"/>
64+
</effectiveTime>
65+
<specimenSpecimenMaterial determinerCode="INSTANCE" classCode="MAT">
66+
<desc>UNKNOWN</desc>
67+
</specimenSpecimenMaterial>
68+
</specimenRole>
69+
</specimen>
70+
<component typeCode="COMP" contextConductionInd="true">
71+
<CompoundStatement classCode="CLUSTER" moodCode="EVN">
72+
<id root="II-for-Observation-Observation/6E30A9E3-FF9A-4868-8FCD-7DAC26A16E78"/>
73+
<code nullFlavor="UNK">
74+
<originalText>Mocked code</originalText>
75+
</code>
76+
<statusCode code="COMPLETE"/>
77+
<effectiveTime>
78+
<center value="20100223"/>
79+
</effectiveTime>
80+
<availabilityTime value="20100223"/>
81+
<component typeCode="COMP" contextConductionInd="true">
82+
<ObservationStatement classCode="OBS" moodCode="EVN">
83+
<id root="5E496953-065B-41F2-9577-BE8F2FBD0757"/>
84+
<code nullFlavor="UNK">
85+
<originalText>Mocked code</originalText>
86+
</code>
87+
<statusCode code="COMPLETE"/>
88+
<effectiveTime>
89+
<center value="20100223"/>
90+
</effectiveTime>
91+
<availabilityTime value="20100223"/>
92+
<value xsi:type="PQ" value="8.800" unit="1">
93+
<translation value="8.800">
94+
<originalText>mmol/L</originalText>
95+
</translation>
96+
</value>
97+
<referenceRange typeCode="REFV">
98+
<referenceInterpretationRange classCode="OBS" moodCode="EVN.CRT">
99+
<value>
100+
<low value="10.000"/>
101+
<high value="90.000"/>
102+
</value>
103+
</referenceInterpretationRange>
104+
</referenceRange>
105+
106+
</ObservationStatement>
107+
</component>
108+
<component typeCode="COMP" contextConductionInd="true">
109+
<NarrativeStatement classCode="OBS" moodCode="EVN">
110+
<id root="II-for-Observation-Observation/6E30A9E3-FF9A-4868-8FCD-7DAC26A16E78-COMM"/>
111+
<text mediaType="text/x-h7uk-pmip">CommentType:USER COMMENT
112+
CommentDate:20201215151713
113+
114+
(q) - Normal - No Action
115+
</text>
116+
<statusCode code="COMPLETE"/>
117+
<availabilityTime value="20201215151713"/>
118+
</NarrativeStatement>
119+
</component>
120+
</CompoundStatement>
121+
</component>
122+
</CompoundStatement>
123+
</component>
124+
<Participant typeCode="AUT" contextControlCode="OP">
125+
<agentRef classCode="AGNT">
126+
<id root="II-for-Organization/5E496953-065B-41F2-9577-BE8F2FBD0757"/>
127+
</agentRef>
128+
</Participant>
129+
</CompoundStatement>
130+
</component>

0 commit comments

Comments
 (0)