/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.modules.reminder.manager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.olat.basesecurity.GroupRoles;
import org.olat.core.id.Identity;
import org.olat.modules.reminder.FilterRuleSPI;
import org.olat.modules.reminder.IdentitiesProviderRuleSPI;
import org.olat.modules.reminder.Reminder;
import org.olat.modules.reminder.ReminderModule;
import org.olat.modules.reminder.ReminderRule;
import org.olat.modules.reminder.ReminderService;
import org.olat.modules.reminder.RepositoryEntryRuleSPI;
import org.olat.modules.reminder.RuleSPI;
import org.olat.modules.reminder.model.ReminderRules;
import org.olat.modules.reminder.rule.BusinessGroupRoleRuleSPI;
import org.olat.modules.reminder.rule.DateRuleSPI;
import org.olat.modules.reminder.rule.RepositoryEntryRoleRuleSPI;
import org.olat.modules.reminder.rule.UserPropertyRuleSPI;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryRelationType;
import org.olat.repository.manager.RepositoryEntryRelationDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
*
* Initial date: 08.04.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
@Service
public class ReminderRuleEngine {
public static final String DATE_RULE_TYPE = DateRuleSPI.class.getSimpleName();
public static final String USER_PROP_RULE_TYPE = UserPropertyRuleSPI.class.getSimpleName();
public static final String REPO_ROLE_RULE_TYPE = RepositoryEntryRoleRuleSPI.class.getSimpleName();
public static final String BUSINESSGROUP_ROLE_RULE_TYPE = BusinessGroupRoleRuleSPI.class.getSimpleName();
@Autowired
private DateRuleSPI dateRuleSpi;
@Autowired
private UserPropertyRuleSPI userPropertyRuleSpi;
@Autowired
private ReminderDAO reminderDao;
@Autowired
private ReminderModule reminderModule;
@Autowired
private ReminderService reminderManager;
@Autowired
private RepositoryEntryRelationDAO repositoryEntryRelationDao;
public List<Identity> evaluate(Reminder reminder, boolean resend) {
String config = reminder.getConfiguration();
ReminderRules rules = reminderManager.toRules(config);
if(rules == null || rules.getRules() == null || rules.getRules().isEmpty()) {
return Collections.emptyList();
}
List<ReminderRule> ruleList = new ArrayList<>(rules.getRules());
//1. Date rules doesn't need database queries
boolean allOk = evaluateDateRule(ruleList);
if(allOk) {
allOk = evaluateRepositoryEntryRule(reminder.getEntry(), ruleList);
}
List<Identity> identities;
if(allOk) {
identities = getIdentities(reminder.getEntry(), reminder, ruleList, resend);
if(identities.size() > 0 && ruleList.size() > 0) {
filterByRules(reminder.getEntry(), identities, ruleList);
}
} else {
identities = Collections.emptyList();
}
return identities;
}
/**
*
* @param reminder
*/
protected boolean evaluateDateRule(List<ReminderRule> ruleList) {
boolean allOk = true;
for(Iterator<ReminderRule> ruleIt=ruleList.iterator(); ruleIt.hasNext(); ) {
ReminderRule rule = ruleIt.next();
if(DATE_RULE_TYPE.equals(rule.getType())) {
allOk &= dateRuleSpi.evaluate(rule);
ruleIt.remove();
}
}
return allOk;
}
/**
*
* @param reminder
*/
protected boolean evaluateRepositoryEntryRule(RepositoryEntry entry, List<ReminderRule> ruleList) {
boolean allOk = true;
for(Iterator<ReminderRule> ruleIt=ruleList.iterator(); ruleIt.hasNext(); ) {
ReminderRule rule = ruleIt.next();
RuleSPI ruleSpi = reminderModule.getRuleSPIByType(rule.getType());
if(ruleSpi instanceof RepositoryEntryRuleSPI) {
allOk &= ((RepositoryEntryRuleSPI)ruleSpi).evaluate(entry, rule);
ruleIt.remove();
}
}
return allOk;
}
protected List<Identity> getIdentities(RepositoryEntry entry, Reminder reminder, List<ReminderRule> ruleList, boolean resend) {
List<ReminderRule> identitiesProviderRules = new ArrayList<>();
for(Iterator<ReminderRule> ruleIt=ruleList.iterator(); ruleIt.hasNext(); ) {
ReminderRule rule = ruleIt.next();
RuleSPI ruleSpi = reminderModule.getRuleSPIByType(rule.getType());
if(ruleSpi instanceof IdentitiesProviderRuleSPI) {
identitiesProviderRules.add(rule);
ruleIt.remove();
}
}
List<Identity> identities;
if(identitiesProviderRules.isEmpty()) {
//all members of repository entry
List<Identity> duplicatedIdentities = repositoryEntryRelationDao.getMembers(entry, RepositoryEntryRelationType.both,
GroupRoles.owner.name(), GroupRoles.coach.name(), GroupRoles.participant.name());
identities = new ArrayList<>(new HashSet<>(duplicatedIdentities));
} else {
identities = null;
for(ReminderRule rule:identitiesProviderRules) {
RuleSPI ruleSpi = reminderModule.getRuleSPIByType(rule.getType());
IdentitiesProviderRuleSPI identitiesProviderRuleSpi = (IdentitiesProviderRuleSPI)ruleSpi;
List<Identity> members = identitiesProviderRuleSpi.evaluate(entry, rule);
if(identities == null) {
identities = members;
} else {
identities.retainAll(members);
}
}
}
//filter by user property
filterIdentitiesByProperty(identities, ruleList);
// deduplicated the list
identities = new ArrayList<>(new HashSet<>(identities));
if(!resend) {
List<Long> alreadySendKeys = reminderDao.getReminderRecipientKeys(reminder);
Set<Long> alreadySendKeySet = new HashSet<>(alreadySendKeys);
for(Iterator<Identity> identityIt=identities.iterator(); identityIt.hasNext(); ) {
if(alreadySendKeySet.contains(identityIt.next().getKey())) {
identityIt.remove();
}
}
}
return identities;
}
/**
* Remove identities of the list which not match the user properties rules if any.
*
* @param identities
* @param ruleList
*/
protected void filterIdentitiesByProperty(List<Identity> identities, List<ReminderRule> ruleList) {
List<ReminderRule> userPropRules = new ArrayList<>(3);
for(Iterator<ReminderRule> ruleIt=ruleList.iterator(); ruleIt.hasNext(); ) {
ReminderRule rule = ruleIt.next();
if(USER_PROP_RULE_TYPE.equals(rule.getType())) {
userPropRules.add(rule);
ruleIt.remove();
}
}
if(userPropRules.size() > 0) {
int numOfRules = userPropRules.size();
ReminderRule[] ruleArr = userPropRules.toArray(new ReminderRule[numOfRules]);
for(Iterator<Identity> identityIt=identities.iterator(); identityIt.hasNext(); ) {
Identity identity = identityIt.next();
boolean accept = true;
for(int i=numOfRules; i-->0; ) {
accept &= userPropertyRuleSpi.accept(ruleArr[i], identity);
}
if(!accept) {
identityIt.remove();
}
}
}
}
protected void filterByRules(RepositoryEntry entry, List<Identity> identities, List<ReminderRule> ruleList) {
List<ReminderRule> filterRules = new ArrayList<>(3);
for(Iterator<ReminderRule> ruleIt=ruleList.iterator(); ruleIt.hasNext(); ) {
ReminderRule rule = ruleIt.next();
RuleSPI ruleSpi = reminderModule.getRuleSPIByType(rule.getType());
if(ruleSpi instanceof FilterRuleSPI) {
filterRules.add(rule);
ruleIt.remove();
}
}
for(ReminderRule rule:filterRules) {
RuleSPI ruleSpi = reminderModule.getRuleSPIByType(rule.getType());
if(ruleSpi instanceof FilterRuleSPI) {
FilterRuleSPI filter = (FilterRuleSPI)ruleSpi;
filter.filter(entry, identities, rule);
}
}
}
}