From 3ad048ffb6b94302cbea7ca96579c24d412dbce0 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Wed, 27 May 2015 09:42:49 +0200
Subject: [PATCH] OO-1494: add rule based on valid from date of the lifecycle
 of a course

---
 .../reminder/RepositoryEntryRuleSPI.java      |  35 +++++
 .../reminder/manager/ReminderRuleEngine.java  |  23 +++
 ...ryEntryLifecycleAfterValidFromRuleSPI.java | 127 ++++++++++++++++
 ...ntryLifecycleAfterValidFromRuleEditor.java | 136 ++++++++++++++++++
 .../reminder/ui/_content/repo_valid.html      |   6 +
 .../ui/_i18n/LocalStrings_de.properties       |   4 +-
 .../ui/_i18n/LocalStrings_en.properties       |   2 +
 .../manager/ReminderRuleEngineTest.java       |  93 ++++++++++++
 8 files changed, 425 insertions(+), 1 deletion(-)
 create mode 100644 src/main/java/org/olat/modules/reminder/RepositoryEntryRuleSPI.java
 create mode 100644 src/main/java/org/olat/modules/reminder/rule/RepositoryEntryLifecycleAfterValidFromRuleSPI.java
 create mode 100644 src/main/java/org/olat/modules/reminder/ui/RepositoryEntryLifecycleAfterValidFromRuleEditor.java
 create mode 100644 src/main/java/org/olat/modules/reminder/ui/_content/repo_valid.html

