Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIAD-3220: Detect Referral + Document LinkSets and not create a Problem (Condition) from them #937

Merged
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Fixed

* When a `LinkSet` which is a linkage between a `ReferralRequest` and one or more `DocumentReferences` is mapped then a
`Condition` will no longer be mapped for this `Linkset`.
Instead, the `statementRef`s will be added to the `supportingInfo` when mapping the `ReferralRequest`
as `DocumentReferences`

## [3.0.7] - 2024-11-19

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2073,6 +2073,11 @@
} ],
"text": "Referral to private doctor"
} ],
"supportingInfo": [ {
"reference": "DocumentReference/BFBF038A-F142-4C67-B05B-D155E2C89990"
}, {
"reference": "DocumentReference/6DC83A17-4DFD-4C1C-A452-45F8F8A8FBA1"
} ],
"note": [ {
"text": "Priority: Routine"
}, {
Expand Down Expand Up @@ -2623,61 +2628,6 @@
} ],
"comment": "This is a dry eyes problem {Episodicity : code=255217005, displayName=First}"
}
}, {
"resource": {
"resourceType": "Condition",
"id": "E0EF416A-A513-4668-B454-1F3002573B80",
"meta": {
"profile": [ "https://fhir.nhs.uk/STU3/StructureDefinition/CareConnect-GPC-ProblemHeader-Condition-1" ]
},
"extension": [ {
"url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-ProblemSignificance-1",
"valueCode": "minor"
}, {
"url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-ActualProblem-1",
"valueReference": {
"reference": "ReferralRequest/E63AF323-919F-4D5F-9A1D-BA933BC230BC"
}
}, {
"url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-RelatedClinicalContent-1",
"valueReference": {
"reference": "DocumentReference/BFBF038A-F142-4C67-B05B-D155E2C89990"
}
}, {
"url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-RelatedClinicalContent-1",
"valueReference": {
"reference": "DocumentReference/6DC83A17-4DFD-4C1C-A452-45F8F8A8FBA1"
}
} ],
"identifier": [ {
"system": "https://PSSAdaptor/B83002",
"value": "E0EF416A-A513-4668-B454-1F3002573B80"
} ],
"clinicalStatus": "active",
"category": [ {
"coding": [ {
"system": "https://fhir.hl7.org.uk/STU3/CodeSystem/CareConnect-ConditionCategory-1",
"code": "problem-list-item",
"display": "Problem List Item"
} ]
} ],
"subject": {
"reference": "Patient/00000000-0000-0000-0000-000000000012"
},
"context": {
"reference": "Encounter/7626FAAD-8562-478D-B79C-598520953E86"
},
"onsetDateTime": "2010-01-19T11:32:00+00:00",
"assertedDate": "2010-01-19T11:32:30+00:00",
"asserter": {
"reference": "Practitioner/1E473786-E7FA-785E-C911-A8D38FB56F20"
},
"note": [ {
"text": "Defaulted status to active : Unknown status at source"
}, {
"text": "Unspecified Significance: Defaulted to Minor"
} ]
}
}, {
"resource": {
"resourceType": "Condition",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import uk.nhs.adaptors.pss.translator.service.ConfidentialityService;
import uk.nhs.adaptors.pss.translator.util.CompoundStatementResourceExtractors;
import uk.nhs.adaptors.pss.translator.util.DegradedCodeableConcepts;
import uk.nhs.adaptors.pss.translator.util.ResourceFilterUtil;
import static uk.nhs.adaptors.common.util.CodeableConceptUtils.createCodeableConcept;

@Service
Expand Down Expand Up @@ -96,6 +97,7 @@ public List<Condition> mapResources(RCMRMT030101UKEhrExtract ehrExtract, Patient
return mapEhrExtractToFhirResource(ehrExtract, (extract, composition, component) ->
extractAllLinkSets(component)
.filter(Objects::nonNull)
.filter(linkSet -> !ResourceFilterUtil.isReferralRequestToExternalDocumentLinkSet(ehrExtract, linkSet))
.map(linkSet -> getCondition(
patient,
encounters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,35 @@
import static uk.nhs.adaptors.pss.translator.util.CompoundStatementResourceExtractors.extractAllRequestStatements;
import static uk.nhs.adaptors.pss.translator.util.ResourceUtil.buildIdentifier;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.model.Annotation;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Encounter;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Meta;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.dstu3.model.ReferralRequest;
import org.hl7.fhir.dstu3.model.ReferralRequest.ReferralCategory;
import org.hl7.fhir.dstu3.model.ReferralRequest.ReferralRequestStatus;
import org.hl7.fhir.dstu3.model.ResourceType;
import org.hl7.v3.CD;
import org.hl7.v3.CR;
import org.hl7.v3.CV;
import org.hl7.v3.IVLTS;
import org.hl7.v3.RCMRMT030101UKComponent;
import org.hl7.v3.RCMRMT030101UKComponent3;
import org.hl7.v3.RCMRMT030101UKComponent4;
import org.hl7.v3.RCMRMT030101UKEhrComposition;
import org.hl7.v3.RCMRMT030101UKEhrExtract;
import org.hl7.v3.RCMRMT030101UKLinkSet;
import org.hl7.v3.RCMRMT030101UKPart;
import org.hl7.v3.RCMRMT030101UKRequestStatement;
import org.hl7.v3.RCMRMT030101UKResponsibleParty3;
Expand All @@ -38,6 +46,7 @@
import uk.nhs.adaptors.pss.translator.util.DateFormatUtil;
import uk.nhs.adaptors.pss.translator.util.DegradedCodeableConcepts;
import uk.nhs.adaptors.pss.translator.util.ParticipantReferenceUtil;
import uk.nhs.adaptors.pss.translator.util.ResourceFilterUtil;

@Service
@AllArgsConstructor
Expand Down Expand Up @@ -65,13 +74,17 @@ public List<ReferralRequest> mapResources(RCMRMT030101UKEhrExtract ehrExtract,
List<Encounter> encounters,
String practiceCode) {

return mapEhrExtractToFhirResource(ehrExtract, (extract, composition, component) ->
var referralRequests = mapEhrExtractToFhirResource(ehrExtract, (extract, composition, component) ->
extractAllRequestStatements(component)
.filter(Objects::nonNull)
.filter(this::isNotSelfReferral)
.map(requestStatement
-> mapToReferralRequest(ehrExtract, composition, requestStatement, patient, encounters, practiceCode)))
-> mapToReferralRequest(ehrExtract, composition, requestStatement, patient, encounters, practiceCode)))
.toList();

populateSupportingInfoWithDocumentReferencesFromLinkSets(ehrExtract, referralRequests);
MartinWheelerMT marked this conversation as resolved.
Show resolved Hide resolved

return referralRequests;
}

public ReferralRequest mapToReferralRequest(RCMRMT030101UKEhrExtract ehrExtract,
Expand Down Expand Up @@ -274,4 +287,44 @@ private boolean isNotSelfReferral(RCMRMT030101UKRequestStatement requestStatemen
}
return true;
}

private static void populateSupportingInfoWithDocumentReferencesFromLinkSets(
RCMRMT030101UKEhrExtract ehrExtract,
List<ReferralRequest> referralRequests
) {
extractReferralRequestIdToDocumentReferences(ehrExtract).forEach(
(referralRequestId, documentReferences) -> referralRequests.stream()
.filter(referralRequest -> referralRequestId.equals(referralRequest.getId()))
.findFirst()
.ifPresent(referencedReferralRequest -> referencedReferralRequest.setSupportingInfo(documentReferences))
);
}

private static Map<String, List<Reference>> extractReferralRequestIdToDocumentReferences(
MartinWheelerMT marked this conversation as resolved.
Show resolved Hide resolved
RCMRMT030101UKEhrExtract ehrExtract) {

return ehrExtract.getComponent()
.stream()
.map(RCMRMT030101UKComponent::getEhrFolder)
.flatMap(ehrFolder -> ehrFolder.getComponent().stream())
.map(RCMRMT030101UKComponent3::getEhrComposition)
.flatMap(ehrComposition -> ehrComposition.getComponent().stream())
.map(RCMRMT030101UKComponent4::getLinkSet)
.filter(Objects::nonNull)
.filter(linkSet -> ResourceFilterUtil.isReferralRequestToExternalDocumentLinkSet(ehrExtract, linkSet))
.map(ReferralRequestMapper::buildReferralRequestToDocumentReferenceSimpleEntry)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

private static AbstractMap.SimpleEntry<String, List<Reference>> buildReferralRequestToDocumentReferenceSimpleEntry(
RCMRMT030101UKLinkSet linkSet
) {
var referralRequestId = linkSet.getConditionNamed().getNamedStatementRef().getId().getRoot();
var documentReferences = linkSet.getComponent().stream()
.map(component -> component.getStatementRef().getId().getRoot())
.map(id -> new Reference(new IdType(ResourceType.DocumentReference.name(), id)))
.toList();

return new AbstractMap.SimpleEntry<>(referralRequestId, documentReferences);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,26 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.hl7.v3.RCMRMT030101UKComponent;
import org.hl7.v3.RCMRMT030101UKComponent3;
import org.hl7.v3.RCMRMT030101UKComponent4;
import org.hl7.v3.RCMRMT030101UKCompoundStatement;
import org.hl7.v3.RCMRMT030101UKEhrExtract;
import org.hl7.v3.RCMRMT030101UKLinkSet;
import org.hl7.v3.RCMRMT030101UKNarrativeStatement;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ResourceFilterUtil {

private static final List<String> ALLERGY_CODES = List.of("SN53.00", "14L..00");
private static final String CODE_SYSTEM_READ_CODE_V2 = "2.16.840.1.113883.2.1.6.2";
private static final String SNOMED_CODE_SYSTEM = "2.16.840.1.113883.2.1.3.2.4.15";
private static final String UNSPECIFIED_PROBLEM_CODE = "394776006";
private static final String PATHOLOGY_CODE = "16488004";
private static final String SPECIMEN_CODE = "123038009";
private static final String BATTERY_VALUE = "BATTERY";
Expand Down Expand Up @@ -82,6 +90,63 @@ public static boolean isTemplate(RCMRMT030101UKCompoundStatement compoundStateme
&& List.of(BATTERY_VALUE, CLUSTER_VALUE).contains(compoundStatement.getClassCode().getFirst());
}

public static boolean isReferralRequestToExternalDocumentLinkSet(RCMRMT030101UKEhrExtract ehrExtract, RCMRMT030101UKLinkSet linkSet) {
return hasUnspecifiedProblemCodeWithoutQualifierOrOriginalText(linkSet)
&& !linkSet.getComponent().isEmpty()
&& hasNamedStatementRefReferencingARequestStatement(ehrExtract, linkSet)
&& containsOnlyComponentsWithStatementRefReferencingADocument(ehrExtract, linkSet);
}

private static boolean hasUnspecifiedProblemCodeWithoutQualifierOrOriginalText(RCMRMT030101UKLinkSet linkSet) {
return linkSet.hasCode()
&& SNOMED_CODE_SYSTEM.equals(linkSet.getCode().getCodeSystem())
&& UNSPECIFIED_PROBLEM_CODE.equals(linkSet.getCode().getCode())
&& linkSet.getCode().getQualifier().isEmpty()
&& !linkSet.getCode().hasOriginalText();
}

private static boolean containsOnlyComponentsWithStatementRefReferencingADocument(
RCMRMT030101UKEhrExtract ehrExtract,
RCMRMT030101UKLinkSet linkSet
) {
var statementRefIds = linkSet.getComponent()
.stream()
.map(component -> component.getStatementRef().getId().getRoot())
.collect(Collectors.toSet());

return extractAllEhrCompositionComponentsAsStream(ehrExtract)
.flatMap(CompoundStatementResourceExtractors::extractAllNonBloodPressureNarrativeStatements)
.filter(Objects::nonNull)
.filter(ResourceFilterUtil::isDocumentReference)
.map(narrativeStatement -> narrativeStatement.getId().getRoot())
.collect(Collectors.toSet())
.containsAll(statementRefIds);
}

private static boolean hasNamedStatementRefReferencingARequestStatement(
RCMRMT030101UKEhrExtract ehrExtract,
RCMRMT030101UKLinkSet linkSet
) {
if (linkSet.getConditionNamed() == null) {
return false;
}
var namedStatementRefId = linkSet.getConditionNamed().getNamedStatementRef().getId().getRoot();

return extractAllEhrCompositionComponentsAsStream(ehrExtract)
.flatMap(CompoundStatementResourceExtractors::extractAllRequestStatements)
.filter(Objects::nonNull)
.anyMatch(requestStatement -> namedStatementRefId.equals(requestStatement.getId().getFirst().getRoot()));
}

private static Stream<RCMRMT030101UKComponent4> extractAllEhrCompositionComponentsAsStream(RCMRMT030101UKEhrExtract ehrExtract) {
return ehrExtract.getComponent()
.stream()
.map(RCMRMT030101UKComponent::getEhrFolder)
.flatMap(ehrFolder -> ehrFolder.getComponent().stream())
.map(RCMRMT030101UKComponent3::getEhrComposition)
.flatMap(ehrComposition -> ehrComposition.getComponent().stream());
}

private static boolean hasCode(RCMRMT030101UKCompoundStatement compoundStatement) {
return compoundStatement.hasCode() && compoundStatement.getCode().hasCode();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,18 @@ void When_Condition_With_NopatConfidentialityCodeInEhrComposition_Expect_MetaFro
);
}

@Test
void When_MappingLinksetWhichIsAReferralRequestToExternalDocumentLinkSet_Expect_ConditionNotToBeMapped() {
final var ehrExtract = unmarshallEhrExtract(
"ResourceFilter",
"ehr_extract_with_referral_request_to_external_document_linkset.xml"
);

final List<Condition> conditions = conditionMapper.mapResources(ehrExtract, patient, Collections.emptyList(), PRACTISE_CODE);

assertThat(conditions).hasSize(0);
}

private void addMedicationRequestsToBundle(Bundle bundle) {
var planMedicationRequest = new MedicationRequest().setId(AUTHORISE_ID);
var orderMedicationRequest = new MedicationRequest().setId(PRESCRIBE_ID);
Expand Down Expand Up @@ -448,11 +460,16 @@ private void assertAllConditionsHaveMeta(List<Condition> conditions, Meta expect
}

@SneakyThrows
private RCMRMT030101UKEhrExtract unmarshallEhrExtract(String filename) {
final File file = FileFactory.getXmlFileFor(TEST_FILES_DIRECTORY, filename);
private RCMRMT030101UKEhrExtract unmarshallEhrExtract(String testFilesDirectory, String filename) {
final File file = FileFactory.getXmlFileFor(testFilesDirectory, filename);
return unmarshallFile(file, RCMRMT030101UKEhrExtract.class);
}

@SneakyThrows
private RCMRMT030101UKEhrExtract unmarshallEhrExtract(String filename) {
return unmarshallEhrExtract(TEST_FILES_DIRECTORY, filename);
}

private void configureCommonStubs() {
Mockito.lenient().when(dateTimeMapper.mapDateTime(
any(String.class)
Expand Down
Loading