Skip to content

Commit 22d3a07

Browse files
authored
CompoundStatement confidentialityCode field is populated when Observation.meta.security field contains NOPAT (#1155)
1 parent 27e3f34 commit 22d3a07

File tree

10 files changed

+258
-13
lines changed

10 files changed

+258
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ In the case that neither of these are present, the existing behavior of using th
1616

1717
* GP2GP Adaptor now populates the PlanStatement / confidentialityCode field when the ProcedureRequest.meta.security field contains NOPAT
1818
* When the ReferralRequest.meta.security field contains NOPAT, the GP2GP Adaptor will now populate the RequestStatement / confidentialityCode field accordingly.
19+
* The GP2GP Adaptor now populates the CompoundStatement / confidentialityCode field when Observation.meta.security field contains NOPAT
1920

2021
## [2.2.2] - 2025-02-07
2122

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.springframework.beans.factory.annotation.Autowired;
1111
import org.springframework.stereotype.Component;
1212

13+
import uk.nhs.adaptors.gp2gp.common.service.ConfidentialityService;
1314
import uk.nhs.adaptors.gp2gp.common.service.RandomIdGeneratorService;
1415
import uk.nhs.adaptors.gp2gp.ehr.exception.EhrMapperException;
1516
import uk.nhs.adaptors.gp2gp.ehr.mapper.parameters.BloodPressureParameters;
@@ -57,13 +58,18 @@ public class BloodPressureMapper {
5758
private final StructuredObservationValueMapper structuredObservationValueMapper;
5859
private final CodeableConceptCdMapper codeableConceptCdMapper;
5960
private final ParticipantMapper participantMapper;
61+
private final ConfidentialityService confidentialityService;
6062

6163
public String mapBloodPressure(Observation observation, boolean isNested) {
64+
65+
var confidentialityCode = confidentialityService.generateConfidentialityCode(observation);
66+
6267
BloodPressureParametersBuilder builder = BloodPressureParameters.builder()
6368
.isNested(isNested)
6469
.id(messageContext.getIdMapper().getOrNew(ResourceType.Observation, observation.getIdElement()))
6570
.effectiveTime(prepareEffectiveTimeForObservation(observation))
6671
.availabilityTime(prepareAvailabilityTimeForObservation(observation))
72+
.confidentialityCode(confidentialityCode.orElse(null))
6773
.compoundStatementCode(buildBloodPressureCode(observation));
6874

6975
extractBloodPressureComponent(observation, SYSTOLIC_CODE).ifPresent(observationComponent -> {

service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/mapper/parameters/BloodPressureParameters.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ public class BloodPressureParameters {
2323
private String systolicCode;
2424
private String diastolicCode;
2525
private String participant;
26+
private String confidentialityCode;
2627
}

service/src/main/resources/templates/ehr_compound_statement_blood_pressure_template.mustache

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
{{{effectiveTime}}}
88
</effectiveTime>
99
{{{availabilityTime}}}
10+
{{#confidentialityCode}}
11+
{{{confidentialityCode}}}
12+
{{/confidentialityCode}}
1013
{{#systolicId}}
1114
<component typeCode="COMP" contextConductionInd="true">
1215
<ObservationStatement classCode="OBS" moodCode="EVN">

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

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
import org.mockito.Mock;
1414
import org.mockito.junit.jupiter.MockitoExtension;
1515

16+
import uk.nhs.adaptors.gp2gp.common.service.ConfidentialityService;
1617
import uk.nhs.adaptors.gp2gp.common.service.FhirParseService;
1718
import uk.nhs.adaptors.gp2gp.common.service.RandomIdGeneratorService;
1819
import uk.nhs.adaptors.gp2gp.ehr.exception.EhrMapperException;
1920
import uk.nhs.adaptors.gp2gp.utils.CodeableConceptMapperMockUtil;
2021
import uk.nhs.adaptors.gp2gp.utils.ResourceTestFileUtils;
2122
import uk.nhs.adaptors.gp2gp.utils.TestArgumentsLoaderUtil;
22-
23-
import java.io.IOException;
23+
import java.util.Optional;
2424
import java.util.stream.Stream;
2525

2626
import static org.assertj.core.api.Assertions.assertThat;
@@ -43,6 +43,8 @@ public class BloodPressureMapperTest {
4343
private static final String EXPECTED_ARTERIAL_PRESSURE_WITHOUT_DATA = "arterial-pressure-without-data.xml";
4444
private static final String INPUT_BLOOD_PRESSURE_WITH_DATA = "blood-pressure-with-data.json";
4545
private static final String EXPECTED_BLOOD_PRESSURE_WITH_DATA = "blood-pressure-with-data.xml";
46+
private static final String INPUT_BLOOD_PRESSURE_WITH_NOPAT = "blood-pressure-with-nopat.json";
47+
private static final String EXPECTED_NESTED_BLOOD_PRESSURE_WITH_CONFIDENTIALITY_CODE = "blood-pressure-with-nopat.xml";
4648
private static final String EXPECTED_NESTED_BLOOD_PRESSURE = "blood-pressure-with-data-nested.xml";
4749
private static final String INPUT_BLOOD_PRESSURE_WITHOUT_DATA = "blood-pressure-without-data.json";
4850
private static final String EXPECTED_BLOOD_PRESSURE_WITHOUT_DATA = "blood-pressure-without-data.xml";
@@ -60,11 +62,19 @@ public class BloodPressureMapperTest {
6062
private static final String EXPECTED_BLOOD_PRESSURE_WITH_CODEABLE_CONCEPTS = "blood-pressure-with-codeable-concepts.xml";
6163
private static final String INPUT_BLOOD_PRESSURE_WITH_NO_CODEABLE_CONCEPTS = "blood-pressure-with-no-codeable-concepts.json";
6264

65+
public static final String CONFIDENTIALITY_CODE =
66+
"<confidentialityCode code=\"NOPAT\" "
67+
+ "codeSystem=\"2.16.840.1.113883.4.642.3.47\" "
68+
+ "displayName=\"no disclosure to patient, family or caregivers without attending provider's authorization\" />";
69+
6370
@Mock
6471
private RandomIdGeneratorService randomIdGeneratorService;
6572
@Mock
6673
private CodeableConceptCdMapper mockCodeableConceptCdMapper;
6774

75+
@Mock
76+
private ConfidentialityService confidentialityService;
77+
6878
private MessageContext messageContext;
6979
private BloodPressureMapper bloodPressureMapper;
7080

@@ -76,7 +86,7 @@ public void setUp() {
7686
messageContext.initialize(new Bundle());
7787
bloodPressureMapper = new BloodPressureMapper(
7888
messageContext, randomIdGeneratorService, new StructuredObservationValueMapper(),
79-
mockCodeableConceptCdMapper, new ParticipantMapper());
89+
mockCodeableConceptCdMapper, new ParticipantMapper(), confidentialityService);
8090
}
8191

8292
@AfterEach
@@ -85,7 +95,24 @@ public void tearDown() {
8595
}
8696

8797
@Test
88-
public void When_MappingEmptyObservation_Expect_CompoundStatementXmlReturned() throws IOException {
98+
public void When_MappingBloodPressureWithNopat_Expect_CompoundStatementWithConfidentialityCode() {
99+
when(mockCodeableConceptCdMapper.mapCodeableConceptToCdForBloodPressure(any(CodeableConcept.class)))
100+
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
101+
102+
var jsonInput = ResourceTestFileUtils.getFileContent(BLOOD_PRESSURE_FILE_LOCATION + INPUT_BLOOD_PRESSURE_WITH_NOPAT);
103+
var expectedOutput = ResourceTestFileUtils.getFileContent(BLOOD_PRESSURE_FILE_LOCATION
104+
+ EXPECTED_NESTED_BLOOD_PRESSURE_WITH_CONFIDENTIALITY_CODE);
105+
106+
Observation observation = new FhirParseService().parseResource(jsonInput, Observation.class);
107+
when(confidentialityService.generateConfidentialityCode(observation)).thenReturn(Optional.of(CONFIDENTIALITY_CODE));
108+
109+
var outputMessage = bloodPressureMapper.mapBloodPressure(observation, true);
110+
111+
assertThat(outputMessage).isEqualToIgnoringWhitespace(expectedOutput);
112+
}
113+
114+
@Test
115+
public void When_MappingEmptyObservation_Expect_CompoundStatementXmlReturned() {
89116
when(mockCodeableConceptCdMapper.mapCodeableConceptToCdForBloodPressure(any(CodeableConcept.class)))
90117
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
91118

@@ -99,7 +126,7 @@ public void When_MappingEmptyObservation_Expect_CompoundStatementXmlReturned() t
99126
}
100127

101128
@Test
102-
public void When_MappingBloodPressureWithNestedTrue_Expect_CompoundStatementXmlReturned() throws IOException {
129+
public void When_MappingBloodPressureWithNestedTrue_Expect_CompoundStatementXmlReturned() {
103130
when(mockCodeableConceptCdMapper.mapCodeableConceptToCdForBloodPressure(any(CodeableConcept.class)))
104131
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
105132

@@ -114,7 +141,7 @@ public void When_MappingBloodPressureWithNestedTrue_Expect_CompoundStatementXmlR
114141

115142
@ParameterizedTest
116143
@MethodSource("testArguments")
117-
public void When_MappingBloodPressure_Expect_CompoundStatementXmlReturned(String inputJson, String outputXml) throws IOException {
144+
public void When_MappingBloodPressure_Expect_CompoundStatementXmlReturned(String inputJson, String outputXml) {
118145
when(mockCodeableConceptCdMapper.mapCodeableConceptToCdForBloodPressure(any(CodeableConcept.class)))
119146
.thenReturn(CodeableConceptMapperMockUtil.NULL_FLAVOR_CODE);
120147

@@ -144,7 +171,7 @@ private static Stream<Arguments> testArguments() {
144171
}
145172

146173
@Test
147-
public void When_MappingBloodPressureWithCodeableConcepts_Expect_CompoundStatementXmlReturned() throws IOException {
174+
public void When_MappingBloodPressureWithCodeableConcepts_Expect_CompoundStatementXmlReturned() {
148175
when(randomIdGeneratorService.createNewOrUseExistingUUID(any()))
149176
.thenReturn("5E496953-065B-41F2-9577-BE8F2FBD0757");
150177

@@ -155,7 +182,7 @@ public void When_MappingBloodPressureWithCodeableConcepts_Expect_CompoundStateme
155182
CodeableConceptCdMapper codeableConceptCdMapper = new CodeableConceptCdMapper();
156183
bloodPressureMapper = new BloodPressureMapper(
157184
messageContext, randomIdGeneratorService, new StructuredObservationValueMapper(),
158-
codeableConceptCdMapper, new ParticipantMapper());
185+
codeableConceptCdMapper, new ParticipantMapper(), confidentialityService);
159186

160187
Observation observation = new FhirParseService().parseResource(jsonInput, Observation.class);
161188
var outputMessage = bloodPressureMapper.mapBloodPressure(observation, true);
@@ -164,13 +191,13 @@ messageContext, randomIdGeneratorService, new StructuredObservationValueMapper()
164191
}
165192

166193
@Test
167-
public void When_MappingBloodPressureWithNoCodeableConcepts_Expect_Exception() throws IOException {
194+
public void When_MappingBloodPressureWithNoCodeableConcepts_Expect_Exception() {
168195
var jsonInput = ResourceTestFileUtils.getFileContent(BLOOD_PRESSURE_FILE_LOCATION + INPUT_BLOOD_PRESSURE_WITH_NO_CODEABLE_CONCEPTS);
169196

170197
CodeableConceptCdMapper codeableConceptCdMapper = new CodeableConceptCdMapper();
171198
bloodPressureMapper = new BloodPressureMapper(
172199
messageContext, randomIdGeneratorService, new StructuredObservationValueMapper(),
173-
codeableConceptCdMapper, new ParticipantMapper());
200+
codeableConceptCdMapper, new ParticipantMapper(), confidentialityService);
174201

175202
Observation observation = new FhirParseService().parseResource(jsonInput, Observation.class);
176203

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ public void setUp() {
161161
new AllergyStructureMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
162162
new BloodPressureMapper(
163163
messageContext, randomIdGeneratorService, new StructuredObservationValueMapper(),
164-
codeableConceptCdMapper, new ParticipantMapper()),
164+
codeableConceptCdMapper, new ParticipantMapper(), confidentialityService),
165165
new ConditionLinkSetMapper(
166166
messageContext, randomIdGeneratorService, codeableConceptCdMapper, participantMapper, confidentialityService),
167167
new DiaryPlanStatementMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ public void setUp() {
143143
randomIdGeneratorService,
144144
structuredObservationValueMapper,
145145
codeableConceptCdMapper,
146-
participantMapper
146+
participantMapper,
147+
confidentialityService
147148
);
148149
ConditionLinkSetMapper conditionLinkSetMapper = new ConditionLinkSetMapper(messageContext,
149150
randomIdGeneratorService,

service/src/test/java/uk/nhs/adaptors/gp2gp/uat/EhrExtractUATTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ public void setUp() {
181181
new AllergyStructureMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
182182
new BloodPressureMapper(
183183
messageContext, randomIdGeneratorService, new StructuredObservationValueMapper(),
184-
codeableConceptCdMapper, new ParticipantMapper()),
184+
codeableConceptCdMapper, new ParticipantMapper(), confidentialityService),
185185
new ConditionLinkSetMapper(
186186
messageContext, randomIdGeneratorService, codeableConceptCdMapper, participantMapper, confidentialityService),
187187
new DiaryPlanStatementMapper(messageContext, codeableConceptCdMapper, participantMapper, confidentialityService),
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
{
2+
"resourceType": "Observation",
3+
"id": "296D5D73-8D65-4CAC-8047-553232F77A82",
4+
"meta": {
5+
"profile": [
6+
"https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-DiagnosticReport-1"
7+
],
8+
"security": [
9+
{
10+
"system": "http://hl7.org/fhir/v3/ActCode",
11+
"code": "NOPAT",
12+
"display": "no disclosure to patient, family or caregivers without attending provider's authorization"
13+
}
14+
]
15+
},
16+
"identifier": [
17+
{
18+
"system": "https://EMISWeb/A82038",
19+
"value": "296D5D73-8D65-4CAC-8047-553232F77A82"
20+
}
21+
],
22+
"status": "final",
23+
"code": {
24+
"coding": [
25+
{
26+
"system": "http://read.info/readv2",
27+
"code": "42Q5.00",
28+
"display": "Prothrombin time",
29+
"userSelected": true
30+
},
31+
{
32+
"extension": [
33+
{
34+
"url": "https://fhir.nhs.uk/STU3/StructureDefinition/Extension-coding-sctdescid",
35+
"extension": [
36+
{
37+
"url": "descriptionId",
38+
"valueId": "2207951000000110"
39+
}
40+
]
41+
}
42+
],
43+
"system": "http://snomed.info/sct",
44+
"code": "852471000000107",
45+
"display": "Prothrombin time"
46+
}
47+
]
48+
},
49+
"component": [
50+
{
51+
"code": {
52+
"coding": [
53+
{
54+
"system": "https://fhir.hl7.org.uk/Id/egton-codes",
55+
"code": "2469",
56+
"display": "Systolic blood pressure",
57+
"userSelected": true
58+
},
59+
{
60+
"extension": [
61+
{
62+
"url": "https://fhir.nhs.uk/STU3/StructureDefinition/Extension-coding-sctdescid",
63+
"extension": [
64+
{
65+
"url": "descriptionId",
66+
"valueId": "120159016"
67+
},
68+
{
69+
"url": "descriptionDisplay",
70+
"valueString": "Systolic blood pressure"
71+
}
72+
]
73+
}
74+
],
75+
"system": "http://snomed.info/sct",
76+
"code": "271649006",
77+
"display": "Systolic blood pressure"
78+
}
79+
]
80+
},
81+
"valueQuantity": {
82+
"value": 110,
83+
"unit": "mm[Hg]"
84+
},
85+
"interpretation": {
86+
"text": "Systolic blood pressure interpretation text"
87+
}
88+
},
89+
{
90+
"code": {
91+
"coding": [
92+
{
93+
"system": "https://fhir.hl7.org.uk/Id/egton-codes",
94+
"code": "246A",
95+
"display": "Diastolic blood pressure",
96+
"userSelected": true
97+
},
98+
{
99+
"extension": [
100+
{
101+
"url": "https://fhir.nhs.uk/STU3/StructureDefinition/Extension-coding-sctdescid",
102+
"extension": [
103+
{
104+
"url": "descriptionId",
105+
"valueId": "271650006"
106+
},
107+
{
108+
"url": "descriptionDisplay",
109+
"valueString": "Diastolic blood pressure"
110+
}
111+
]
112+
}
113+
],
114+
"system": "http://snomed.info/sct",
115+
"code": "271650006",
116+
"display": "Diastolic blood pressure"
117+
}
118+
]
119+
},
120+
"valueQuantity": {
121+
"value": 55,
122+
"unit": "mm[Hg]"
123+
},
124+
"interpretation": {
125+
"coding": [
126+
{
127+
"display": "Diastolic blood pressure interpretation display"
128+
}
129+
]
130+
}
131+
}
132+
],
133+
"subject": {
134+
"reference": "Patient/7CCF0FBE-C849-4BA2-A7DD-9907D1676161"
135+
},
136+
"context": {
137+
"reference": "Encounter/41882B34-B22F-4A5B-970E-75CF7846A629"
138+
},
139+
"effectiveDateTime": "2013-11-18T09:41:49.46+00:00",
140+
"issued": "2013-11-18T09:41:49.46+00:00",
141+
"performer": [
142+
{
143+
"reference": "Practitioner/749107A2-4975-441F-8EDF-ADFF451FD12D"
144+
}
145+
],
146+
"comment": "blood pressure is very low",
147+
"bodySite": {
148+
"coding": [
149+
{
150+
"display": "Body site display"
151+
}
152+
]
153+
}
154+
}

0 commit comments

Comments
 (0)