Skip to content

Commit

Permalink
chore: Load all events for rule-engine context [DHIS2-18089](2.39) (#…
Browse files Browse the repository at this point in the history
…18830)

* chore: Load all events for rule-engine context [DHIS2-18089]

* chore: Use createdAt as secundary sorting param [DHIS2-18089]
  • Loading branch information
enricocolasante authored Oct 17, 2024
1 parent 0999dbd commit 8156e65
Show file tree
Hide file tree
Showing 11 changed files with 398 additions and 20 deletions.
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 @@ -168,6 +168,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 @@ -38,9 +38,12 @@
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
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 @@ -79,6 +82,10 @@ public class DefaultTrackerProgramRuleService implements TrackerProgramRuleServi
private final TrackerConverterService<Attribute, TrackedEntityAttributeValue>
attributeValueTrackerConverterService;

private final EventService eventService;

private final ProgramStageInstanceService programStageInstanceService;

/**
* This method is calling rule engine for every enrollment and all the linked events, for all
* events linked to an enrollment not present in the payload and for all the program events.
Expand Down Expand Up @@ -196,22 +203,19 @@ private ProgramInstance getEnrollment(TrackerBundle bundle, String enrollmentUid
}

private Set<ProgramStageInstance> getEventsFromEnrollment(
String enrollment, TrackerBundle bundle) {
List<String> bundleEventUids =
bundle.getEvents().stream().map(Event::getUid).collect(Collectors.toList());
String enrollmentUid, TrackerBundle bundle) {
EventQueryParams eventQueryParams = new EventQueryParams();
eventQueryParams.setProgramInstances(Set.of(enrollmentUid));
List<org.hisp.dhis.dxf2.events.event.Event> events =
eventService.getEvents(eventQueryParams).getEvents();

// Get all programStageInstances from preheat that are linked to
// enrollment
// and are not present in the payload
Stream<ProgramStageInstance> programStageInstances =
bundle.getPreheat().getEvents().values().stream()
.filter(e -> e.getProgramInstance().getUid().equals(enrollment))
.filter(e -> !bundleEventUids.contains(e.getUid()));
events.stream().map(e -> programStageInstanceService.getProgramStageInstance(e.getUid()));

// All events in the payload that are linked to enrollment
Stream<ProgramStageInstance> bundleEvents =
bundle.getEvents().stream()
.filter(e -> e.getEnrollment().equals(enrollment))
.filter(e -> e.getEnrollment().equals(enrollmentUid))
.map(
event ->
eventTrackerConverterService.fromForRuleEngine(bundle.getPreheat(), event));
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,21 +33,29 @@
import static org.hisp.dhis.tracker.report.TrackerErrorCode.E1307;
import static org.hisp.dhis.tracker.report.TrackerErrorCode.E1308;
import static org.hisp.dhis.tracker.report.TrackerErrorCode.E1310;
import static org.hisp.dhis.utils.Assertions.assertContainsOnly;
import static org.hisp.dhis.utils.Assertions.assertIsEmpty;
import static org.junit.jupiter.api.Assertions.assertEquals;

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 @@ -56,7 +64,11 @@
import org.hisp.dhis.tracker.TrackerImportStrategy;
import org.hisp.dhis.tracker.TrackerTest;
import org.hisp.dhis.tracker.report.TrackerImportReport;
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 @@ -74,15 +86,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 @@ -94,17 +107,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 @@ -115,9 +133,99 @@ void shouldNotImportWithWarningWhenAttributeWithSameValueIsAssignedByAssignRule(
assertEquals(E1310, importReport.getValidationReport().getWarnings().get(0).getWarningCode());
}

@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));

TrackerImportReport importReport = trackerImportService.importTracker(params);
assertEquals(E1308, importReport.getValidationReport().getWarnings().get(0).getWarningCode());

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)))
.importStrategy(TrackerImportStrategy.CREATE_AND_UPDATE)
.build();

TrackerImportReport 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(
() ->
assertEquals(
E1308, importReport.getValidationReport().getWarnings().get(0).getWarningCode()),
() -> 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 @@ -130,6 +238,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 @@ -143,6 +252,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 @@ -158,7 +268,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 @@ -169,6 +282,27 @@ void shouldNotImportWhenDataElementWithDifferentValueIsAssignedByAssignRule() th
assertEquals(E1308, importReport.getValidationReport().getWarnings().get(0).getWarningCode());
}

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 @@ -183,6 +317,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 @@ -195,7 +195,7 @@ void testImportEnrollmentSuccessWithWarningRaised() throws IOException {
trackerImportService.importTracker(fromJson("tracker/single_enrollment.json"));

assertNoErrors(trackerImportEnrollmentReport);
assertEquals(2, trackerImportEnrollmentReport.getValidationReport().getWarnings().size());
assertEquals(3, trackerImportEnrollmentReport.getValidationReport().getWarnings().size());
}

@Test
Expand All @@ -207,12 +207,12 @@ void testImportEventInProgramStageSuccessWithWarningRaised() throws IOException
assertNoErrors(trackerImportReport);
List<TrackerWarningReport> warningReports =
trackerImportReport.getValidationReport().getWarnings();
assertEquals(6, warningReports.size());
assertEquals(7, warningReports.size());
assertEquals(
4,
warningReports.stream().filter(w -> w.getTrackerType().equals(TrackerType.EVENT)).count());
assertEquals(
2,
3,
warningReports.stream()
.filter(w -> w.getTrackerType().equals(TrackerType.ENROLLMENT))
.count());
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
Loading

0 comments on commit 8156e65

Please sign in to comment.