Skip to content

Commit

Permalink
Add endpoint for relevant qdm elements (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
chubert-sb authored Oct 9, 2023
1 parent 2a0515d commit 99d99bb
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import lombok.extern.slf4j.Slf4j;
import org.cqframework.cql.tools.formatter.CqlFormatterVisitor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -71,4 +72,12 @@ public ResponseEntity<List<String>> getLibraryElms(
return ResponseEntity.status(HttpStatus.OK)
.body(cqlConversionService.getElmForCql(cql, accessToken));
}

@PutMapping("/qdm/relevant-elements")
public ResponseEntity<List<SourceDataCriteria>> getRelevantElements(
@RequestBody Measure measure, @RequestHeader("Authorization") String accessToken) {
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(dataCriteriaService.getRelevantElements(measure, accessToken));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gov.cms.mat.cql_elm_translation.service;

import gov.cms.madie.models.measure.Measure;
import gov.cms.mat.cql_elm_translation.cql_translator.MadieLibrarySourceProvider;
import gov.cms.mat.cql_elm_translation.data.DataCriteria;
import gov.cms.mat.cql_elm_translation.data.RequestData;
Expand All @@ -21,11 +22,7 @@

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
Expand All @@ -36,6 +33,10 @@ public class DataCriteriaService {
private final CqlConversionService cqlConversionService;

public DataCriteria parseDataCriteriaFromCql(String cql, String accessToken) {
return parseCql(cql, accessToken).getDataCriteria();
}

private CQLTools parseCql(String cql, String accessToken) {
// Run Translator to compile libraries
MadieLibrarySourceProvider librarySourceProvider = new MadieLibrarySourceProvider();
cqlConversionService.setUpLibrarySourceProvider(cql, accessToken);
Expand All @@ -53,7 +54,55 @@ public DataCriteria parseDataCriteriaFromCql(String cql, String accessToken) {
} catch (IOException e) {
throw new RuntimeException(e);
}
return cqlTools.getDataCriteria();
return cqlTools;
}

public List<SourceDataCriteria> getRelevantElements(Measure measure, String accessToken) {
if (StringUtils.isBlank(measure.getCql())) {
log.info("Data criteria not found as cql is blank");
return Collections.emptyList();
}

List<SourceDataCriteria> sourceDataCriteria =
getSourceDataCriteria(measure.getCql(), accessToken);

CQLTools tools = parseCql(measure.getCql(), accessToken);

Set<String> usedDefinitions = new HashSet<>();
measure
.getGroups()
.forEach(
group -> {
group
.getPopulations()
.forEach(
population -> {
if (!population.getDefinition().isEmpty()) {
usedDefinitions.add(population.getDefinition());
}
});
});

Set<String> values = new HashSet<>();
usedDefinitions.forEach(
def -> {
if (!MapUtils.isEmpty(tools.getExpressionNameToValuesetDataTypeMap())) {
tools
.getExpressionNameToValuesetDataTypeMap()
.get(def)
.forEach((expression, valueSet) -> values.add(expression));
}
if (!MapUtils.isEmpty(tools.getExpressionNameToCodeDataTypeMap())) {
tools
.getExpressionNameToCodeDataTypeMap()
.get(def)
.forEach((expression, valueSet) -> values.add(expression));
}
});

return sourceDataCriteria.stream()
.filter(sourceDataCriteria1 -> values.contains(sourceDataCriteria1.getTitle()))
.toList();
}

public List<SourceDataCriteria> getSourceDataCriteria(String cql, String accessToken) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package gov.cms.mat.cql_elm_translation.controllers;

import gov.cms.madie.models.measure.Group;
import gov.cms.madie.models.measure.Measure;
import gov.cms.madie.models.measure.Population;
import gov.cms.mat.cql_elm_translation.ResourceFileUtil;
import gov.cms.mat.cql_elm_translation.dto.SourceDataCriteria;
import gov.cms.mat.cql_elm_translation.exceptions.CqlFormatException;
Expand All @@ -15,6 +17,7 @@
import org.springframework.http.HttpStatus;

import java.security.Principal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

Expand Down Expand Up @@ -110,6 +113,21 @@ void testGenerateHumanReadable() {
assertEquals(result.getStatusCode(), HttpStatus.OK);
}

@Test
void testGetRelevantElements() {
String cql = getData("/qdm_data_criteria_retrieval_test.cql");
Measure measure = Measure.builder().cql(cql).build();
String token = "john";
var sdc = SourceDataCriteria.builder().oid("1.2.3").description("EP: Test").title("EP").build();
when(dataCriteriaService.getRelevantElements(any(Measure.class), anyString()))
.thenReturn(List.of(sdc));
var result = cqlToolsController.getRelevantElements(measure, token);
SourceDataCriteria sourceDataCriteria = result.getBody().get(0);
assertThat(sourceDataCriteria.getOid(), is(equalTo(sdc.getOid())));
assertThat(sourceDataCriteria.getDescription(), is(equalTo(sdc.getDescription())));
assertThat(sourceDataCriteria.getTitle(), is(equalTo(sdc.getTitle())));
}

private boolean inputMatchesOutput(String input, String output) {
return input
.replaceAll("[\\s\\u0000\\u00a0]", "")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package gov.cms.mat.cql_elm_translation.service;

import gov.cms.madie.models.measure.Group;
import gov.cms.madie.models.measure.Measure;
import gov.cms.madie.models.measure.Population;
import gov.cms.mat.cql_elm_translation.ResourceFileUtil;
import gov.cms.mat.cql_elm_translation.cql_translator.TranslationResource;
import gov.cms.mat.cql_elm_translation.data.RequestData;
Expand All @@ -14,6 +17,7 @@
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Collections;
import java.util.List;

import static org.hamcrest.CoreMatchers.equalTo;
Expand Down Expand Up @@ -120,4 +124,80 @@ void testGetSourceDataCriteriaWhenNoCqlProvided() {
dataCriteriaService.getSourceDataCriteria("", token);
assertThat(sourceDataCriteria.size(), is(equalTo(0)));
}

@Test
void testGetRelevantElements() {
Population population = Population.builder().definition("Qualifying Encounters").build();
Group group = Group.builder().populations(Collections.singletonList(population)).build();
Measure measure = Measure.builder().cql(cql).groups(Collections.singletonList(group)).build();
CqlTranslator translator =
TranslationResource.getInstance(false)
.buildTranslator(requestData.getCqlDataInputStream(), requestData.createMap());

Mockito.doNothing()
.when(cqlConversionService)
.setUpLibrarySourceProvider(anyString(), anyString());
when(cqlConversionService.processCqlData(any(RequestData.class))).thenReturn(translator);

List<SourceDataCriteria> relevantElements =
dataCriteriaService.getRelevantElements(measure, token);

// source data criteria for value set
assertThat(relevantElements.size(), is(equalTo(2)));
assertThat(relevantElements.get(0).getOid(), is(equalTo("2.16.840.1.113883.3.666.5.307")));
assertThat(relevantElements.get(0).getTitle(), is(equalTo("Encounter Inpatient")));
assertThat(relevantElements.get(0).getType(), is(equalTo("EncounterPerformed")));
assertThat(
relevantElements.get(0).getDescription(),
is(equalTo("Encounter, Performed: Encounter Inpatient")));
assertFalse(relevantElements.get(0).isDrc());

// source data criteria for direct reference code
assertTrue(relevantElements.get(1).isDrc());
assertThat(relevantElements.get(1).getTitle(), is(equalTo("Clinical Examples")));
assertThat(relevantElements.get(1).getType(), is(equalTo("EncounterPerformed")));
assertThat(
relevantElements.get(1).getDescription(),
is(equalTo("Encounter, Performed: Clinical Examples")));
}

@Test
void testGetRelevantElementsWhenNoSourceCriteriaFound() {
String cql =
"library DataCriteriaRetrivalTest version '0.0.000'\n"
+ "using QDM version '5.6'\n"
+ "valueset \"Encounter Inpatient\": 'urn:oid:2.16.840.1.113883.3.666.5.307'\n"
+ "parameter \"Measurement Period\" Interval<DateTime>\n"
+ "context Patient\n"
+ "define \"Qualifying Encounters\":\n true";

Population population = Population.builder().definition("Qualifying Encounters").build();
Group group = Group.builder().populations(Collections.singletonList(population)).build();
Measure measure = Measure.builder().cql(cql).groups(Collections.singletonList(group)).build();

RequestData data = requestData.toBuilder().cqlData(cql).build();
CqlTranslator translator =
TranslationResource.getInstance(false)
.buildTranslator(data.getCqlDataInputStream(), data.createMap());

Mockito.doNothing()
.when(cqlConversionService)
.setUpLibrarySourceProvider(anyString(), anyString());
when(cqlConversionService.processCqlData(any(RequestData.class))).thenReturn(translator);

List<SourceDataCriteria> sourceDataCriteria =
dataCriteriaService.getRelevantElements(measure, token);

assertThat(sourceDataCriteria.size(), is(equalTo(0)));
}

@Test
void testGetRelevantElementsWhenNoCqlProvided() {
Population population = Population.builder().definition("Qualifying Encounters").build();
Group group = Group.builder().populations(Collections.singletonList(population)).build();
Measure measure = Measure.builder().cql("").groups(Collections.singletonList(group)).build();
List<SourceDataCriteria> sourceDataCriteria =
dataCriteriaService.getRelevantElements(measure, token);
assertThat(sourceDataCriteria.size(), is(equalTo(0)));
}
}

0 comments on commit 99d99bb

Please sign in to comment.