diff --git a/src/main/java/org/olat/modules/reminder/RepositoryEntryRuleSPI.java b/src/main/java/org/olat/modules/reminder/RepositoryEntryRuleSPI.java
new file mode 100644
index 00000000000..11ebc453224
--- /dev/null
+++ b/src/main/java/org/olat/modules/reminder/RepositoryEntryRuleSPI.java
@@ -0,0 +1,35 @@
+/**
+ * <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;
+
+import org.olat.repository.RepositoryEntry;
+
+/**
+ * 
+ * 
+ * Initial date: 26.05.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface RepositoryEntryRuleSPI extends RuleSPI {
+	
+	public boolean evaluate(RepositoryEntry entry, ReminderRule rule);
+
+}
diff --git a/src/main/java/org/olat/modules/reminder/manager/ReminderRuleEngine.java b/src/main/java/org/olat/modules/reminder/manager/ReminderRuleEngine.java
index b2d7592fb40..ac3f48448cf 100644
--- a/src/main/java/org/olat/modules/reminder/manager/ReminderRuleEngine.java
+++ b/src/main/java/org/olat/modules/reminder/manager/ReminderRuleEngine.java
@@ -34,6 +34,7 @@ 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;
@@ -89,6 +90,9 @@ public class ReminderRuleEngine {
 		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) {
@@ -122,6 +126,25 @@ public class ReminderRuleEngine {
 		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<>();
 
diff --git a/src/main/java/org/olat/modules/reminder/rule/RepositoryEntryLifecycleAfterValidFromRuleSPI.java b/src/main/java/org/olat/modules/reminder/rule/RepositoryEntryLifecycleAfterValidFromRuleSPI.java
new file mode 100644
index 00000000000..41b19ed8629
--- /dev/null
+++ b/src/main/java/org/olat/modules/reminder/rule/RepositoryEntryLifecycleAfterValidFromRuleSPI.java
@@ -0,0 +1,127 @@
+/**
+ * <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.rule;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.olat.course.export.CourseEnvironmentMapper;
+import org.olat.modules.reminder.ReminderRule;
+import org.olat.modules.reminder.RepositoryEntryRuleSPI;
+import org.olat.modules.reminder.RuleEditorFragment;
+import org.olat.modules.reminder.model.ReminderRuleImpl;
+import org.olat.modules.reminder.ui.RepositoryEntryLifecycleAfterValidFromRuleEditor;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.model.RepositoryEntryLifecycle;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * 
+ * Initial date: 26.05.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+public class RepositoryEntryLifecycleAfterValidFromRuleSPI implements RepositoryEntryRuleSPI  {
+
+
+	@Override
+	public String getLabelI18nKey() {
+		return "rule.lifecycle.validfrom";
+	}
+	
+	@Override
+	public String getCategory() {
+		return "course";
+	}
+	
+	@Override
+	public ReminderRule clone(ReminderRule rule, CourseEnvironmentMapper envMapper) {
+		return rule.clone();
+	}
+	
+	@Override
+	public RuleEditorFragment getEditorFragment(ReminderRule rule, RepositoryEntry entry) {
+		return new RepositoryEntryLifecycleAfterValidFromRuleEditor(rule, this.getClass().getSimpleName());
+	}
+
+	@Override
+	public boolean evaluate(RepositoryEntry entry, ReminderRule rule) {
+		boolean allOk = true;
+		if(rule instanceof ReminderRuleImpl) {
+			RepositoryEntryLifecycle lifecycle = entry.getLifecycle();
+			if(lifecycle != null && lifecycle.getValidFrom() != null) {
+				allOk &= evaluate(lifecycle, rule);
+			} else {
+				allOk &= false;
+			}
+		}
+		return allOk;
+	}
+	
+	public boolean evaluate(RepositoryEntryLifecycle lifecycle, ReminderRule rule) {
+		Date now = cleanNow();
+		Date validTo = lifecycle.getValidTo();
+		if(validTo != null && now.compareTo(validTo) >= 0) {
+			return false;//the course is at the end
+		}
+
+		ReminderRuleImpl r = (ReminderRuleImpl)rule;
+		int distance = Integer.parseInt(r.getRightOperand());
+		LaunchUnit unit = LaunchUnit.valueOf(r.getRightUnit());
+		Date referenceDate = getDate(lifecycle.getValidFrom(), distance, unit);
+		return now.compareTo(referenceDate) >= 0;
+	}
+
+	private Date cleanNow() {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(new Date());
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return cal.getTime();
+	}
+	
+	private Date getDate(Date date, int distance, LaunchUnit unit) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		switch(unit) {
+			case day:
+				cal.add(Calendar.DATE, distance);
+				break;
+			case week:
+				cal.add(Calendar.DATE, 7 * distance);
+				break;
+			case month:
+				cal.add(Calendar.MONTH, distance);
+				break;
+			case year:
+				cal.add(Calendar.YEAR, distance);
+				break;
+		}
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return cal.getTime();
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/reminder/ui/RepositoryEntryLifecycleAfterValidFromRuleEditor.java b/src/main/java/org/olat/modules/reminder/ui/RepositoryEntryLifecycleAfterValidFromRuleEditor.java
new file mode 100644
index 00000000000..dd2cb38f50f
--- /dev/null
+++ b/src/main/java/org/olat/modules/reminder/ui/RepositoryEntryLifecycleAfterValidFromRuleEditor.java
@@ -0,0 +1,136 @@
+/**
+ * <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.ui;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.util.CodeHelper;
+import org.olat.core.util.StringHelper;
+import org.olat.core.util.Util;
+import org.olat.modules.reminder.ReminderRule;
+import org.olat.modules.reminder.RuleEditorFragment;
+import org.olat.modules.reminder.model.ReminderRuleImpl;
+import org.olat.modules.reminder.rule.LaunchUnit;
+
+/**
+ * 
+ * Initial date: 26.05.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class RepositoryEntryLifecycleAfterValidFromRuleEditor extends RuleEditorFragment {
+	
+	private static final String[] unitKeys = new String[]{
+		LaunchUnit.day.name(), LaunchUnit.week.name(), LaunchUnit.month.name(), LaunchUnit.year.name()
+	};
+	
+	private TextElement valueEl;
+	private SingleSelection unitEl;
+	
+	private final String ruleType;
+	
+	public RepositoryEntryLifecycleAfterValidFromRuleEditor(ReminderRule rule, String ruleType) {
+		super(rule);
+		this.ruleType = ruleType;
+	}
+
+	@Override
+	public FormItem initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		
+		String page = Util.getPackageVelocityRoot(this.getClass()) + "/repo_valid.html";
+		String id = Long.toString(CodeHelper.getRAMUniqueID());
+		
+		Translator trans = formLayout.getTranslator();
+		FormLayoutContainer ruleCont = FormLayoutContainer
+				.createCustomFormLayout("repo.valid.".concat(id), formLayout.getTranslator(), page);
+		ruleCont.setRootForm(formLayout.getRootForm());
+		formLayout.add(ruleCont);
+		ruleCont.contextPut("id", id);
+		
+		String currentValue = null;
+		String currentUnit = null;
+		if(rule instanceof ReminderRuleImpl) {
+			ReminderRuleImpl r = (ReminderRuleImpl)rule;
+			currentValue = r.getRightOperand();
+			currentUnit = r.getRightUnit();
+		}
+
+		valueEl = uifactory.addTextElement("launchvalue".concat(id), null, 128, currentValue, ruleCont);
+		valueEl.setDomReplacementWrapperRequired(false);
+		valueEl.setDisplaySize(3);
+		
+		String[] unitValues = new String[] {
+				trans.translate(LaunchUnit.day.name()), trans.translate(LaunchUnit.week.name()),
+				trans.translate(LaunchUnit.month.name()), trans.translate(LaunchUnit.year.name())
+		};
+		
+		unitEl = uifactory.addDropdownSingleselect("launchunit".concat(id), null, ruleCont, unitKeys, unitValues, null);
+		unitEl.setDomReplacementWrapperRequired(false);
+		boolean unitSelected = false;
+		if(currentUnit != null) {
+			for(String unitKey:unitKeys) {
+				if(currentUnit.equals(unitKey)) {
+					unitEl.select(unitKey, true);
+					unitSelected = true;
+				}
+			}
+		}
+		if(!unitSelected) {
+			unitEl.select(unitKeys[1], true);	
+		}
+
+		return ruleCont;
+	}
+
+	@Override
+	public boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = true;
+		
+		unitEl.clearError();
+		if(!unitEl.isOneSelected()) {
+			unitEl.setErrorKey("form.mandatory.hover", null);
+			allOk &= false;
+		}
+		
+		valueEl.clearError();
+		if(!StringHelper.containsNonWhitespace(valueEl.getValue())) {
+			valueEl.setErrorKey("form.mandatory.hover", null);
+			allOk &= false;
+		}
+		
+		return allOk;
+	}
+
+	@Override
+	public ReminderRule getConfiguration() {
+		ReminderRuleImpl configuredRule = new ReminderRuleImpl();
+		configuredRule.setType(ruleType);
+		configuredRule.setOperator(">");
+		configuredRule.setRightOperand(valueEl.getValue());
+		configuredRule.setRightUnit(unitEl.getSelectedKey());
+		return configuredRule;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/reminder/ui/_content/repo_valid.html b/src/main/java/org/olat/modules/reminder/ui/_content/repo_valid.html
new file mode 100644
index 00000000000..aedb0408566
--- /dev/null
+++ b/src/main/java/org/olat/modules/reminder/ui/_content/repo_valid.html
@@ -0,0 +1,6 @@
+<div class='form-inline'>
+	$r.render("launchvalue$id") $r.render("launchunit$id") <span class="form-control-static">$r.translate("after.validfrom")</span>
+	#if($f.hasError("launchvalue$id"))
+		<br/>$r.render("launchvalue${id}_ERROR")
+	#end
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_de.properties
index d017a6aff4a..a4ff292848d 100644
--- a/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_de.properties
@@ -1,8 +1,9 @@
 #Thu Apr 09 21:54:06 CEST 2015
 admin.menu.title=Kurserinnerungen
 admin.menu.title.alt=Kurserinnerungen
+after.validfrom=nach Beginsdatum
 ago=her
-default.send.time=Standard Sendezeit für Erinnerungen
+default.send.time=Standard Sendezeit f\u00FCr Erinnerungen
 enable.reminders=Kurserinnerungen einschalten
 enable.sms.reminders=SMS Erinnerungen
 error.group.not.found=Gruppe existiert nicht in diesem Kurs
@@ -19,6 +20,7 @@ rule.course.enrollment.date=Einschreibedatum
 rule.course.role=Kursrolle
 rule.group.member=Gruppenteilnehmer
 rule.initial.course.launch.date=Erster Kursbesuch
+rule.lifecycle.validfrom=Begindatum von Kursdurchf\u00FChrungzeitraum
 rule.recent.course.launch.date=Letzter Kursbesuch
 rule.user.property=Benutzereigenschaft
 
diff --git a/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_en.properties
index c133b55888c..b2817fd996b 100644
--- a/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_en.properties
@@ -1,6 +1,7 @@
 #Thu Apr 09 21:54:18 CEST 2015
 admin.menu.title=Course reminders
 admin.menu.title.alt=Course reminders
+after.validfrom=after begin date
 ago=since
 default.send.time=Default time to send reminders
 enable.reminders=Activate course reminders
@@ -19,6 +20,7 @@ rule.after.date=After date
 rule.course.enrollment.date=Enrollment date
 rule.course.role=Course role
 rule.group.member=Group member
+rule.lifecycle.validfrom=Begin date of execution period
 rule.initial.course.launch.date=Initial course launch date
 rule.recent.course.launch.date=Recent course launch date
 rule.user.property=User property
diff --git a/src/test/java/org/olat/modules/reminder/manager/ReminderRuleEngineTest.java b/src/test/java/org/olat/modules/reminder/manager/ReminderRuleEngineTest.java
index 6d3e71d0bfe..8cfd80217b3 100644
--- a/src/test/java/org/olat/modules/reminder/manager/ReminderRuleEngineTest.java
+++ b/src/test/java/org/olat/modules/reminder/manager/ReminderRuleEngineTest.java
@@ -57,10 +57,14 @@ import org.olat.modules.reminder.rule.DateRuleSPI;
 import org.olat.modules.reminder.rule.InitialCourseLaunchRuleSPI;
 import org.olat.modules.reminder.rule.LaunchUnit;
 import org.olat.modules.reminder.rule.RecentCourseLaunchRuleSPI;
+import org.olat.modules.reminder.rule.RepositoryEntryLifecycleAfterValidFromRuleSPI;
 import org.olat.modules.reminder.rule.RepositoryEntryRoleRuleSPI;
 import org.olat.modules.reminder.rule.UserPropertyRuleSPI;
 import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryManager;
+import org.olat.repository.manager.RepositoryEntryLifecycleDAO;
 import org.olat.repository.manager.RepositoryEntryRelationDAO;
+import org.olat.repository.model.RepositoryEntryLifecycle;
 import org.olat.repository.model.RepositoryEntryToGroupRelation;
 import org.olat.restapi.repository.course.CoursesWebService;
 import org.olat.test.JunitTestHelper;
@@ -85,6 +89,10 @@ public class ReminderRuleEngineTest extends OlatTestCase {
 	@Autowired
 	private BusinessGroupDAO businessGroupDao;
 	@Autowired
+	private RepositoryManager repositoryManager;
+	@Autowired
+	private RepositoryEntryLifecycleDAO lifecycleDao;
+	@Autowired
 	private BusinessGroupRelationDAO businessGroupRelationDao;
 	@Autowired
 	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
@@ -652,6 +660,91 @@ public class ReminderRuleEngineTest extends OlatTestCase {
 		return rules;
 	}
 	
+	@Test
+	public void afterBeginDate() {
+		//create a course with 3 members
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("before-begin-1");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("before-begin-2");
+		Identity id3 = JunitTestHelper.createAndPersistIdentityAsRndUser("before-begin-3");
+
+		ICourse course = CoursesWebService.createEmptyCourse(null, "initial-launch-dates", "course long name", null);
+		RepositoryEntry re = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
+		
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(new Date());//now
+		cal.add(Calendar.DATE, -21);
+		Date validFrom = cal.getTime();
+		cal.add(Calendar.DATE, 90);
+		Date validTo = cal.getTime();
+		
+		RepositoryEntryLifecycle cycle = lifecycleDao.create("Cycle 1", "Cycle soft 1", false, validFrom, validTo);
+		re = repositoryManager.setDescriptionAndName(re, null, null, null, null, null, null, cycle);
+		repositoryEntryRelationDao.addRole(id1, re, GroupRoles.owner.name());
+		repositoryEntryRelationDao.addRole(id2, re, GroupRoles.coach.name());
+		repositoryEntryRelationDao.addRole(id3, re, GroupRoles.participant.name());
+		dbInstance.commit();
+		
+		{ // check after 2 days
+			List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(2, LaunchUnit.day);
+			boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules);
+			Assert.assertTrue(match);
+		}
+		
+		{ // check after 7 days (between begin and and date)
+			List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(7, LaunchUnit.day);
+			boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules);
+			Assert.assertTrue(match);
+		}
+		
+		{ // check after 2 week s
+			List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(2, LaunchUnit.week);
+			boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules);
+			Assert.assertTrue(match);
+		}
+		
+		{ // check after 21 days
+			List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(21, LaunchUnit.day);
+			boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules);
+			Assert.assertTrue(match);
+		}
+		
+		{ // check after 3 weeks
+			List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(3, LaunchUnit.week);
+			boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules);
+			Assert.assertTrue(match);
+		}
+		
+		{ // check after 22 days
+			List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(22, LaunchUnit.day);
+			boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules);
+			Assert.assertFalse(match);
+		}
+		
+		{ // check after 4 weeks
+			List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(4, LaunchUnit.week);
+			boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules);
+			Assert.assertFalse(match);
+		}
+		
+		{ // check after 1 month
+			List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(1, LaunchUnit.month);
+			boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules);
+			Assert.assertFalse(match);
+		}
+	}
+	
+	private List<ReminderRule> getRepositoryEntryLifecycleRuleValidFromRule(int amount, LaunchUnit unit) {
+		ReminderRuleImpl rule = new ReminderRuleImpl();
+		rule.setType(RepositoryEntryLifecycleAfterValidFromRuleSPI.class.getSimpleName());
+		rule.setOperator(">");
+		rule.setRightOperand(Integer.toString(amount));
+		rule.setRightUnit(unit.name());
+		
+		List<ReminderRule> rules = new ArrayList<>(1);
+		rules.add(rule);
+		return rules;
+	}
+	
 	@Test
 	public void score() {
 		//create a course with 3 members and generate some datas
-- 
GitLab