Skip to content

Commit

Permalink
Merge pull request #78 from MeasureAuthoringTool/MAT-6856/populateSav…
Browse files Browse the repository at this point in the history
…edCodesSubTab

validate codes list for saved codes tab
  • Loading branch information
sb-prateekkeerthi authored May 10, 2024
2 parents 8a83bc3 + 70cfc87 commit faac06a
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.security.Principal;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping(path = "/terminology")
Expand Down Expand Up @@ -85,4 +86,13 @@ public ResponseEntity<Code> getCode(
return ResponseEntity.ok()
.body(fhirTerminologyService.retrieveCode(code, codeSystem, version, user.getApiKey()));
}

@PostMapping(path = "/codes", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Code>> getCodesAndCodeSystems(
@RequestBody() List<Map<String, String>> codeList, Principal principal) {
final String username = principal.getName();
UmlsUser user = vsacService.verifyUmlsAccess(username);
return ResponseEntity.ok()
.body(fhirTerminologyService.retrieveCodesAndCodeSystems(codeList, user.getApiKey()));
}
}
3 changes: 2 additions & 1 deletion src/main/java/gov/cms/madie/terminology/dto/Code.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
public class Code {
private String name;
private String display;
private String version;
private String version; // 'fhir' in the code-system-entry.json
private String svsVersion; // 'vsac' in the code-system-entry.json
private String codeSystem;
private String codeSystemOid;
private CodeStatus status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ public interface CodeSystemRepository extends MongoRepository<CodeSystem, String
Optional<CodeSystem> findById(String id);

Optional<CodeSystem> findByNameAndVersion(String name, String version);

Optional<CodeSystem> findByOidAndVersion(String oid, String version);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@
import org.springframework.web.util.UriComponentsBuilder;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
Expand Down Expand Up @@ -278,4 +277,94 @@ private void updateOrInsertAllCodeSystems(List<CodeSystem> codeSystemList) {
}
}
}

public List<Code> retrieveCodesAndCodeSystems(List<Map<String, String>> codeList, String apiKey) {
return codeList.stream()
.map(
code -> {
List<CodeSystemEntry> codeSystemEntries = mappingService.getCodeSystemEntries();
String codeName = code.get("code");
String codeSystemName = code.get("codeSystem");
String oid = code.get("oid") != null ? code.get("oid").replaceAll("'|'", "") : null;

Optional<Map.Entry<String, String>> mappedVersion =
mapToFhirVersion(code.get("version"), oid, codeSystemEntries);

if (mappedVersion.isPresent()) {
String vsacVersion = mappedVersion.get().getKey();
String fhirVersion = mappedVersion.get().getValue();

return retrieveCodes(
codeName, codeSystemName, vsacVersion, fhirVersion, oid, apiKey);
}
return null;
})
.collect(Collectors.toList());
}

private Optional<Map.Entry<String, String>> mapToFhirVersion(
String version, String oid, List<CodeSystemEntry> codeSystemEntries) {
if (oid == null) {
return Optional.empty();
}

Optional<Map.Entry<String, String>> result;
if (version == null) {
result =
codeSystemEntries.stream()
.filter(codeSystemEntry -> StringUtils.equals(codeSystemEntry.getOid(), oid))
.findFirst()
.map(
codeSystemVersion ->
Map.entry(
codeSystemVersion.getVersions().get(0).getVsac(),
codeSystemVersion.getVersions().get(0).getFhir()));
} else {
result =
codeSystemEntries.stream()
.filter(codeSystemEntry -> StringUtils.equals(codeSystemEntry.getOid(), oid))
.flatMap(codeSystemEntry -> codeSystemEntry.getVersions().stream())
.filter(codeSystemVersion -> StringUtils.equals(codeSystemVersion.getVsac(), version))
.map(
codeSystemVersion ->
Map.entry(codeSystemVersion.getVsac(), codeSystemVersion.getFhir()))
.findFirst();
}

return result;
}

