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

chore: Load all events for rule-engine context [DHIS2-18089](2.40) #18829

Merged
merged 2 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -440,6 +440,7 @@ public RuleEvent toMappedRuleEvent(ProgramStageInstance psi) {
psi.getProgramStage().getUid(),
RuleEvent.Status.valueOf(psi.getStatus().toString()),
ObjectUtils.defaultIfNull(psi.getExecutionDate(), psi.getDueDate()),
psi.getCreated(),
psi.getDueDate(),
orgUnit,
orgUnitCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ public ProgramStageInstance fromForRuleEngine(TrackerPreheat preheat, Event even
ProgramStageInstance psi = from(preheat, event, null);
// merge data values from DB
psi.getEventDataValues().addAll(getProgramStageInstanceDataValues(preheat, event));
ProgramStageInstance savedEvent = preheat.getEvent(event.getUid());
if (savedEvent != null) {
psi.setCreated(savedEvent.getCreated());
}
return psi;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.ListUtils;
import org.hisp.dhis.dxf2.events.event.EventQueryParams;
import org.hisp.dhis.dxf2.events.event.EventService;
import org.hisp.dhis.program.Program;
import org.hisp.dhis.program.ProgramInstance;
import org.hisp.dhis.program.ProgramStageInstance;
import org.hisp.dhis.program.ProgramStageInstanceService;
import org.hisp.dhis.programrule.engine.ProgramRuleEngine;
import org.hisp.dhis.rules.models.RuleEffects;
import org.hisp.dhis.trackedentity.TrackedEntityInstance;
Expand Down Expand Up @@ -77,6 +80,10 @@ class DefaultProgramRuleService implements ProgramRuleService {

private final RuleActionEventMapper ruleActionEventMapper;

private final EventService eventService;

private final ProgramStageInstanceService programStageInstanceService;

@Override
@Transactional(readOnly = true)
public void calculateRuleEffects(TrackerBundle bundle, TrackerPreheat preheat) {
Expand Down Expand Up @@ -200,10 +207,13 @@ private List<TrackedEntityAttributeValue> getAttributes(
// if they are present in both places
private Set<ProgramStageInstance> getEventsFromEnrollment(
String enrollmentUid, TrackerBundle bundle, TrackerPreheat preheat) {
EventQueryParams eventQueryParams = new EventQueryParams();
eventQueryParams.setProgramInstances(Set.of(enrollmentUid));
List<org.hisp.dhis.dxf2.events.event.Event> events =
eventService.getEvents(eventQueryParams).getEvents();

Stream<ProgramStageInstance> programStageInstances =
preheat.getEvents().values().stream()
.filter(e -> e.getProgramInstance().getUid().equals(enrollmentUid))
.filter(e -> bundle.findEventByUid(e.getUid()).isEmpty());
events.stream().map(e -> programStageInstanceService.getProgramStageInstance(e.getUid()));

Stream<ProgramStageInstance> bundleEvents =
bundle.getEvents().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ protected void setUpTest() throws IOException {
user.getGroups().add(userGroup);
manager.update(user);

injectSecurityContext(user);

templateForEnrollment =
createProgramNotification(
"enrollment",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,28 @@
import static org.hisp.dhis.tracker.validation.ValidationCode.E1307;
import static org.hisp.dhis.tracker.validation.ValidationCode.E1308;
import static org.hisp.dhis.tracker.validation.ValidationCode.E1310;
import static org.hisp.dhis.utils.Assertions.assertContainsOnly;
import static org.hisp.dhis.utils.Assertions.assertIsEmpty;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import org.hisp.dhis.common.CodeGenerator;
import org.hisp.dhis.dataelement.DataElement;
import org.hisp.dhis.dxf2.metadata.objectbundle.ObjectBundle;
import org.hisp.dhis.eventdatavalue.EventDataValue;
import org.hisp.dhis.preheat.PreheatIdentifier;
import org.hisp.dhis.program.Program;
import org.hisp.dhis.program.ProgramStage;
import org.hisp.dhis.program.ProgramStageInstance;
import org.hisp.dhis.programrule.ProgramRule;
import org.hisp.dhis.programrule.ProgramRuleAction;
import org.hisp.dhis.programrule.ProgramRuleActionService;
import org.hisp.dhis.programrule.ProgramRuleActionType;
import org.hisp.dhis.programrule.ProgramRuleService;
import org.hisp.dhis.programrule.ProgramRuleVariable;
import org.hisp.dhis.programrule.ProgramRuleVariableService;
import org.hisp.dhis.programrule.ProgramRuleVariableSourceType;
import org.hisp.dhis.setting.SettingKey;
import org.hisp.dhis.setting.SystemSettingManager;
import org.hisp.dhis.trackedentity.TrackedEntityAttribute;
Expand All @@ -55,7 +63,11 @@
import org.hisp.dhis.tracker.TrackerImportStrategy;
import org.hisp.dhis.tracker.TrackerTest;
import org.hisp.dhis.tracker.report.ImportReport;
import org.hisp.dhis.util.DateUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.beans.factory.annotation.Autowired;

class ProgramRuleAssignActionTest extends TrackerTest {
Expand All @@ -73,15 +85,16 @@ class ProgramRuleAssignActionTest extends TrackerTest {

private DataElement dataElement1;

private DataElement dataElement2;

private TrackedEntityAttribute attribute1;

@Override
public void initTest() throws IOException {
ObjectBundle bundle = setUpMetadata("tracker/simple_metadata.json");
program = bundle.getPreheat().get(PreheatIdentifier.UID, Program.class, "BFcipDERJnf");
dataElement1 = bundle.getPreheat().get(PreheatIdentifier.UID, DataElement.class, "DATAEL00001");
DataElement dataElement2 =
bundle.getPreheat().get(PreheatIdentifier.UID, DataElement.class, "DATAEL00002");
dataElement2 = bundle.getPreheat().get(PreheatIdentifier.UID, DataElement.class, "DATAEL00002");
attribute1 =
bundle.getPreheat().get(PreheatIdentifier.UID, TrackedEntityAttribute.class, "dIVt4l5vIOa");
TrackedEntityAttribute attribute2 =
Expand All @@ -93,17 +106,22 @@ public void initTest() throws IOException {
programRuleVariableService.addProgramRuleVariable(programRuleVariable);
programRuleVariableService.addProgramRuleVariable(programRuleVariableAttribute);

ProgramRuleVariable programRuleVariablePreviousEvent =
createProgramRuleVariableWithDataElement('C', program, dataElement1);
programRuleVariablePreviousEvent.setSourceType(
ProgramRuleVariableSourceType.DATAELEMENT_PREVIOUS_EVENT);
programRuleVariableService.addProgramRuleVariable(programRuleVariablePreviousEvent);

injectAdminUser();

assignProgramRule();
trackerImportService.importTracker(
fromJson("tracker/programrule/tei_enrollment_completed_event.json"));
}

@Test
void shouldNotImportWithWarningWhenAttributeWithSameValueIsAssignedByAssignRule()
throws IOException {

assignProgramRule();
TrackerImportParams params =
fromJson("tracker/programrule/te_enrollment_update_attribute_same_value.json");
params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE);
Expand All @@ -113,9 +131,96 @@ void shouldNotImportWithWarningWhenAttributeWithSameValueIsAssignedByAssignRule(
assertHasOnlyWarnings(importReport, E1310);
}

@ParameterizedTest
@CsvSource({"2024-02-10,THIRD", "2024-01-28,SECOND", "2024-01-19,FIRST"})
void shouldImportEventAndCorrectlyAssignPreviousEventDataValue(
String eventOccurredDate, String previousEventDataValue) throws IOException {
TrackerImportParams params =
fromJson("tracker/programrule/three_events_with_different_dates.json");
params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE);

trackerImportService.importTracker(params);

assignPreviousEventProgramRule();

dbmsManager.clearSession();
dbmsManager.flushSession();

params = fromJson("tracker/programrule/event_with_data_value.json");

params.getEvents().get(0).setOccurredAt(DateUtils.instantFromDateAsString(eventOccurredDate));

ImportReport importReport = trackerImportService.importTracker(params);
assertHasOnlyWarnings(importReport, E1308);

ProgramStageInstance event = manager.get(ProgramStageInstance.class, "D9PbzJY8bZZ");

List<String> eventDataValues =
event.getEventDataValues().stream()
.filter(dv -> dv.getDataElement().equals("DATAEL00002"))
.map(EventDataValue::getValue)
.collect(Collectors.toList());
assertContainsOnly(List.of(previousEventDataValue), eventDataValues);
}

@Test
void
shouldImportEventAndCorrectlyAssignPreviousEventDataValueConsideringCreateAtWhenOccurredAtIsSame()
throws IOException {
String firstEventUid = CodeGenerator.generateUid();
String secondEventUid = CodeGenerator.generateUid();
String thirdEventUid = CodeGenerator.generateUid();
String fourthEventUid = CodeGenerator.generateUid();

// Events are imported separately to have different createdAt
TrackerImportParams firstEvent = getEvent(firstEventUid, "2024-01-11", "FIRST");
trackerImportService.importTracker(firstEvent);

TrackerImportParams fourthEvent = getEvent(fourthEventUid, "2024-01-26", "FOURTH");
trackerImportService.importTracker(fourthEvent);

TrackerImportParams secondEvent = getEvent(secondEventUid, "2024-01-25", "SECOND");
trackerImportService.importTracker(secondEvent);

TrackerImportParams thirdEvent = getEvent(thirdEventUid, "2024-01-25", "THIRD");
trackerImportService.importTracker(thirdEvent);

assignPreviousEventProgramRule();

dbmsManager.clearSession();
dbmsManager.flushSession();

TrackerImportParams trackerImportParams =
TrackerImportParams.builder()
.events(
List.of(
firstEvent.getEvents().get(0),
secondEvent.getEvents().get(0),
thirdEvent.getEvents().get(0),
fourthEvent.getEvents().get(0)))
.build();

ImportReport importReport = trackerImportService.importTracker(trackerImportParams);

List<String> firstEventDataValues = getValueForAssignedDataElement(firstEventUid);
List<String> secondEventDataValues = getValueForAssignedDataElement(secondEventUid);
List<String> thirdEventDataValues = getValueForAssignedDataElement(thirdEventUid);
List<String> fourthEventDataValues = getValueForAssignedDataElement(fourthEventUid);

Assertions.assertAll(
() -> assertHasOnlyWarnings(importReport, E1308, E1308, E1308, E1308),
() -> assertIsEmpty(firstEventDataValues),
() -> assertContainsOnly(List.of("FIRST"), secondEventDataValues),
() -> assertContainsOnly(List.of("SECOND"), thirdEventDataValues),
() -> assertContainsOnly(List.of("THIRD"), fourthEventDataValues));
}

@Test
void shouldImportWithWarningWhenDataElementWithSameValueIsAssignedByAssignRule()
throws IOException {
assignProgramRule();
dbmsManager.clearSession();
dbmsManager.flushSession();
TrackerImportParams params =
fromJson("tracker/programrule/event_update_datavalue_same_value.json");
params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE);
Expand All @@ -127,6 +232,7 @@ void shouldImportWithWarningWhenDataElementWithSameValueIsAssignedByAssignRule()

@Test
void shouldNotImportWhenDataElementWithDifferentValueIsAssignedByAssignRule() throws IOException {
assignProgramRule();
TrackerImportParams params =
fromJson("tracker/programrule/event_update_datavalue_different_value.json");
params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE);
Expand All @@ -140,6 +246,7 @@ void shouldNotImportWhenDataElementWithDifferentValueIsAssignedByAssignRule() th
void
shouldImportWithWarningWhenDataElementWithDifferentValueIsAssignedByAssignRuleAndOverwriteKeyIsTrue()
throws IOException {
assignProgramRule();
systemSettingManager.saveSystemSetting(SettingKey.RULE_ENGINE_ASSIGN_OVERWRITE, true);
TrackerImportParams params =
fromJson("tracker/programrule/event_update_datavalue_different_value.json");
Expand All @@ -154,7 +261,10 @@ void shouldNotImportWhenDataElementWithDifferentValueIsAssignedByAssignRule() th
void
shouldImportWithWarningWhenDataElementWithDifferentAndEmptyValueIsAssignedByAssignRuleAndOverwriteKeyIsTrue()
throws IOException {
assignProgramRule();
systemSettingManager.saveSystemSetting(SettingKey.RULE_ENGINE_ASSIGN_OVERWRITE, true);
dbmsManager.clearSession();
dbmsManager.flushSession();
TrackerImportParams params =
fromJson("tracker/programrule/event_update_datavalue_empty_value.json");
params.setImportStrategy(TrackerImportStrategy.CREATE_AND_UPDATE);
Expand All @@ -164,6 +274,27 @@ void shouldNotImportWhenDataElementWithDifferentValueIsAssignedByAssignRule() th
assertHasOnlyWarnings(importReport, E1308);
}

private TrackerImportParams getEvent(String eventUid, String occurredDate, String value)
throws IOException {
TrackerImportParams trackerImportParams =
fromJson("tracker/programrule/event_without_date.json");
trackerImportParams
.getEvents()
.get(0)
.setOccurredAt(DateUtils.instantFromDateAsString(occurredDate));
trackerImportParams.getEvents().get(0).setEvent(eventUid);
trackerImportParams.getEvents().get(0).getDataValues().iterator().next().setValue(value);

return trackerImportParams;
}

private List<String> getValueForAssignedDataElement(String eventUid) {
return manager.get(ProgramStageInstance.class, eventUid).getEventDataValues().stream()
.filter(dv -> dv.getDataElement().equals("DATAEL00002"))
.map(EventDataValue::getValue)
.collect(Collectors.toList());
}

private void assignProgramRule() {
ProgramRule programRule = createProgramRule('F', program, null, "true");
programRuleService.addProgramRule(programRule);
Expand All @@ -178,6 +309,16 @@ private void assignProgramRule() {
programRuleService.updateProgramRule(programRule);
}

private void assignPreviousEventProgramRule() {
ProgramRule programRule = createProgramRule('G', program, null, "true");
programRuleService.addProgramRule(programRule);
ProgramRuleAction programRuleAction =
createProgramRuleAction(programRule, ASSIGN, dataElement2, "#{ProgramRuleVariableC}");
programRuleActionService.addProgramRuleAction(programRuleAction);
programRule.getProgramRuleActions().add(programRuleAction);
programRuleService.updateProgramRule(programRule);
}

private ProgramRule createProgramRule(
char uniqueCharacter, Program program, ProgramStage programStage, String condition) {
ProgramRule programRule = createProgramRule(uniqueCharacter, program);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"enrollments": [],
"events": [
{
"event": "D9PbzJY8bJO",
"event": "D9PbzJY8bJX",
"status": "COMPLETED",
"program": {
"idScheme": "UID",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"trackedEntities": [],
"enrollments": [],
"events": [
{
"event": "D9PbzJY8bZZ",
"status": "COMPLETED",
"program": {
"idScheme": "UID",
"identifier": "BFcipDERJnf"
},
"programStage": {
"idScheme": "UID",
"identifier": "NpsdDv6kKSO"
},
"enrollment": "TvctPPhpD8u",
"orgUnit": {
"idScheme": "UID",
"identifier": "h4w96yEMlzO"
},
"relationships": [],
"scheduledAt": "2019-01-25T12:10:38.100",
"storedBy": "admin",
"deleted": false,
"attributeOptionCombo": {
"idScheme": "UID"
},
"attributeCategoryOptions": [],

"dataValues": [
{
"storedBy": "admin",
"providedElsewhere": false,
"dataElement": {
"idScheme": "UID",
"identifier": "DATAEL00001"
},
"value": "LAST"
}
],
"notes": []
}
],
"relationships": []
}
Loading
Loading