diff --git a/src/main/java/cms/gov/madie/measure/config/UpdateQICoreTestCaseStratificationsChangeUnit.java b/src/main/java/cms/gov/madie/measure/config/UpdateQICoreTestCaseStratificationsChangeUnit.java new file mode 100644 index 00000000..4c6a4e6b --- /dev/null +++ b/src/main/java/cms/gov/madie/measure/config/UpdateQICoreTestCaseStratificationsChangeUnit.java @@ -0,0 +1,86 @@ +package cms.gov.madie.measure.config; + +import cms.gov.madie.measure.repositories.MeasureRepository; +import gov.cms.madie.models.common.ModelType; +import gov.cms.madie.models.measure.Measure; +import gov.cms.madie.models.measure.Stratification; +import gov.cms.madie.models.measure.TestCasePopulationValue; +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Slf4j +@ChangeUnit(id = "qi_core_testcase_strat_update", order = "1", author = "madie_dev") +public class UpdateQICoreTestCaseStratificationsChangeUnit { + @Setter @Getter private List tempMeasures; + + @Execution + public void updateQICoreTestCaseStratifications(MeasureRepository measureRepository) + throws Exception { + log.debug("Entering updateQICoreTestCaseStratifications()"); + List measureList = measureRepository.findAllByModel(ModelType.QI_CORE.getValue()); + log.info("found QI Core measures: {}", measureList.size()); + + measureList.forEach( + measure -> { + if (!measure.isActive() + || CollectionUtils.isEmpty(measure.getGroups()) + || CollectionUtils.isEmpty(measure.getTestCases())) { + return; + } + for (var testCase : measure.getTestCases()) { + if (CollectionUtils.isEmpty(testCase.getGroupPopulations())) { + continue; + } + for (var groupPopulation : testCase.getGroupPopulations()) { + if (CollectionUtils.isEmpty(groupPopulation.getStratificationValues())) { + continue; + } + for (var stratificationValue : groupPopulation.getStratificationValues()) { + Optional matchingGroupStratification = + measure.getGroups().stream() + .flatMap(group -> group.getStratifications().stream()) + .filter(strat -> strat.getId().equals(stratificationValue.getId())) + .findFirst(); + + if (matchingGroupStratification.isPresent() + && stratificationValue.getPopulationValues() != null) { + List filteredStratifiedPopulationValues = + stratificationValue.getPopulationValues().stream() + .filter( + populationValue -> + matchingGroupStratification.get().getAssociations().stream() + .anyMatch( + validAssociation -> + validAssociation + .getDisplay() + .equalsIgnoreCase( + populationValue.getName().getDisplay()))) + .collect(Collectors.toList()); + stratificationValue.setPopulationValues(filteredStratifiedPopulationValues); + } + } + } + } + + measure.setTestCases(measure.getTestCases()); + measureRepository.save(measure); + }); + } + + @RollbackExecution + public void rollbackExecution(MeasureRepository measureRepository) throws Exception { + log.debug("Entering rollbackExecution() "); + if (CollectionUtils.isNotEmpty(tempMeasures)) { + tempMeasures.forEach(measureRepository::save); + } + } +} diff --git a/src/main/java/cms/gov/madie/measure/services/GroupService.java b/src/main/java/cms/gov/madie/measure/services/GroupService.java index 3e6d7818..f8572ad0 100644 --- a/src/main/java/cms/gov/madie/measure/services/GroupService.java +++ b/src/main/java/cms/gov/madie/measure/services/GroupService.java @@ -94,7 +94,7 @@ public Group createOrUpdateGroup(Group group, String measureId, String username) measure.getGroups().add(group); } } - updateGroupForTestCases(group, measure.getTestCases()); + updateGroupForTestCases(group, measure.getTestCases(), measure.getModel()); Measure errors = measureUtil.validateAllMeasureDependencies(measure); measure.setErrors(errors.getErrors()); @@ -117,7 +117,7 @@ public Group createOrUpdateGroup(Group group, String measureId, String username) * @param group Group being changed * @param testCases TestCases to iterate over and update */ - public void updateGroupForTestCases(Group group, List testCases) { + public void updateGroupForTestCases(Group group, List testCases, String measureModel) { if (group != null && !CollectionUtils.isEmpty(testCases)) { testCases.forEach( testCase -> { @@ -137,7 +137,7 @@ public void updateGroupForTestCases(Group group, List testCases) { if (StringUtils.equals(testCaseGroupPopulation.getScoring(), group.getScoring()) && StringUtils.equals( testCaseGroupPopulation.getPopulationBasis(), group.getPopulationBasis())) { - updateTestCaseGroupWithMeasureGroup(testCaseGroupPopulation, group); + updateTestCaseGroupWithMeasureGroup(testCaseGroupPopulation, group, measureModel); } else { removeGroupFromTestCase(group.getId(), testCase); } @@ -188,7 +188,7 @@ public Measure deleteMeasureGroup(String measureId, String groupId, String usern } public void updateTestCaseGroupWithMeasureGroup( - TestCaseGroupPopulation testCaseGroup, Group measureGroup) { + TestCaseGroupPopulation testCaseGroup, Group measureGroup, String measureModel) { // update test case populations based on measure group population List populations = measureGroup.getPopulations().stream() @@ -235,10 +235,12 @@ public void updateTestCaseGroupWithMeasureGroup( String.format( "Strata-%d %s", count.getAndIncrement(), measureStrata.getAssociation().getDisplay()); - return updateTestCaseStratification(measureStrata, testCaseGroup, strataName); + return updateTestCaseStratification( + measureStrata, testCaseGroup, strataName, measureModel); } else { String strataName = String.format("Strata-%d", count.getAndIncrement()); - return updateTestCaseStratification(measureStrata, testCaseGroup, strataName); + return updateTestCaseStratification( + measureStrata, testCaseGroup, strataName, measureModel); } }) .filter(Objects::nonNull) @@ -282,7 +284,10 @@ private TestCasePopulationValue updateTestCasePopulation( } protected TestCaseStratificationValue updateTestCaseStratification( - Stratification stratification, TestCaseGroupPopulation testCaseGroup, String strataName) { + Stratification stratification, + TestCaseGroupPopulation testCaseGroup, + String strataName, + String measureModel) { // if no cql definition(optional), no need to consider stratification if (StringUtils.isEmpty(stratification.getCqlDefinition())) { return null; @@ -307,13 +312,16 @@ protected TestCaseStratificationValue updateTestCaseStratification( .build(); } testCaseStrata.setName(strataName); - handlePopulationChange(testCaseStrata, testCaseGroup); + handlePopulationChange(testCaseStrata, testCaseGroup, measureModel, stratification); return testCaseStrata; } private void handlePopulationChange( - TestCaseStratificationValue testCaseStrata, TestCaseGroupPopulation testCaseGroup) { + TestCaseStratificationValue testCaseStrata, + TestCaseGroupPopulation testCaseGroup, + String measureModel, + Stratification stratification) { List testCasePopulationValues = testCaseStrata.getPopulationValues(); List testCasePopulationValuesFromGroup = testCaseGroup.getPopulationValues(); @@ -330,9 +338,18 @@ private void handlePopulationChange( // delete any that is not in testCasePopulationValuesFromGroup List tempTestCasePopulationValues = new ArrayList<>(); for (TestCasePopulationValue tempTestCasePopulationValue : testCasePopulationValues) { - if (findExistsTestCasePopulationValue( - tempTestCasePopulationValue.getId(), testCasePopulationValuesFromGroup)) { - tempTestCasePopulationValues.add(tempTestCasePopulationValue); + if (StringUtils.equals(measureModel, ModelType.QDM_5_6.getValue())) { + if (findExistsTestCasePopulationValue( + tempTestCasePopulationValue.getId(), testCasePopulationValuesFromGroup)) { + tempTestCasePopulationValues.add(tempTestCasePopulationValue); + } + } else { + // remove association from testcases when stratifications are changed in measure + // groups + if (handleStratificationAssociationChange( + tempTestCasePopulationValue, stratification)) { + tempTestCasePopulationValues.add(tempTestCasePopulationValue); + } } } testCaseStrata.setPopulationValues(tempTestCasePopulationValues); @@ -344,6 +361,27 @@ private void handlePopulationChange( } } + private boolean handleStratificationAssociationChange( + TestCasePopulationValue tempTestCasePopulationValue, Stratification stratification) { + if (stratification.getAssociations() != null) { + PopulationType populationAsocciation = + stratification.getAssociations().stream() + .filter( + association -> { + return association + .getDisplay() + .equalsIgnoreCase(tempTestCasePopulationValue.getName().getDisplay()); + }) + .findFirst() + .orElse(null); + + if (populationAsocciation != null) { + return true; + } + } + return false; + } + private boolean findExistsTestCasePopulationValue( String id, List testCasePopulationValues) { return testCasePopulationValues.stream() diff --git a/src/test/java/cms/gov/madie/measure/config/UpdateQICoreTestCaseStratificationsChangeUnitTest.java b/src/test/java/cms/gov/madie/measure/config/UpdateQICoreTestCaseStratificationsChangeUnitTest.java new file mode 100644 index 00000000..e8e9f109 --- /dev/null +++ b/src/test/java/cms/gov/madie/measure/config/UpdateQICoreTestCaseStratificationsChangeUnitTest.java @@ -0,0 +1,115 @@ +package cms.gov.madie.measure.config; + +import cms.gov.madie.measure.repositories.MeasureRepository; +import gov.cms.madie.models.common.ModelType; +import gov.cms.madie.models.measure.*; +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 java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class UpdateQICoreTestCaseStratificationsChangeUnitTest { + + @Mock private MeasureRepository measureRepository; + + @Captor private ArgumentCaptor measureArgumentCaptor; + + @InjectMocks + UpdateQICoreTestCaseStratificationsChangeUnit updateQICoreTestCaseStratificationsChangeUnit; + + @Test + void testStratsAreUpdatedInTestCasesAndRollbackHoldsMeasures() throws Exception { + List testCaseStratificationPopulationValues = + List.of( + TestCasePopulationValue.builder() + .name(PopulationType.INITIAL_POPULATION) + .expected(true) + .build(), + TestCasePopulationValue.builder() + .name(PopulationType.DENOMINATOR) + .expected(true) + .build(), + TestCasePopulationValue.builder() + .name(PopulationType.NUMERATOR) + .expected(true) + .build()); + + final TestCaseGroupPopulation tcGroupPop = + TestCaseGroupPopulation.builder() + .groupId("group-1") + .scoring(MeasureScoring.CONTINUOUS_VARIABLE.toString()) + .stratificationValues( + List.of( + TestCaseStratificationValue.builder() + .id("strat-1") + .populationValues(testCaseStratificationPopulationValues) + .build())) + .build(); + + List qiCoreMeasures = + List.of( + Measure.builder().measureName("M0_DEAD").active(false).build(), + Measure.builder().measureName("M0_NOGROUPS").active(true).groups(List.of()).build(), + Measure.builder() + .measureName("M1") + .active(true) + .model(ModelType.QI_CORE.getValue()) + .groups( + List.of( + Group.builder() + .id("group-1") + .stratifications( + List.of( + Stratification.builder() + .id("strat-1") + .associations( + List.of( + PopulationType.INITIAL_POPULATION, + PopulationType.NUMERATOR)) + .build())) + .build())) + .testCases( + List.of(TestCase.builder().groupPopulations(List.of(tcGroupPop)).build())) + .build()); + + when(measureRepository.findAllByModel(anyString())).thenReturn(qiCoreMeasures); + updateQICoreTestCaseStratificationsChangeUnit.updateQICoreTestCaseStratifications( + measureRepository); + verify(measureRepository, times(1)).save(measureArgumentCaptor.capture()); + List allUpdatedMeasures = measureArgumentCaptor.getAllValues(); + assertThat(allUpdatedMeasures, is(notNullValue())); + assertThat(allUpdatedMeasures.size(), is(equalTo(1))); + } + + @Test + void testRollbackDoesNothingForNullTempMeasures() throws Exception { + updateQICoreTestCaseStratificationsChangeUnit.setTempMeasures(null); + updateQICoreTestCaseStratificationsChangeUnit.rollbackExecution(measureRepository); + verifyNoInteractions(measureRepository); + } + + @Test + void testRollbackDoesNothingForEmptyTempMeasures() throws Exception { + updateQICoreTestCaseStratificationsChangeUnit.setTempMeasures(List.of()); + updateQICoreTestCaseStratificationsChangeUnit.rollbackExecution(measureRepository); + verifyNoInteractions(measureRepository); + } + + @Test + void testRollbackHandlesRollback() throws Exception { + updateQICoreTestCaseStratificationsChangeUnit.setTempMeasures( + List.of(Measure.builder().build(), Measure.builder().build())); + updateQICoreTestCaseStratificationsChangeUnit.rollbackExecution(measureRepository); + verify(measureRepository, times(2)).save(measureArgumentCaptor.capture()); + } +} diff --git a/src/test/java/cms/gov/madie/measure/services/GroupServiceTest.java b/src/test/java/cms/gov/madie/measure/services/GroupServiceTest.java index 26e993e4..6af7394b 100644 --- a/src/test/java/cms/gov/madie/measure/services/GroupServiceTest.java +++ b/src/test/java/cms/gov/madie/measure/services/GroupServiceTest.java @@ -660,7 +660,7 @@ public void testResetPopulationValuesForGroupHandlesNullGroup() { final List testCases = List.of(TestCase.builder().groupPopulations(List.of(testPopulation)).build()); - groupService.updateGroupForTestCases(null, testCases); + groupService.updateGroupForTestCases(null, testCases, ModelType.QI_CORE.getValue()); // unchanged test case populations assertEquals(testCases.get(0).getGroupPopulations(), List.of(testPopulation)); } @@ -682,7 +682,7 @@ public void testUpdateTestCaseGroupGroupScoringChanged() { .build()); // before updates assertEquals(1, testCases.get(0).getGroupPopulations().size()); - groupService.updateGroupForTestCases(group, testCases); + groupService.updateGroupForTestCases(group, testCases, ModelType.QI_CORE.getValue()); // group should be removed from test case as measure group scoring was changed assertEquals(0, testCases.get(0).getGroupPopulations().size()); } @@ -722,7 +722,7 @@ public void testUpdateTestCaseGroupGroupScoringNotChanged() { .build()); // before updates assertEquals(1, testCases.get(0).getGroupPopulations().size()); - groupService.updateGroupForTestCases(group, testCases); + groupService.updateGroupForTestCases(group, testCases, ModelType.QI_CORE.getValue()); // group should not be removed from test case as measure group scoring was not // changed assertEquals(1, testCases.get(0).getGroupPopulations().size()); @@ -745,7 +745,7 @@ public void testUpdateTestCaseGroupGroupPopulationBasisChanged() { .build()); // before updates assertEquals(1, testCases.get(0).getGroupPopulations().size()); - groupService.updateGroupForTestCases(group, testCases); + groupService.updateGroupForTestCases(group, testCases, ModelType.QI_CORE.getValue()); // group should be removed from test case as populationBasis for measure group // was changed assertEquals(0, testCases.get(0).getGroupPopulations().size()); @@ -757,7 +757,7 @@ public void testResetPopulationValuesForGroupHandlesEmptyTestCaseList() { final List testCases = List.of(); // before updates assertEquals(0, testCases.size()); - groupService.updateGroupForTestCases(group, testCases); + groupService.updateGroupForTestCases(group, testCases, ModelType.QI_CORE.getValue()); // after updates, no change to the test case list assertEquals(0, testCases.size()); } @@ -768,7 +768,7 @@ public void testResetPopulationValuesForGroupHandlesNullGroupPopulationList() { final List testCases = List.of(TestCase.builder().groupPopulations(null).build()); // before updates assertNull(testCases.get(0).getGroupPopulations()); - groupService.updateGroupForTestCases(group, testCases); + groupService.updateGroupForTestCases(group, testCases, ModelType.QI_CORE.getValue()); // after updates, no population added to the group assertNull(testCases.get(0).getGroupPopulations()); } @@ -795,7 +795,7 @@ public void testResetPopulationValuesForNewGroup() { .build()); // before updates assertEquals(1, testCases.get(0).getGroupPopulations().size()); - groupService.updateGroupForTestCases(group, testCases); + groupService.updateGroupForTestCases(group, testCases, ModelType.QI_CORE.getValue()); // after update call, do nothing for new group assertEquals(1, testCases.get(0).getGroupPopulations().size()); } @@ -956,7 +956,8 @@ public void updateTestCaseGroupToAddMeasurePopulationsAndStratification() { // before updates assertEquals(5, testCaseGroup.getPopulationValues().size()); assertNull(testCaseGroup.getStratificationValues()); - groupService.updateTestCaseGroupWithMeasureGroup(testCaseGroup, measureGroup); + groupService.updateTestCaseGroupWithMeasureGroup( + testCaseGroup, measureGroup, ModelType.QI_CORE.getValue()); // after updates assertEquals(6, testCaseGroup.getPopulationValues().size()); assertEquals(2, testCaseGroup.getStratificationValues().size()); @@ -985,7 +986,8 @@ public void updateTestCaseGroupToAddMeasurePopulationsHandlesNoAssociation() { // before updates assertEquals(5, testCaseGroup.getPopulationValues().size()); assertNull(testCaseGroup.getStratificationValues()); - groupService.updateTestCaseGroupWithMeasureGroup(testCaseGroup, measureGroup); + groupService.updateTestCaseGroupWithMeasureGroup( + testCaseGroup, measureGroup, ModelType.QI_CORE.getValue()); // after updates assertEquals(6, testCaseGroup.getPopulationValues().size()); assertEquals(2, testCaseGroup.getStratificationValues().size()); @@ -1010,7 +1012,8 @@ public void updateTestCaseGroupToRemoveMeasurePopulationsAndStratification() { // unselect 1 population and 1 stratification from measure group measureGroup.getPopulations().get(2).setDefinition(null); measureGroup.getStratifications().get(1).setCqlDefinition(null); - groupService.updateTestCaseGroupWithMeasureGroup(testCaseGroup, measureGroup); + groupService.updateTestCaseGroupWithMeasureGroup( + testCaseGroup, measureGroup, ModelType.QI_CORE.getValue()); // after updates assertEquals(5, testCaseGroup.getPopulationValues().size()); assertEquals(1, testCaseGroup.getStratificationValues().size()); @@ -1087,7 +1090,8 @@ public void updateTestCaseGroupToModifyObservations() { // unselect 1 population and 1 stratification from measure group measureGroup.getPopulations().get(2).setDefinition(null); measureGroup.getStratifications().get(0).setCqlDefinition(null); - groupService.updateTestCaseGroupWithMeasureGroup(testCaseGroup, measureGroup); + groupService.updateTestCaseGroupWithMeasureGroup( + testCaseGroup, measureGroup, ModelType.QI_CORE.getValue()); // after updates assertEquals(5, testCaseGroup.getPopulationValues().size()); assertEquals(0, testCaseGroup.getStratificationValues().size()); @@ -1123,7 +1127,8 @@ public void updateTestCaseGroupToRemoveAllStratificationAndAnObservations() { // update measure group to remove stratification and observations measureGroup.setStratifications(null); measureGroup.setMeasureObservations(null); - groupService.updateTestCaseGroupWithMeasureGroup(testCaseGroup, measureGroup); + groupService.updateTestCaseGroupWithMeasureGroup( + testCaseGroup, measureGroup, ModelType.QI_CORE.getValue()); // after updates assertEquals(4, testCaseGroup.getPopulationValues().size()); assertEquals(0, testCaseGroup.getStratificationValues().size()); @@ -1138,7 +1143,8 @@ public void updateTestCaseGroupToRemoveDenominatorObservation() { assertEquals(5, testCaseGroup.getPopulationValues().size()); // remove denominator observation from measure group measureGroup.getMeasureObservations().remove(0); - groupService.updateTestCaseGroupWithMeasureGroup(testCaseGroup, measureGroup); + groupService.updateTestCaseGroupWithMeasureGroup( + testCaseGroup, measureGroup, ModelType.QI_CORE.getValue()); // after updates assertEquals(5, testCaseGroup.getPopulationValues().size()); } @@ -1152,7 +1158,8 @@ public void updateTestCaseGroupToRemoveNumeratorObservation() { assertEquals(5, testCaseGroup.getPopulationValues().size()); // remove denominator observation from measure group measureGroup.getMeasureObservations().remove(1); - groupService.updateTestCaseGroupWithMeasureGroup(testCaseGroup, measureGroup); + groupService.updateTestCaseGroupWithMeasureGroup( + testCaseGroup, measureGroup, ModelType.QI_CORE.getValue()); // after updates assertEquals(5, testCaseGroup.getPopulationValues().size()); } @@ -1163,7 +1170,8 @@ public void testCVObservationsAreUnchangedOnPopulationChange() { TestCaseGroupPopulation testCaseGroup = buildTestCaseCVGroup(); // before updates assertEquals(4, testCaseGroup.getPopulationValues().size()); - groupService.updateTestCaseGroupWithMeasureGroup(testCaseGroup, group2); + groupService.updateTestCaseGroupWithMeasureGroup( + testCaseGroup, group2, ModelType.QI_CORE.getValue()); // after updates assertEquals(4, testCaseGroup.getPopulationValues().size()); } @@ -1386,7 +1394,7 @@ public void testUpdateTestCaseStratificationForAddedGroupPopulation() { TestCaseStratificationValue testCaseStratificationValue = groupService.updateTestCaseStratification( - stratification, testCaseGroupPopulation, "Strata-1"); + stratification, testCaseGroupPopulation, "Strata-1", ModelType.QDM_5_6.getValue()); assertTrue(testCaseStratificationValue != null); assertEquals(testCaseStratificationValue.getPopulationValues().size(), 3); } @@ -1428,7 +1436,7 @@ public void testUpdateTestCaseStratificationForDeletedGroupPopulation() { TestCaseStratificationValue testCaseStratificationValue = groupService.updateTestCaseStratification( - stratification, testCaseGroupPopulation, "Strata-1"); + stratification, testCaseGroupPopulation, "Strata-1", ModelType.QDM_5_6.getValue()); assertTrue(testCaseStratificationValue != null); assertEquals(testCaseStratificationValue.getPopulationValues().size(), 3); } @@ -1467,7 +1475,7 @@ public void testUpdateTestCaseStratificationForChangedGroupPopulation() { TestCaseStratificationValue testCaseStratificationValue = groupService.updateTestCaseStratification( - stratification, testCaseGroupPopulation, "Strata-1"); + stratification, testCaseGroupPopulation, "Strata-1", ModelType.QDM_5_6.getValue()); assertTrue(testCaseStratificationValue != null); assertEquals(testCaseStratificationValue.getPopulationValues().size(), 2); assertNotEquals( @@ -1482,7 +1490,10 @@ public void testUpdateTestCaseStratificationForChangedGroupPopulation() { public void testUpdateTestCaseStratificationForNonTestCasePopulationValuesFromGroup() { TestCaseStratificationValue testCaseStratificationValue = groupService.updateTestCaseStratification( - stratification, new TestCaseGroupPopulation(), "Strata-1"); + stratification, + new TestCaseGroupPopulation(), + "Strata-1", + ModelType.QI_CORE.getValue()); assertNull(testCaseStratificationValue.getPopulationValues()); }