private Code retrieveCodes(
String codeName,
String codeSystemName,
String vsacVersion,
String fhirVersion,
String oid,
String apiKey) {
if (StringUtils.isEmpty(codeName)
|| StringUtils.isEmpty(codeSystemName)
|| StringUtils.isEmpty(fhirVersion)) {
return null;
}
CodeSystem codeSystem = codeSystemRepository.findByOidAndVersion(oid, fhirVersion).orElse(null);
if (codeSystem == null) {
return null;
}
String codeJson = fhirTerminologyServiceWebClient.getCodeResource(codeName, codeSystem, apiKey);

Parameters parameters = fhirContext.newJsonParser().parseResource(Parameters.class, codeJson);
Code code =
Code.builder()
.name(codeName)
.codeSystem(codeSystemName)
.version(fhirVersion)
.svsVersion(vsacVersion)
.display(parameters.getParameter("display").getValue().toString())
.codeSystemOid(parameters.getParameter("Oid").getValue().toString())
.build();

CodeStatus status = vsacService.getCodeStatus(code, apiKey);
code.setStatus(status);
return code;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ public List<CodeSystemEntry> getCodeSystemEntries() {
return Collections.emptyList();
}

public CodeSystemEntry getCodeSystemEntry(String codeSystemName) {
public CodeSystemEntry getCodeSystemEntryByOid(String oid) {
List<CodeSystemEntry> codeSystemEntries = getCodeSystemEntries();
if (CollectionUtils.isEmpty(codeSystemEntries)) {
return null;
}
return codeSystemEntries.stream()
.filter(entry -> Objects.equals(entry.getName(), codeSystemName))
.filter(
entry ->
Objects.equals(entry.getOid(), oid.startsWith("urn:oid:") ? oid : "urn:oid:" + oid))
.findFirst()
.orElse(null);
}
Expand Down
42 changes: 26 additions & 16 deletions src/main/java/gov/cms/madie/terminology/service/VsacService.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,34 @@ public List<CqlCode> validateCodes(List<CqlCode> cqlCodes, UmlsUser umlsUser, St
}

public CodeStatus getCodeStatus(Code code, String apiKey) {
CodeSystemEntry systemEntry = mappingService.getCodeSystemEntry(code.getCodeSystem());
String svsVersion = getSvsCodeSystemVersion(code);
if (svsVersion == null) {
return CodeStatus.NA;
}
// prepare code path e.g. CODE:/CodeSystem/ActCode/Version/9.0.0/Code/AMB/Info
String codePath =
TerminologyServiceUtil.buildCodePath(code.getCodeSystem(), svsVersion, code.getName());
VsacCode svsCode = terminologyWebClient.getCode(codePath, apiKey);
if (svsCode.getStatus().equalsIgnoreCase("ok")) {
if ("Yes".equals(svsCode.getData().getResultSet().get(0).getActive())) {
return CodeStatus.ACTIVE;
} else {
return CodeStatus.INACTIVE;
}
}
return CodeStatus.NA;
}

private String getSvsCodeSystemVersion(Code code) {
if (StringUtils.isNotBlank(code.getSvsVersion())) {
return code.getSvsVersion();
}
CodeSystemEntry systemEntry = mappingService.getCodeSystemEntryByOid(code.getCodeSystemOid());
// do not call SVS API to get code status if the system is not in SVS API
if (systemEntry == null
|| systemEntry.getOid().contains("NOT.IN.VSAC")
|| CollectionUtils.isEmpty(systemEntry.getVersions())) {
return CodeStatus.NA;
return null;
}

// get corresponding SVS version for given FHIR version
Expand All @@ -176,21 +198,9 @@ public CodeStatus getCodeStatus(Code code, String apiKey) {
.findFirst()
.orElse(null);
if (version == null || version.getVsac() == null) {
return CodeStatus.NA;
}
// prepare code path e.g. CODE:/CodeSystem/ActCode/Version/9.0.0/Code/AMB/Info
String codePath =
TerminologyServiceUtil.buildCodePath(
code.getCodeSystem(), version.getVsac(), code.getName());
VsacCode svsCode = terminologyWebClient.getCode(codePath, apiKey);
if (svsCode.getStatus().equalsIgnoreCase("ok")) {
if ("Yes".equals(svsCode.getData().getResultSet().get(0).getActive())) {
return CodeStatus.ACTIVE;
} else {
return CodeStatus.INACTIVE;
}
return null;
}
return CodeStatus.NA;
return version.getVsac();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import gov.cms.madie.models.measure.ManifestExpansion;
import gov.cms.madie.terminology.dto.Code;
import gov.cms.madie.terminology.dto.CodeStatus;
import gov.cms.madie.terminology.dto.QdmValueSet;
import gov.cms.madie.terminology.dto.ValueSetsSearchCriteria;
import gov.cms.madie.terminology.exceptions.VsacUnauthorizedException;
Expand All @@ -22,10 +23,7 @@

import java.security.Principal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.*;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -230,4 +228,33 @@ void testGetCodeIfNoUmlsUserFound() {
() -> vsacFhirTerminologyController.getCode(codeName, codeSystem, version, principal));
assertEquals(ex.getMessage(), "Please login to UMLS before proceeding");
}

@Test
void testGetCodesList() {
List<Map<String, String>> codeList =
List.of(
Map.of(
"code", "1963-8", "codeSystem", "LOINC", "oid", "'urn:oid:2.16.840.1.113883.6.1'"),
Map.of(
"code", "8462-4", "codeSystem", "LOINC", "oid", "'urn:oid:2.16.840.1.113883.6.1'"));
Code code =
Code.builder()
.name("1963-8")
.codeSystem("LOINC")
.version("2.72")
.display("Bicarbonate [Moles/volume] in Serum")
.codeSystemOid("2.16.840.1.113883.6.1")
.status(CodeStatus.valueOf("ACTIVE"))
.build();

Principal principal = mock(Principal.class);
when(principal.getName()).thenReturn(TEST_USER);
when(vsacService.verifyUmlsAccess(anyString())).thenReturn(umlsUser);
when(fhirTerminologyService.retrieveCodesAndCodeSystems(any(), anyString()))
.thenReturn(List.of(code));
ResponseEntity<List<Code>> response =
vsacFhirTerminologyController.getCodesAndCodeSystems(codeList, principal);
assertEquals(response.getStatusCode(), HttpStatus.OK);
assertEquals(response.getBody().get(0), code);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -451,4 +451,70 @@ void testRetrieveCodeSuccessfully() {
assertThat(code.getVersion(), is(equalTo(version)));
assertThat(code.getStatus(), is(equalTo(CodeStatus.ACTIVE)));
}

@Test
void testRetrieveCodesListSuccessfully() {
List<Map<String, String>> codeList =
List.of(
Map.of(
"code", "1963-8", "codeSystem", "LOINC", "oid", "'urn:oid:2.16.840.1.113883.6.1'"));

String codeJson =
"{\n"
+ " \"resourceType\": \"Parameters\",\n"
+ " \"parameter\": [ {\n"
+ " \"name\": \"name\",\n"
+ " \"valueString\": \"LOINC\"\n"
+ " }, {\n"
+ " \"name\": \"version\",\n"
+ " \"valueString\": \"2.40\"\n"
+ " }, {\n"
+ " \"name\": \"display\",\n"
+ " \"valueString\": \"Bicarbonate [Moles/volume] in Serum\"\n"
+ " }, {\n"
+ " \"name\": \"Oid\",\n"
+ " \"valueString\": \"2.16.840.1.113883.6.1\"\n"
+ " } ]\n"
+ "}";

codeSystemEntries = new ArrayList<>();
CodeSystemEntry.Version version = new CodeSystemEntry.Version();
version.setVsac("2.40");
version.setFhir("2.40");
var codeSystemEntry =
CodeSystemEntry.builder()
.name("8462-4")
.oid("urn:oid:2.16.840.1.113883.6.1")
.url("http://loinc.org")
.versions(List.of(version))
.build();
codeSystemEntries.add(codeSystemEntry);

gov.cms.madie.terminology.models.CodeSystem codeSystem =
gov.cms.madie.terminology.models.CodeSystem.builder()
.id("LOINC2.40")
.fullUrl("http://loinc.org")
.title("LOINC")
.name("LOINC")
.version("2.40")
.versionId("404676818")
.oid("urn:oid:2.16.840.1.113883.6.1")
.lastUpdated(Instant.parse("2024-04-30T20:18:48.706Z"))
.lastUpdatedUpstream(new Date("Fri Apr 01 00:00:00 EDT 2022"))
.build();

when(mappingService.getCodeSystemEntries()).thenReturn(codeSystemEntries);
when(codeSystemRepository.findByOidAndVersion(anyString(), anyString()))
.thenReturn(Optional.ofNullable(codeSystem));
when(fhirTerminologyServiceWebClient.getCodeResource(anyString(), any(), any()))
.thenReturn(codeJson);
when(fhirContext.newJsonParser()).thenReturn(FhirContext.forR4().newJsonParser());
when(vsacService.getCodeStatus(any(), anyString())).thenReturn(CodeStatus.ACTIVE);
List<Code> code = fhirTerminologyService.retrieveCodesAndCodeSystems(codeList, TEST_API_KEY);
assertThat(code.get(0).getName(), is(equalTo("1963-8")));
assertThat(code.get(0).getDisplay(), is(equalTo("Bicarbonate [Moles/volume] in Serum")));
assertThat(code.get(0).getCodeSystem(), is(equalTo("LOINC")));
assertThat(code.get(0).getVersion(), is(equalTo("2.40")));
assertThat(code.get(0).getStatus(), is(equalTo(CodeStatus.ACTIVE)));
}
}
Loading

0 comments on commit faac06a

Please sign in to comment.