Skip to content

Commit

Permalink
Merge pull request #120 from MeasureAuthoringTool/MAT-8013
Browse files Browse the repository at this point in the history
MAT-8013 Replacing the usage of SVS api for valueset search in QI Core measure test cases page
  • Loading branch information
adongare authored Jan 14, 2025
2 parents d25d042 + 5ccf8d1 commit 9846752
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 434 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package gov.cms.madie.terminology.controller;

import java.util.List;
import java.util.stream.Collectors;
import java.security.Principal;
import java.util.Optional;
import gov.cms.madie.terminology.dto.QdmValueSet;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -22,7 +20,6 @@

import generated.vsac.nlm.nih.gov.RetrieveMultipleValueSetsResponse;
import gov.cms.madie.models.cql.terminology.CqlCode;
import gov.cms.madie.terminology.dto.ValueSetsSearchCriteria;
import gov.cms.madie.terminology.models.UmlsUser;
import gov.cms.madie.terminology.service.VsacService;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -66,43 +63,6 @@ protected String serializeFhirValueset(ValueSet fhirValueSet) {
return fhirContext.newJsonParser().encodeResourceToString(fhirValueSet);
}

@PutMapping(path = "/value-sets/searches", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> searchValueSets(
Principal principal, @RequestBody ValueSetsSearchCriteria searchCriteria) {
log.debug("VsacController::getValueSets");

final String username = principal.getName();
Optional<UmlsUser> umlsUser = vsacService.findByHarpId(username);
if (umlsUser.isPresent()) {

List<RetrieveMultipleValueSetsResponse> vsacValueSets =
vsacService.getValueSets(searchCriteria, umlsUser.get());

List<ValueSet> fhirValueSets = vsacService.convertToFHIRValueSets(vsacValueSets);
String serializedValueSets =
fhirValueSets.stream().map(this::serializeFhirValueset).collect(Collectors.joining(", "));

return ResponseEntity.ok().body("[" + serializedValueSets + "]");
}
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}

@PutMapping("/qdm/value-sets/searches")
public ResponseEntity<List<QdmValueSet>> getQdmValueSets(
Principal principal, @RequestBody ValueSetsSearchCriteria searchCriteria) {
log.debug("VsacController::getQdmValueSets");

final String username = principal.getName();
Optional<UmlsUser> umlsUser = vsacService.findByHarpId(username);
if (umlsUser.isPresent()) {
List<QdmValueSet> qdmValueSets =
vsacService.getValueSetsInQdmFormat(searchCriteria, umlsUser.get());

return ResponseEntity.ok().body(qdmValueSets);
}
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}

@PutMapping(path = "/validations/codes", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<CqlCode>> validateCodes(
Principal principal,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gov.cms.madie.terminology.controller;

import ca.uhn.fhir.context.FhirContext;
import gov.cms.madie.models.measure.ManifestExpansion;
import gov.cms.madie.terminology.dto.Code;
import gov.cms.madie.terminology.dto.QdmValueSet;
Expand All @@ -12,6 +13,7 @@
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hl7.fhir.r4.model.ValueSet;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -21,6 +23,7 @@
import java.security.Principal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping(path = "/terminology")
Expand All @@ -29,6 +32,7 @@
public class VsacFhirTerminologyController {
private final FhirTerminologyService fhirTerminologyService;
private final VsacService vsacService;
private final FhirContext fhirContext;

@GetMapping("/manifest-list")
public ResponseEntity<List<ManifestExpansion>> getManifests(Principal principal) {
Expand All @@ -39,18 +43,30 @@ public ResponseEntity<List<ManifestExpansion>> getManifests(Principal principal)
}

@PutMapping("/value-sets/expansion/qdm")
public ResponseEntity<List<QdmValueSet>> getValueSetsExpansions(
public ResponseEntity<List<QdmValueSet>> getQdmValueSetsExpansions(
Principal principal, @RequestBody ValueSetsSearchCriteria searchCriteria) {
final String username = principal.getName();
log.info(
"User [{}] is attempting to fetch value sets expansions from VSAC FHIR Terminology Server.",
username);
log.info("User [{}] is attempting to fetch QDM value sets expansions.", username);
UmlsUser umlsUser = vsacService.verifyUmlsAccess(username);
List<QdmValueSet> qdmValueSets =
fhirTerminologyService.getValueSetsExpansionsForQdm(searchCriteria, umlsUser);
return ResponseEntity.ok().body(qdmValueSets);
}

@PutMapping("/value-sets/expansion/fhir")
public ResponseEntity<String> getFhirValueSetsExpansions(
Principal principal, @RequestBody ValueSetsSearchCriteria searchCriteria) {
final String username = principal.getName();
log.info("User [{}] is attempting to fetch FHIR value sets expansions.", username);
UmlsUser umlsUser = vsacService.verifyUmlsAccess(username);
List<ValueSet> fhirValueSets =
fhirTerminologyService.getValueSetsExpansion(searchCriteria, umlsUser);
String serializedValueSets =
fhirValueSets.stream().map(this::serializeFhirValueSet).collect(Collectors.joining(", "));

return ResponseEntity.ok().body("[" + serializedValueSets + "]");
}

@GetMapping(path = "/update-code-systems", produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("#request.getHeader('api-key') == #apiKey")
public ResponseEntity<List<CodeSystem>> retrieveAndUpdateCodeSystems(
Expand Down Expand Up @@ -106,4 +122,8 @@ public ResponseEntity<List<Code>> getCodesAndCodeSystems(
return ResponseEntity.ok()
.body(fhirTerminologyService.retrieveCodesAndCodeSystems(codeList, user.getApiKey()));
}

private String serializeFhirValueSet(ValueSet fhirValueSet) {
return fhirContext.newJsonParser().encodeResourceToString(fhirValueSet);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.hl7.fhir.r4.model.*;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.UriComponentsBuilder;

import java.time.Instant;
Expand Down Expand Up @@ -51,12 +52,11 @@ public List<ManifestExpansion> getManifests(UmlsUser umlsUser) {
}

// spin off requests based on values provided in the expansion
public List<QdmValueSet> recursivelyRequestAllValueSetsExpansionsForQDM(
List<QdmValueSet> allValueSets,
public List<ValueSet> recursivelyRequestAllValueSetsExpansions(
List<ValueSet> allValueSets,
String apiKey,
ValueSetsSearchCriteria.ValueSetParams vsParam,
ValueSetsSearchCriteria valueSetsSearchCriteria,
List<CodeSystemEntry> codeSystemEntries) {
ValueSetsSearchCriteria valueSetsSearchCriteria) {
IParser parser = fhirContext.newJsonParser();
String resource =
fhirTerminologyServiceWebClient.getValueSetResource(
Expand All @@ -67,59 +67,45 @@ public List<QdmValueSet> recursivelyRequestAllValueSetsExpansionsForQDM(
valueSetsSearchCriteria.getActiveOnly(),
valueSetsSearchCriteria.getManifestExpansion());

ValueSet valueSetResource = parser.parseResource(ValueSet.class, resource);
var total = valueSetResource.getExpansion().getTotal(); // total valuesets
ValueSet valueSet = parser.parseResource(ValueSet.class, resource);
// total number of pages in expansion, max 1000 codes supported in one-page request
var total = valueSet.getExpansion().getTotal();

List<QdmValueSet.Concept> concepts =
getValueSetConcepts(valueSetResource, codeSystemEntries, "QDM");
log.info(
"vs total [{}] count: [{}] offset: [{}], oid: [{}]",
total,
vsParam.getCount(),
vsParam.getOffset(),
vsParam.getOid());

// Check if the ValueSet with the same oid already exists in allValueSets
QdmValueSet existingValueSet =
ValueSet existingValueSet =
allValueSets.stream()
.filter(vs -> vs.getOid().equals(vsParam.getOid()))
.filter(vs -> valueSet.getIdPart().equals(valueSet.getIdPart()))
.findFirst()
.orElse(null);
if (existingValueSet != null) {
List<QdmValueSet.Concept> updatedConcepts = new ArrayList<>(existingValueSet.getConcepts());
updatedConcepts.addAll(concepts);
// Create a new QdmValueSet with the updated concepts
QdmValueSet updatedValueSet =
QdmValueSet.builder()
.oid(existingValueSet.getOid())
.displayName(existingValueSet.getDisplayName())
.version(existingValueSet.getVersion())
.concepts(updatedConcepts)
.build();
// Replace the existing QdmValueSet in the list
allValueSets.set(allValueSets.indexOf(existingValueSet), updatedValueSet);
// Check if the ValueSet with the same oid already exists in allValueSets, update if exists
// This happens if value set has multiple page requests.
if (existingValueSet == null) {
allValueSets.add(valueSet);
} else {
allValueSets.add(
QdmValueSet.builder()
.oid(valueSetResource.getIdPart())
.displayName(valueSetResource.getName())
.version(valueSetResource.getVersion())
.concepts(concepts)
.build());
existingValueSet.getExpansion().getContains().addAll(valueSet.getExpansion().getContains());
}
// if the total results in the searchSet are still greater than our current offset + the count
// of our last request, then we request again
if (vsParam.getOffset() + vsParam.getCount() <= total) {
vsParam.setOffset(vsParam.getOffset() + 1000);
return recursivelyRequestAllValueSetsExpansionsForQDM(
allValueSets, apiKey, vsParam, valueSetsSearchCriteria, codeSystemEntries);
return recursivelyRequestAllValueSetsExpansions(
allValueSets, apiKey, vsParam, valueSetsSearchCriteria);
}
return allValueSets;
}

public List<QdmValueSet> getValueSetsExpansionsForQdm(
public List<ValueSet> getValueSetsExpansion(
ValueSetsSearchCriteria valueSetsSearchCriteria, UmlsUser umlsUser) {
List<CodeSystemEntry> codeSystemEntries = mappingService.getCodeSystemEntries();
if (valueSetsSearchCriteria == null
|| CollectionUtils.isEmpty(valueSetsSearchCriteria.getValueSetParams())) {
return Collections.emptyList();
}

return valueSetsSearchCriteria.getValueSetParams().stream()
.map(
vsParam -> {
Expand All @@ -129,14 +115,30 @@ public List<QdmValueSet> getValueSetsExpansionsForQdm(
})
.flatMap(
vsParam ->
recursivelyRequestAllValueSetsExpansionsForQDM(
new ArrayList<>(),
umlsUser.getApiKey(),
vsParam,
valueSetsSearchCriteria,
codeSystemEntries)
recursivelyRequestAllValueSetsExpansions(
new ArrayList<>(), umlsUser.getApiKey(), vsParam, valueSetsSearchCriteria)
.stream())
.collect(Collectors.toList());
.toList();
}

public List<QdmValueSet> getValueSetsExpansionsForQdm(
ValueSetsSearchCriteria valueSetsSearchCriteria, UmlsUser umlsUser) {
List<CodeSystemEntry> codeSystemEntries = mappingService.getCodeSystemEntries();
List<ValueSet> fhirValueSets = getValueSetsExpansion(valueSetsSearchCriteria, umlsUser);

return fhirValueSets.stream()
.map(
fhirValueSet -> {
List<QdmValueSet.Concept> concepts =
getValueSetConcepts(fhirValueSet, codeSystemEntries, "QDM");
return QdmValueSet.builder()
.oid(fhirValueSet.getIdPart())
.displayName(fhirValueSet.getName())
.version(fhirValueSet.getVersion())
.concepts(concepts)
.build();
})
.toList();
}

/**
Expand Down
52 changes: 0 additions & 52 deletions src/main/java/gov/cms/madie/terminology/service/VsacService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import gov.cms.madie.models.mapping.CodeSystemEntry;
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;
import gov.cms.madie.terminology.mapper.VsacToFhirValueSetMapper;
Expand All @@ -27,8 +26,6 @@
import java.util.Optional;
import java.util.stream.Collectors;

import static generated.vsac.nlm.nih.gov.RetrieveMultipleValueSetsResponse.DescribedValueSet;

@Service
@Slf4j
@RequiredArgsConstructor
Expand Down Expand Up @@ -68,13 +65,6 @@ public ValueSet convertToFHIRValueSet(RetrieveMultipleValueSetsResponse vsacValu
return vsacToFhirValueSetMapper.convertToFHIRValueSet(vsacValueSetResponse);
}

public List<ValueSet> convertToFHIRValueSets(
List<RetrieveMultipleValueSetsResponse> vsacValueSets) {
return vsacValueSets.stream()
.map(vsacToFhirValueSetMapper::convertToFHIRValueSet)
.collect(Collectors.toList());
}

public List<RetrieveMultipleValueSetsResponse> getValueSets(
ValueSetsSearchCriteria searchCriteria, UmlsUser umlsUser) {
List<ValueSetsSearchCriteria.ValueSetParams> valueSetParams =
Expand All @@ -92,12 +82,6 @@ public List<RetrieveMultipleValueSetsResponse> getValueSets(
.collect(Collectors.toList());
}

public List<QdmValueSet> getValueSetsInQdmFormat(
ValueSetsSearchCriteria searchCriteria, UmlsUser umlsUser) {
List<RetrieveMultipleValueSetsResponse> vsacValueSets = getValueSets(searchCriteria, umlsUser);
return convertToQdmValueSets(vsacValueSets);
}

/**
* @param cqlCodes List of objects generated by Antlr from user entered CQL string.
* @param umlsUser umls information from db
Expand Down Expand Up @@ -319,42 +303,6 @@ public UmlsUser saveUmlsUser(String harpId, String apiKey) {
return umlsUserRepository.save(umlsUser);
}

private List<QdmValueSet> convertToQdmValueSets(
List<RetrieveMultipleValueSetsResponse> valueSetsResponses) {
return valueSetsResponses.stream()
.map(
response -> {
var describedValueSet = response.getDescribedValueSet();
List<QdmValueSet.Concept> concepts = getValueSetConcepts(describedValueSet);
return QdmValueSet.builder()
.oid(describedValueSet.getID())
.displayName(describedValueSet.getDisplayName())
.version(describedValueSet.getVersion())
.concepts(concepts)
.build();
})
.toList();
}

private List<QdmValueSet.Concept> getValueSetConcepts(DescribedValueSet valueSet) {
var conceptList = valueSet.getConceptList();
if (conceptList == null) {
log.info("Empty value set:{}", valueSet.getID());
return List.of();
}
return conceptList.getConcepts().stream()
.map(
concept ->
QdmValueSet.Concept.builder()
.code(concept.getCode())
.codeSystemName(concept.getCodeSystemName())
.codeSystemVersion(concept.getCodeSystemVersion())
.codeSystemOid(concept.getCodeSystem())
.displayName(concept.getDisplayName())
.build())
.toList();
}

public Optional<UmlsUser> findByHarpId(String harpId) {
return umlsUserRepository.findByHarpId(harpId);
}
Expand Down
Loading

0 comments on commit 9846752

Please sign in to comment.