/** * <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.course.nodes.gta.rule; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.olat.basesecurity.GroupRoles; import org.olat.core.CoreSpringFactory; import org.olat.core.id.Identity; import org.olat.core.util.StringHelper; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.GTACourseNode; import org.olat.course.nodes.gta.GTAManager; import org.olat.course.nodes.gta.GTARelativeToDates; import org.olat.course.nodes.gta.GTAType; import org.olat.course.nodes.gta.Task; import org.olat.course.nodes.gta.TaskList; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; import org.olat.modules.ModuleConfiguration; import org.olat.modules.reminder.IdentitiesProviderRuleSPI; import org.olat.modules.reminder.ReminderRule; import org.olat.modules.reminder.model.ReminderRuleImpl; import org.olat.modules.reminder.rule.LaunchUnit; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRelationType; import org.olat.repository.RepositoryService; import org.olat.repository.manager.RepositoryEntryRelationDAO; import org.olat.repository.model.RepositoryEntryLifecycle; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 10.04.2015<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public abstract class AbstractDueDateTaskRuleSPI implements IdentitiesProviderRuleSPI { @Autowired private GTAManager gtaManager; @Autowired private BusinessGroupService businessGroupService; @Autowired private RepositoryEntryRelationDAO repositoryEntryRelationDao; @Override public List<Identity> evaluate(RepositoryEntry entry, ReminderRule rule) { List<Identity> identities = null; if(rule instanceof ReminderRuleImpl) { ReminderRuleImpl r = (ReminderRuleImpl)rule; String nodeIdent = r.getLeftOperand(); ICourse course = CourseFactory.loadCourse(entry); CourseNode courseNode = course.getRunStructure().getNode(nodeIdent); if(courseNode instanceof GTACourseNode) { identities = evaluateRule(entry, (GTACourseNode)courseNode, r); } } return identities == null ? Collections.<Identity>emptyList() : identities; } protected List<Identity> evaluateRule(RepositoryEntry entry, GTACourseNode gtaNode, ReminderRuleImpl rule) { List<Identity> identities = null; if(gtaNode.getModuleConfiguration().getBooleanSafe(GTACourseNode.GTASK_RELATIVE_DATES)) { identities = evaluateRelativeDateRule(entry, gtaNode, rule); } else { Date dueDate = getDueDate(gtaNode); if(dueDate != null && isNear(dueDate, now(), rule)) { identities = getPeopleToRemind(entry, gtaNode); } } return identities == null ? Collections.<Identity>emptyList() : identities; } protected abstract List<Identity> evaluateRelativeDateRule(RepositoryEntry entry, GTACourseNode gtaNode, ReminderRuleImpl r); protected abstract Date getDueDate(GTACourseNode gtaNode); protected List<Identity> getPeopleToRemind(RepositoryEntry entry, GTACourseNode gtaNode) { ModuleConfiguration config = gtaNode.getModuleConfiguration(); TaskList taskList = gtaManager.getTaskList(entry, gtaNode); if(GTAType.group.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE))) { return getGroupsToRemind(taskList, gtaNode); } else { return getIndividualsToRemind(taskList, entry, gtaNode); } } protected List<Identity> getPeopleToRemindRelativeTo(RepositoryEntry entry, GTACourseNode gtaNode, int numOfDays, String relativeTo, ReminderRuleImpl rule) { List<Identity> identities = null; if(numOfDays >= 0 && StringHelper.containsNonWhitespace(relativeTo)) { GTARelativeToDates rel = GTARelativeToDates.valueOf(relativeTo); switch(rel) { case courseStart: { RepositoryEntryLifecycle lifecycle = entry.getLifecycle(); if(lifecycle != null && lifecycle.getValidFrom() != null) { Date referenceDate = getDate(lifecycle.getValidFrom(), numOfDays); if(isNear(referenceDate, now(), rule)) { identities = getPeopleToRemind(entry, gtaNode); } } break; } case courseLaunch: { UserCourseInformationsManager userCourseInformationsManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class); Map<Long,Date> initialLaunchDates = userCourseInformationsManager.getInitialLaunchDates(entry.getOlatResource().getResourceableId()); Map<Long,Date> dueDates = getDueDates(initialLaunchDates, numOfDays); identities = getPeopleToRemindRelativeTo(entry, gtaNode, dueDates, rule); break; } case enrollment: { RepositoryService repositoryService = CoreSpringFactory.getImpl(RepositoryService.class); Map<Long,Date> enrollmentDates = repositoryService.getEnrollmentDates(entry); Map<Long,Date> dueDates = getDueDates(enrollmentDates, numOfDays); identities = getPeopleToRemindRelativeTo(entry, gtaNode, dueDates, rule); break; } default: { // } } } return identities; } protected List<Identity> getPeopleToRemindRelativeTo(RepositoryEntry entry, GTACourseNode gtaNode, Map<Long,Date> dates, ReminderRuleImpl rule) { Date now = now(); Set<Long> potentialidentityKeys = new HashSet<>(); for(Map.Entry<Long, Date> entryDate:dates.entrySet()) { Long identityKey = entryDate.getKey(); Date date = entryDate.getValue(); if(isNear(date, now, rule)) { potentialidentityKeys.add(identityKey); } } List<Identity> identities = null; if(potentialidentityKeys.size() > 0) { List<Identity> allIdentities = getPeopleToRemind(entry, gtaNode); identities = new ArrayList<>(); for(Identity identity:allIdentities) { if(potentialidentityKeys.contains(identity.getKey())) { identities.add(identity); } } } return identities; } private Map<Long,Date> getDueDates(Map<Long,Date> referenceDates, int numOfDays) { Map<Long, Date> dueDates = new HashMap<>(); if(referenceDates != null && referenceDates.size() > 0) { Calendar cal = Calendar.getInstance(); for(Map.Entry<Long, Date> referenceEntry:referenceDates.entrySet()) { Long identityKey = referenceEntry.getKey(); cal.setTime(referenceEntry.getValue()); cal.add(Calendar.DATE, numOfDays); dueDates.put(identityKey, cal.getTime()); } } return dueDates; } private Date getDate(Date referenceDate, int numOfDays) { Date date = null; if(referenceDate != null) { Calendar cal = Calendar.getInstance(); cal.setTime(referenceDate); cal.add(Calendar.DATE, numOfDays); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); date = cal.getTime(); } return date; } protected List<Identity> getGroupsToRemind(TaskList taskList, GTACourseNode gtaNode) { List<Task> tasks = gtaManager.getTasks(taskList, gtaNode); Set<BusinessGroup> doneTasks = new HashSet<BusinessGroup>(); for(Task task:tasks) { if(task.getBusinessGroup() != null) { doneTasks.add(task.getBusinessGroup()); } } List<BusinessGroup> groups = gtaManager.getBusinessGroups(gtaNode); for(Iterator<BusinessGroup> groupIt=groups.iterator(); groupIt.hasNext(); ) { if(doneTasks.contains(groupIt.next())) { groupIt.remove(); } } return businessGroupService.getMembers(groups, GroupRoles.participant.name()); } protected List<Identity> getIndividualsToRemind(TaskList taskList, RepositoryEntry entry, GTACourseNode gtaNode) { List<Task> tasks = gtaManager.getTasks(taskList, gtaNode); Set<Identity> doneTasks = new HashSet<Identity>(); for(Task task:tasks) { if(task.getIdentity() != null) { doneTasks.add(task.getIdentity()); } } List<Identity> identities = repositoryEntryRelationDao.getMembers(entry, RepositoryEntryRelationType.both, GroupRoles.participant.name()); for(Iterator<Identity> identityIt=identities.iterator(); identityIt.hasNext(); ) { if(doneTasks.contains(identityIt.next())) { identityIt.remove(); } } return identities; } protected Date now() { Calendar cal = Calendar.getInstance(); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return cal.getTime(); } protected boolean isNear(Date dueDate, Date now, ReminderRuleImpl r) { int value = Integer.parseInt(r.getRightOperand()); String unit = r.getRightUnit(); return near(dueDate, now, value, LaunchUnit.valueOf(unit)); } private boolean near(Date date, Date now, int distance, LaunchUnit unit) { double between = -1; switch(unit) { case day: between = daysBetween(now, date); break; case week: between = weeksBetween(now, date); break; case month: between = monthsBetween(now, date); break; case year: between = yearsBetween(now, date); break; } // 0.1 to let +- 2 hours to match return between <= distance || between - 0.1 <= distance || between < 0.0; } private double daysBetween(Date d1, Date d2) { return ((d2.getTime() - d1.getTime()) / (1000d * 60d * 60d * 24d)); } private double weeksBetween(Date d1, Date d2) { return ((d2.getTime() - d1.getTime()) / (1000d * 60d * 60d * 24d * 7d)); } private double monthsBetween(Date d1, Date d2) { return ((d2.getTime() - d1.getTime()) / (1000d * 60d * 60d * 24d * 30d)); } private double yearsBetween(Date d1, Date d2) { return ((d2.getTime() - d1.getTime()) / (1000d * 60d * 60d * 24d * 365d)); } }