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-3148 - Immunizations outside of consultation are mapped to Observation #752

Merged
merged 20 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8c09388
[NIAD-3148] Add method to TestUtility.java `getEhrFolderComponent`
martin-nhs Aug 8, 2024
e6a8954
[NIAD-3148] Add method to TestUtility.java `getEhrFolderComponent`
martin-nhs Aug 8, 2024
5073d11
[NIAD-3148] Add some unit tests for current logic
martin-nhs Aug 8, 2024
60306be
[NIAD-3148] Remove redundant test file
martin-nhs Aug 8, 2024
1125336
[NIAD-3148] Initial refactor of DatabaseImmunizationChecker.java
martin-nhs Aug 8, 2024
a30b77a
[NIAD-3148] Create DatabaseImmunizationCheckerTest
martin-nhs Aug 8, 2024
30caf90
[NIAD-3148] Create utility to create CD values
martin-nhs Aug 8, 2024
e4a52cf
[NIAD-3148] Add functionality to create-database-postgres.sql to join…
martin-nhs Aug 8, 2024
4806577
[NIAD-3148] Method signature renamed to `getImmunizationSnomedUsingCo…
martin-nhs Aug 8, 2024
b4db52d
[NIAD-3148] Address problem with test-load-immunization-codes.sh
martin-nhs Aug 8, 2024
6854754
Add DirtiesContext to DatabaseImmunizationCheckerIT
MartinWheelerMT Aug 9, 2024
b5ed36a
[NIAD-3148] Address PR to https://github.com/NHSDigital/nia-patient-s…
martin-nhs Aug 9, 2024
71c4a5b
[NIAD-3148] Address PR comments https://github.com/NHSDigital/nia-pat…
martin-nhs Aug 9, 2024
c617824
[NIAD-3148] Address PR comment https://github.com/NHSDigital/nia-pati…
martin-nhs Aug 9, 2024
a0a14c5
[NIAD-3148] Update CHANGELOG.md
martin-nhs Aug 9, 2024
d0033c8
[NIAD-3148] Update CHANGELOG.md
martin-nhs Aug 9, 2024
d00968d
[NIAD-3148] Address PR comment https://github.com/NHSDigital/nia-pati…
martin-nhs Aug 12, 2024
d7c8d10
[NIAD-3148] Address PR comment https://github.com/NHSDigital/nia-pati…
martin-nhs Aug 12, 2024
6f65eb2
[NIAD-3148] Changed test-load-immunization-codes.sh, now if a given e…
martin-nhs Aug 12, 2024
7dbe037
[NIAD-3148] Address Adrian's PR comment regarding name change
martin-nhs Aug 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
public interface ImmunizationSnomedCTDao {
@SqlQuery("select_immunization_concept_id")
@UseClasspathSqlLocator
ImmunizationSnomedCT getImmunizationSnomednUsingConceptId(@Bind("conceptId") String conceptId);
ImmunizationSnomedCT getImmunizationSnomedUsingConceptOrDescriptionId(@Bind("sctid") String sctId);
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved

@SqlQuery("verify_immunizations_loaded")
@UseClasspathSqlLocator
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
SELECT i.conceptid FROM "snomedct".immunization_codes i WHERE conceptid = :conceptId;
SELECT i.conceptid FROM "snomedct".immunization_codes i WHERE conceptid = :sctid OR descriptionid = :sctid;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- the conceptIds being checked for below represent the immunization root codes and those immunization codes outside
-- of the root code hierarchy, as described in the snomed-database-loader README.md file

SELECT COUNT(i.conceptid) = 16 -- total number of codes
SELECT COUNT(DISTINCT i.conceptid) = 16 -- total number of codes
FROM "snomedct".immunization_codes i
WHERE i.conceptid IN (
'787859002', '127785005', '304250009', '90351000119108','713404003','2997511000001102',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package uk.nhs.adaptors.pss.translator;

import org.hl7.v3.RCMRMT030101UKObservationStatement;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import uk.nhs.adaptors.pss.translator.util.DatabaseImmunizationChecker;

import static org.assertj.core.api.Assertions.assertThat;
import static uk.nhs.adaptors.pss.translator.TestUtility.createCd;

@ExtendWith(SpringExtension.class)
@SpringBootTest
class DatabaseImmunizationCheckerIT {
private static final String SNOMED_CODE_SYSTEM = "2.16.840.1.113883.2.1.3.2.4.15";
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved
private static final String DISPLAY_NAME = "Administration of yellow fever vaccine";

@Autowired
private DatabaseImmunizationChecker databaseImmunizationChecker;

@Test
void When_Immunization_With_SnomedDescriptionId_Expect_True() {
final String immunizationDescriptionSnomedId = "67308009";
final RCMRMT030101UKObservationStatement observationStatement =
new RCMRMT030101UKObservationStatement();

observationStatement.setCode(createCd(
immunizationDescriptionSnomedId,
SNOMED_CODE_SYSTEM,
DISPLAY_NAME
));
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved

final boolean result = databaseImmunizationChecker.isImmunization(observationStatement);

assertThat(result).isTrue();
}

@Test
void When_Immunization_With_SnomedConceptId_Expect_True() {
final String immunizationConceptSnomedId = "67308009";
final RCMRMT030101UKObservationStatement observationStatement =
new RCMRMT030101UKObservationStatement();

observationStatement.setCode(createCd(
immunizationConceptSnomedId,
SNOMED_CODE_SYSTEM,
DISPLAY_NAME
));

final boolean result = databaseImmunizationChecker.isImmunization(observationStatement);

assertThat(result).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,39 @@

import lombok.RequiredArgsConstructor;
import uk.nhs.adaptors.connector.dao.ImmunizationSnomedCTDao;
import uk.nhs.adaptors.connector.model.ImmunizationSnomedCT;

@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DatabaseImmunizationChecker implements ImmunizationChecker {
private final ImmunizationSnomedCTDao immunizationSnomedDao;

@Override
public boolean isImmunization(RCMRMT030101UKObservationStatement observationStatement) {
ImmunizationSnomedCT immunizationCode = null;
final boolean translationIsPresent = !observationStatement.getCode().getTranslation().isEmpty();

if (!observationStatement.getCode().getTranslation().isEmpty()) {
immunizationCode = immunizationSnomedDao
.getImmunizationSnomednUsingConceptId(observationStatement.getCode().getTranslation().get(0).getCode());
}
if (translationIsPresent) {
final boolean isImmunization = isTranslationCodeImmunization(observationStatement);

if (immunizationCode == null) {
immunizationCode = immunizationSnomedDao.getImmunizationSnomednUsingConceptId(observationStatement.getCode().getCode());
if (isImmunization) {
return true;
}
}

return immunizationCode != null;
return isCodeImmunization(observationStatement);
}

private boolean isTranslationCodeImmunization(RCMRMT030101UKObservationStatement observationStatement) {
final String code = observationStatement.getCode()
.getTranslation()
.getFirst()
.getCode();

return immunizationSnomedDao.getImmunizationSnomedUsingConceptOrDescriptionId(code) != null;
}

private boolean isCodeImmunization(RCMRMT030101UKObservationStatement observationStatement) {
final String code = observationStatement.getCode().getCode();

return immunizationSnomedDao.getImmunizationSnomedUsingConceptOrDescriptionId(code) != null;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package uk.nhs.adaptors.pss.translator;

import org.hl7.v3.CD;
import org.hl7.v3.CV;
import org.hl7.v3.RCMRMT030101UKComponent3;
import org.hl7.v3.RCMRMT030101UKEhrComposition;
import org.hl7.v3.RCMRMT030101UKEhrExtract;
import org.hl7.v3.RCMRMT030101UKEhrFolder;

import java.util.List;
import java.util.function.Function;

public final class TestUtility {
private TestUtility() { }

public static final Function<RCMRMT030101UKEhrExtract, RCMRMT030101UKEhrComposition> GET_EHR_COMPOSITION =
extract -> extract
.getComponent().get(0)
.getComponent().getFirst()
.getEhrFolder()
.getComponent().get(0)
.getComponent().getFirst()
.getEhrComposition();
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved

public static CV createCv(String code, String codeSystem, String displayName) {
Expand All @@ -28,6 +32,32 @@ public static CV createCv(String code) {
return createCv(code, "", "");
}

public static CD createCd(String code, String codeSystem, String displayName) {
final CD cd = new CD();
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved
cd.setCode(code);
cd.setCodeSystem(codeSystem);
cd.setDisplayName(displayName);
return cd;
}

/**
* An EHR Extract has a cardinality of one to many components, each component (based
* of the UK05 schema) can contain one and only one EHR Folder. This utility method provides
* a means of extracting ALL components from within a target EHR Folder.
* @param extract The EHR Extract.
* @param extractComponentIndex The index of the RCMRMT030101UKComponent which houses the EHR Folder.
* @return A list of RCMRMT030101UKComponent3.
*/
public static List<RCMRMT030101UKComponent3> getEhrFolderComponent(RCMRMT030101UKEhrExtract extract,
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved
int extractComponentIndex) {
final RCMRMT030101UKEhrFolder targetFolder = extract
.getComponent()
.get(extractComponentIndex)
.getEhrFolder();

return targetFolder.getComponent();
}

public static class NoConfidentialityCodePresentException extends RuntimeException {
private static final String EXCEPTION_MESSAGE = "No confidentiality code is present within the test file.";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package uk.nhs.adaptors.pss.translator.util;

import jakarta.xml.bind.JAXBException;
import org.hl7.v3.RCMRMT030101UKComponent3;
import org.hl7.v3.RCMRMT030101UKEhrExtract;
import org.hl7.v3.RCMRMT030101UKObservationStatement;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import uk.nhs.adaptors.connector.dao.ImmunizationSnomedCTDao;
import uk.nhs.adaptors.connector.model.ImmunizationSnomedCT;
import uk.nhs.adaptors.pss.translator.FileFactory;

import java.io.File;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.Mockito.when;
import static uk.nhs.adaptors.pss.translator.TestUtility.getEhrFolderComponent;
import static uk.nhs.adaptors.pss.translator.util.XmlUnmarshallUtil.unmarshallFile;

@ExtendWith(MockitoExtension.class)
class DatabaseImmunizationCheckerTest {
@Mock
private ImmunizationSnomedCTDao immunizationSnomedCTDao;

@InjectMocks
private DatabaseImmunizationChecker databaseImmunizationChecker;

@Captor
private ArgumentCaptor<String> snomedCtIdCaptor;

private static final String TEST_FILES_DIRECTORY = "Immunization";

@Test
void When_IsObservationStatementImmunization_With_ImmunizationCode_Expect_True() throws JAXBException {
final String expectedCode = "3955997015";
final RCMRMT030101UKObservationStatement observationStatement = getObservationStatementFromExtract(
"full_valid_immunization_with_no_translation.xml");
final ImmunizationSnomedCT immunizationSnomedCT = ImmunizationSnomedCT.builder()
.conceptId(expectedCode)
.build();

when(immunizationSnomedCTDao.getImmunizationSnomedUsingConceptOrDescriptionId(
snomedCtIdCaptor.capture()
)).thenReturn(immunizationSnomedCT);

final boolean result = databaseImmunizationChecker.isImmunization(observationStatement);

assertAll(
() -> assertThat(result).isTrue(),
() -> assertThat(snomedCtIdCaptor.getValue()).isEqualTo(expectedCode)
);
}

@Test
void When_IsObservationStatementImmunization_With_ImmunizationCodeAndNonImmunizationTranslation_Expect_True() throws JAXBException {
final String snomedCode = "142934010";
final String readsV2Code = "65E..00";
final RCMRMT030101UKObservationStatement observationStatement = getObservationStatementFromExtract(
"full_valid_immunization_with_translation.xml"
);

final ImmunizationSnomedCT immunizationSnomedCT = ImmunizationSnomedCT.builder()
.conceptId(snomedCode)
.build();

when(immunizationSnomedCTDao.getImmunizationSnomedUsingConceptOrDescriptionId(
snomedCtIdCaptor.capture()
)).thenReturn(null, immunizationSnomedCT);

final boolean result = databaseImmunizationChecker.isImmunization(observationStatement);

assertAll(
() -> assertThat(result).isTrue(),
() -> assertThat(snomedCtIdCaptor.getAllValues().getFirst()).isEqualTo(readsV2Code),
() -> assertThat(snomedCtIdCaptor.getAllValues().get(1)).isEqualTo(snomedCode)
);
}

private RCMRMT030101UKObservationStatement getObservationStatementFromExtract(String filename) throws JAXBException {
final RCMRMT030101UKEhrExtract ehrExtract = getEhrExtractFromFile(filename);
final List<RCMRMT030101UKComponent3> components = getEhrFolderComponent(ehrExtract, 0);

return components.getFirst()
.getEhrComposition()
.getComponent()
.getFirst()
.getObservationStatement();
}

private RCMRMT030101UKEhrExtract getEhrExtractFromFile(String filename) throws JAXBException {
final File file = FileFactory.getXmlFileFor(TEST_FILES_DIRECTORY, filename);
return unmarshallFile(file, RCMRMT030101UKEhrExtract.class);
}
}
Loading