From 31e992648b95166581ceca6488816083412b0278 Mon Sep 17 00:00:00 2001
From: uhensler <urs.hensler@frentix.com>
Date: Tue, 17 Jul 2018 09:40:05 +0200
Subject: [PATCH] OO-3304: Job to update the status of a quality data
 collection

---
 .../scheduler/_spring/schedulerContext.xml    |  2 +-
 .../olat/modules/quality/QualityService.java  | 21 +++++-
 .../quality/_spring/qualityContext.xml        | 12 ++--
 .../manager/QualityDataCollectionDAO.java     | 38 +++++++++++
 ...ualityReminderJob.java => QualityJob.java} | 13 +++-
 .../quality/manager/QualityServiceImpl.java   | 63 +++++++++++++----
 .../quality/model/QualityReminderImpl.java    | 11 +++
 .../database/mysql/alter_12_4_x_to_13_0_0.sql |  3 +
 .../database/mysql/setupDatabase.sql          |  2 +
 .../oracle/alter_12_4_x_to_13_0_0.sql         |  2 +
 .../database/oracle/setupDatabase.sql         |  2 +
 .../postgresql/alter_12_4_x_to_13_0_0.sql     |  2 +
 .../database/postgresql/setupDatabase.sql     |  2 +
 .../manager/QualityDataCollectionDAOTest.java | 68 +++++++++++++++++++
 .../quality/manager/QualityTestHelper.java    | 52 +++++++++++++-
 15 files changed, 265 insertions(+), 28 deletions(-)
 rename src/main/java/org/olat/modules/quality/manager/{QualityReminderJob.java => QualityJob.java} (77%)

diff --git a/src/main/java/org/olat/core/commons/services/scheduler/_spring/schedulerContext.xml b/src/main/java/org/olat/core/commons/services/scheduler/_spring/schedulerContext.xml
index 01aaa7a7487..602bd9c0e59 100644
--- a/src/main/java/org/olat/core/commons/services/scheduler/_spring/schedulerContext.xml
+++ b/src/main/java/org/olat/core/commons/services/scheduler/_spring/schedulerContext.xml
@@ -53,7 +53,7 @@ How to add a new job:
             <ref bean="calendarImportTrigger"/>
             <ref bean="autoCloseLecturesTrigger"/>
             <ref bean="reminderLecturesTrigger"/>
-            <ref bean="qualityReminderTrigger"/>
+            <ref bean="qualityTrigger"/>
             <ref bean="deleteUserDataExportTrigger"/>
             <ref bean="cspCleanupJob"/>
         </list>
diff --git a/src/main/java/org/olat/modules/quality/QualityService.java b/src/main/java/org/olat/modules/quality/QualityService.java
index fbfb9f19fd7..e30da26319a 100644
--- a/src/main/java/org/olat/modules/quality/QualityService.java
+++ b/src/main/java/org/olat/modules/quality/QualityService.java
@@ -46,6 +46,20 @@ public interface QualityService {
 	public QualityDataCollection updateDataCollection(QualityDataCollection dataCollection);
 
 	public QualityDataCollection loadDataCollectionByKey(QualityDataCollectionRef dataCollectionRef);
+	
+	/**
+	 * Updates the status to RUNNING for data collections which are READY and have passed the start date.
+	 * 
+	 * @param until the date to evaluate if a start date has passed
+	 */
+	public void stopDataCollections(Date until);
+
+	/**
+	 * Updates the status to FINISHED of all data collections which are STARTED and have passed the deadline.
+	 * 
+	 * @param until the date to evaluate if a deadline has passed
+	 */
+	public void startDataCollection(Date until);
 
 	public int getDataCollectionCount();
 
@@ -138,6 +152,11 @@ public interface QualityService {
 
 	public void deleteReminder(QualityReminder reminder);
 	
-	public void sendRemainders();
+	/**
+	 * Send all pending reminders.
+	 *
+	 * @param until all reminders with a planed date before this date
+	 */
+	public void sendRemainders(Date until);
 
 }
diff --git a/src/main/java/org/olat/modules/quality/_spring/qualityContext.xml b/src/main/java/org/olat/modules/quality/_spring/qualityContext.xml
index c6c114b3a17..7329da8b6d5 100644
--- a/src/main/java/org/olat/modules/quality/_spring/qualityContext.xml
+++ b/src/main/java/org/olat/modules/quality/_spring/qualityContext.xml
@@ -29,9 +29,9 @@
 		</property>
 	</bean>
 	
-	<!-- Quality reminder job -->
-	<bean id="qualityReminderTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
-		<property name="jobDetail" ref="qualityReminderJob.${cluster.singleton.services}" />
+	<!-- Quality job -->
+	<bean id="qualityTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
+		<property name="jobDetail" ref="qualityJob.${cluster.singleton.services}" />
 	  	<!-- adjust cron style syntax for your notification needs 
 	   	"0 10 0 * *"  e.g. 10 minutes after midnight
 	   	
@@ -52,12 +52,12 @@
 		<property name="startDelay" value="40000" />
 	</bean>
 
-	<bean id="qualityReminderJob.enabled" class="org.springframework.scheduling.quartz.JobDetailFactoryBean" lazy-init="true">
-		<property name="jobClass" value="org.olat.modules.quality.manager.QualityReminderJob" />
+	<bean id="qualityJob.enabled" class="org.springframework.scheduling.quartz.JobDetailFactoryBean" lazy-init="true">
+		<property name="jobClass" value="org.olat.modules.quality.manager.QualityJob" />
 	</bean>
 	
 	<!-- dummy bean -->
-	<bean id="qualityReminderJob.disabled" class="org.springframework.scheduling.quartz.JobDetailFactoryBean" lazy-init="true">
+	<bean id="qualityJob.disabled" class="org.springframework.scheduling.quartz.JobDetailFactoryBean" lazy-init="true">
 		<property name="jobClass" value="org.olat.core.commons.services.scheduler.DummyJob" />
 	</bean>
 	
diff --git a/src/main/java/org/olat/modules/quality/manager/QualityDataCollectionDAO.java b/src/main/java/org/olat/modules/quality/manager/QualityDataCollectionDAO.java
index e3ecbeb261a..d4e223795ff 100644
--- a/src/main/java/org/olat/modules/quality/manager/QualityDataCollectionDAO.java
+++ b/src/main/java/org/olat/modules/quality/manager/QualityDataCollectionDAO.java
@@ -19,6 +19,12 @@
  */
 package org.olat.modules.quality.manager;
 
+import static org.olat.modules.quality.QualityDataCollectionStatus.READY;
+import static org.olat.modules.quality.QualityDataCollectionStatus.RUNNING;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
@@ -81,6 +87,37 @@ class QualityDataCollectionDAO {
 		return dataCollections.isEmpty() ? null : dataCollections.get(0);
 	}
 
+	Collection<QualityDataCollection> loadWithPendingStart(Date until) {
+		if (until == null) return Collections.emptyList();
+		
+		StringBuilder sb = new StringBuilder(256);
+		sb.append("select collection");
+		sb.append("  from qualitydatacollection as collection");
+		sb.append(" where collection.status = '").append(READY).append("'");
+		sb.append("   and collection.start <= :until");
+		
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), QualityDataCollection.class)
+				.setParameter("until", until)
+				.getResultList();
+	}
+
+	Collection<QualityDataCollection> loadWithPendingDeadline(Date until) {
+		if (until == null) return Collections.emptyList();
+		
+		StringBuilder sb = new StringBuilder(256);
+		sb.append("select collection");
+		sb.append("  from qualitydatacollection as collection");
+		sb.append(" where collection.status in :status");
+		sb.append("   and collection.deadline <= :until");
+		
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), QualityDataCollection.class)
+				.setParameter("status", Arrays.asList(READY, RUNNING))
+				.setParameter("until", until)
+				.getResultList();
+	}
+
 	void deleteDataCollection(QualityDataCollectionRef dataCollectionRef) {
 		if (dataCollectionRef == null || dataCollectionRef.getKey() == null) return;
 		
@@ -185,4 +222,5 @@ class QualityDataCollectionDAO {
 		}
 		return sb;
 	}
+
 }
diff --git a/src/main/java/org/olat/modules/quality/manager/QualityReminderJob.java b/src/main/java/org/olat/modules/quality/manager/QualityJob.java
similarity index 77%
rename from src/main/java/org/olat/modules/quality/manager/QualityReminderJob.java
rename to src/main/java/org/olat/modules/quality/manager/QualityJob.java
index a713d7a9cbf..69bfcc25a95 100644
--- a/src/main/java/org/olat/modules/quality/manager/QualityReminderJob.java
+++ b/src/main/java/org/olat/modules/quality/manager/QualityJob.java
@@ -19,8 +19,11 @@
  */
 package org.olat.modules.quality.manager;
 
+import java.util.Date;
+
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.services.scheduler.JobWithDB;
+import org.olat.modules.quality.QualityModule;
 import org.olat.modules.quality.QualityService;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
@@ -31,12 +34,18 @@ import org.quartz.JobExecutionException;
  * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
  *
  */
-public class QualityReminderJob extends JobWithDB {
+public class QualityJob extends JobWithDB {
 
 	@Override
 	public void executeWithDB(JobExecutionContext arg0) throws JobExecutionException {
+		QualityModule qualityModule = CoreSpringFactory.getImpl(QualityModule.class);
+		if (!qualityModule.isEnabled()) return;
+		
 		QualityService qualityService = CoreSpringFactory.getImpl(QualityService.class);
-		qualityService.sendRemainders();
+		Date until = new Date();
+		qualityService.startDataCollection(until);
+		qualityService.stopDataCollections(until);
+		qualityService.sendRemainders(until);
 	}
 
 }
diff --git a/src/main/java/org/olat/modules/quality/manager/QualityServiceImpl.java b/src/main/java/org/olat/modules/quality/manager/QualityServiceImpl.java
index 07f9eafce54..118a372a489 100644
--- a/src/main/java/org/olat/modules/quality/manager/QualityServiceImpl.java
+++ b/src/main/java/org/olat/modules/quality/manager/QualityServiceImpl.java
@@ -19,6 +19,9 @@
  */
 package org.olat.modules.quality.manager;
 
+import static org.olat.modules.quality.QualityDataCollectionStatus.FINISHED;
+import static org.olat.modules.quality.QualityDataCollectionStatus.RUNNING;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -45,10 +48,10 @@ import org.olat.modules.quality.QualityContextRef;
 import org.olat.modules.quality.QualityDataCollection;
 import org.olat.modules.quality.QualityDataCollectionLight;
 import org.olat.modules.quality.QualityDataCollectionRef;
+import org.olat.modules.quality.QualityDataCollectionStatus;
 import org.olat.modules.quality.QualityDataCollectionView;
 import org.olat.modules.quality.QualityExecutorParticipation;
 import org.olat.modules.quality.QualityExecutorParticipationSearchParams;
-import org.olat.modules.quality.QualityModule;
 import org.olat.modules.quality.QualityParticipation;
 import org.olat.modules.quality.QualityReminder;
 import org.olat.modules.quality.QualityReminderType;
@@ -68,8 +71,6 @@ public class QualityServiceImpl implements QualityService {
 
 	private static final OLog log = Tracing.createLoggerFor(QualityServiceImpl.class);
 
-	@Autowired
-	private QualityModule qualityModule;
 	@Autowired
 	private QualityDataCollectionDAO dataCollectionDao;
 	@Autowired
@@ -108,6 +109,34 @@ public class QualityServiceImpl implements QualityService {
 		return dataCollectionDao.loadDataCollectionByKey(dataCollectionRef);
 	}
 
+	@Override
+	public void stopDataCollections(Date until) {
+		Collection<QualityDataCollection> dataCollections = dataCollectionDao.loadWithPendingStart(until);
+		log.debug("Update status to RUNNING. Number of pending data collections: " + dataCollections.size());
+		for (QualityDataCollection dataCollection: dataCollections) {
+			updateDataCollection(dataCollection, RUNNING);
+		}
+	}
+
+	@Override
+	public void startDataCollection(Date until) {
+		Collection<QualityDataCollection> dataCollections = dataCollectionDao.loadWithPendingDeadline(until);
+		log.debug("Update status to FINISHED. Number of pending data collections: " + dataCollections.size());
+		for (QualityDataCollection dataCollection: dataCollections) {
+			updateDataCollection(dataCollection, FINISHED);
+		}
+	}
+
+	private void updateDataCollection(QualityDataCollection dataCollection, QualityDataCollectionStatus status) {
+		try {
+			dataCollection.setStatus(status);
+			QualityDataCollection updatedDataCollection = dataCollectionDao.updateDataCollection(dataCollection);
+			log.info("Status of quality data collection updated to " + status + ". " + updatedDataCollection.toString());
+		} catch (Exception e) {
+			log.error("Update of status of quality data collection to " + status + " failed! " + dataCollection.toString(), e);
+		}
+	}
+
 	@Override
 	public int getDataCollectionCount() {
 		return dataCollectionDao.getDataCollectionCount();
@@ -254,22 +283,27 @@ public class QualityServiceImpl implements QualityService {
 	}
 
 	@Override
-	public void sendRemainders() {
-		if (!qualityModule.isEnabled()) return;
-		
-		Date untilNow = new Date();
-		Collection<QualityReminder> reminders = reminderDao.loadPending(untilNow);
+	public void sendRemainders(Date until) {		
+		Collection<QualityReminder> reminders = reminderDao.loadPending(until);
 		log.debug("Send emails for quality remiders. Number of pending reminders: " + reminders.size());
-		for (QualityReminder reminder : reminders) {
-			QualityReminder invitation = getInvitation(reminder);
-			List<EvaluationFormParticipation> participations = getParticipants(reminder);
-			for (EvaluationFormParticipation participation : participations) {
-				qualityMailing.sendMail(reminder, invitation, participation);
+		for (QualityReminder reminder: reminders) {
+			try {
+				sendReminder(reminder);
+				reminderDao.updateDateDone(reminder, until);
+			} catch (Exception e) {
+				log.error("Send reminder of quality data collection failed!" + reminder.toString(), e);
 			}
-			reminderDao.updateDateDone(reminder, untilNow);
 		}	
 	}
 
+	private void sendReminder(QualityReminder reminder) {
+		QualityReminder invitation = getInvitation(reminder);
+		List<EvaluationFormParticipation> participations = getParticipants(reminder);
+		for (EvaluationFormParticipation participation : participations) {
+			qualityMailing.sendMail(reminder, invitation, participation);
+		}
+	}
+
 	private QualityReminder getInvitation(QualityReminder reminder) {
 		if (QualityReminderType.INVITATION.equals(reminder.getType())) {
 			return reminder;
@@ -284,5 +318,4 @@ public class QualityServiceImpl implements QualityService {
 		EvaluationFormSurvey survey = evaluationFormManager.loadSurvey(dataCollection, null);
 		return evaluationFormManager.loadParticipations(survey, status);
 	}
-
 }
diff --git a/src/main/java/org/olat/modules/quality/model/QualityReminderImpl.java b/src/main/java/org/olat/modules/quality/model/QualityReminderImpl.java
index 7149b907424..8a527a729de 100644
--- a/src/main/java/org/olat/modules/quality/model/QualityReminderImpl.java
+++ b/src/main/java/org/olat/modules/quality/model/QualityReminderImpl.java
@@ -173,5 +173,16 @@ public class QualityReminderImpl implements QualityReminder, Persistable {
 	public boolean equalsByPersistableKey(Persistable persistable) {
 		return equals(persistable);
 	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("QualityReminderImpl [key=");
+		builder.append(key);
+		builder.append(", sendPlaned=");
+		builder.append(sendPlaned);
+		builder.append("]");
+		return builder.toString();
+	}
 	
 }
diff --git a/src/main/resources/database/mysql/alter_12_4_x_to_13_0_0.sql b/src/main/resources/database/mysql/alter_12_4_x_to_13_0_0.sql
index 86a4aa07aa4..0dc4fe0455a 100644
--- a/src/main/resources/database/mysql/alter_12_4_x_to_13_0_0.sql
+++ b/src/main/resources/database/mysql/alter_12_4_x_to_13_0_0.sql
@@ -321,6 +321,9 @@ alter table o_qual_context_to_cur_element ENGINE = InnoDB;
 alter table o_qual_context_to_tax_level ENGINE = InnoDB;
 alter table o_qual_reminder ENGINE = InnoDB;
 
+
+create index idx_dc_status_idx on o_qual_data_collection (q_status);
+
 alter table o_qual_context add constraint qual_con_to_data_collection_idx foreign key (fk_data_collection) references o_qual_data_collection (id);
 alter table o_qual_context add constraint qual_con_to_participation_idx foreign key (fk_eva_participation) references o_eva_form_participation (id);
 alter table o_qual_context add constraint qual_con_to_session_idx foreign key (fk_eva_session) references o_eva_form_session (id);
diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql
index a24345dd588..1f5c4d19be0 100644
--- a/src/main/resources/database/mysql/setupDatabase.sql
+++ b/src/main/resources/database/mysql/setupDatabase.sql
@@ -3360,6 +3360,8 @@ alter table o_eva_form_response add constraint eva_resp_to_sess_idx foreign key
 create index idx_eva_resp_report_idx on o_eva_form_response (fk_session, e_responseidentifier, e_no_response);
 
 -- quality management
+create index idx_dc_status_idx on o_qual_data_collection (q_status);
+
 alter table o_qual_context add constraint qual_con_to_data_collection_idx foreign key (fk_data_collection) references o_qual_data_collection (id);
 alter table o_qual_context add constraint qual_con_to_participation_idx foreign key (fk_eva_participation) references o_eva_form_participation (id);
 alter table o_qual_context add constraint qual_con_to_session_idx foreign key (fk_eva_session) references o_eva_form_session (id);
diff --git a/src/main/resources/database/oracle/alter_12_4_x_to_13_0_0.sql b/src/main/resources/database/oracle/alter_12_4_x_to_13_0_0.sql
index a7dbee71595..b0d9e2cba65 100644
--- a/src/main/resources/database/oracle/alter_12_4_x_to_13_0_0.sql
+++ b/src/main/resources/database/oracle/alter_12_4_x_to_13_0_0.sql
@@ -315,6 +315,8 @@ create table o_qual_reminder (
    primary key (id)
 );
 
+create index idx_dc_status_idx on o_qual_data_collection (q_status);
+
 alter table o_qual_context add constraint qual_con_to_data_collection_idx foreign key (fk_data_collection) references o_qual_data_collection (id);
 create index idx_con_to_data_collection_idx on o_qual_context (fk_data_collection);
 alter table o_qual_context add constraint qual_con_to_participation_idx foreign key (fk_eva_participation) references o_eva_form_participation (id);
diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql
index c285071f881..b9825d229a7 100644
--- a/src/main/resources/database/oracle/setupDatabase.sql
+++ b/src/main/resources/database/oracle/setupDatabase.sql
@@ -3480,6 +3480,8 @@ create index idx_eva_resp_to_sess_idx on o_eva_form_response (fk_session);
 create index idx_eva_resp_report_idx on o_eva_form_response (fk_session, e_responseidentifier, e_no_response);
 
 -- quality management
+create index idx_dc_status_idx on o_qual_data_collection (q_status);
+
 alter table o_qual_context add constraint qual_con_to_data_collection_idx foreign key (fk_data_collection) references o_qual_data_collection (id);
 create index idx_con_to_data_collection_idx on o_qual_context (fk_data_collection);
 alter table o_qual_context add constraint qual_con_to_participation_idx foreign key (fk_eva_participation) references o_eva_form_participation (id);
diff --git a/src/main/resources/database/postgresql/alter_12_4_x_to_13_0_0.sql b/src/main/resources/database/postgresql/alter_12_4_x_to_13_0_0.sql
index 8a8f98862a1..c73c9548af5 100644
--- a/src/main/resources/database/postgresql/alter_12_4_x_to_13_0_0.sql
+++ b/src/main/resources/database/postgresql/alter_12_4_x_to_13_0_0.sql
@@ -321,6 +321,8 @@ create table o_qual_reminder (
    primary key (id)
 );
 
+create index idx_dc_status_idx on o_qual_data_collection (q_status) where q_status in ('READY', 'RUNNING');
+
 alter table o_qual_context add constraint qual_con_to_data_collection_idx foreign key (fk_data_collection) references o_qual_data_collection (id);
 create index idx_con_to_data_collection_idx on o_qual_context (fk_data_collection);
 alter table o_qual_context add constraint qual_con_to_participation_idx foreign key (fk_eva_participation) references o_eva_form_participation (id);
diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql
index 45457efb829..6cc9a3fb675 100644
--- a/src/main/resources/database/postgresql/setupDatabase.sql
+++ b/src/main/resources/database/postgresql/setupDatabase.sql
@@ -3379,6 +3379,8 @@ create index idx_eva_resp_to_sess_idx on o_eva_form_response (fk_session);
 create index idx_eva_resp_report_idx on o_eva_form_response (fk_session, e_responseidentifier, e_no_response);
 
 -- quality management
+create index idx_dc_status_idx on o_qual_data_collection (q_status) where q_status in ('READY', 'RUNNING');
+
 alter table o_qual_context add constraint qual_con_to_data_collection_idx foreign key (fk_data_collection) references o_qual_data_collection (id);
 create index idx_con_to_data_collection_idx on o_qual_context (fk_data_collection);
 alter table o_qual_context add constraint qual_con_to_participation_idx foreign key (fk_eva_participation) references o_eva_form_participation (id);
diff --git a/src/test/java/org/olat/modules/quality/manager/QualityDataCollectionDAOTest.java b/src/test/java/org/olat/modules/quality/manager/QualityDataCollectionDAOTest.java
index d6e96c0c5fb..43af70f750f 100644
--- a/src/test/java/org/olat/modules/quality/manager/QualityDataCollectionDAOTest.java
+++ b/src/test/java/org/olat/modules/quality/manager/QualityDataCollectionDAOTest.java
@@ -20,9 +20,14 @@
 package org.olat.modules.quality.manager;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.olat.modules.quality.QualityDataCollectionStatus.FINISHED;
+import static org.olat.modules.quality.QualityDataCollectionStatus.PREPARATION;
+import static org.olat.modules.quality.QualityDataCollectionStatus.READY;
+import static org.olat.modules.quality.QualityDataCollectionStatus.RUNNING;
 
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collection;
 import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.List;
@@ -39,6 +44,7 @@ import org.olat.modules.quality.QualityDataCollectionRef;
 import org.olat.modules.quality.QualityDataCollectionStatus;
 import org.olat.modules.quality.QualityDataCollectionTopicType;
 import org.olat.modules.quality.QualityDataCollectionView;
+import org.olat.modules.quality.QualityService;
 import org.olat.modules.quality.ui.DataCollectionDataModel.DataCollectionCols;
 import org.olat.repository.RepositoryEntry;
 import org.olat.test.OlatTestCase;
@@ -58,6 +64,8 @@ public class QualityDataCollectionDAOTest extends OlatTestCase {
 	private DB dbInstance;
 	@Autowired
 	private QualityTestHelper qualityTestHelper;
+	@Autowired
+	private QualityService qualityService;
 	
 	@Autowired
 	private QualityDataCollectionDAO sut;
@@ -142,6 +150,66 @@ public class QualityDataCollectionDAOTest extends OlatTestCase {
 		assertThat(loadDataCollection).isEqualTo(dataCollection);
 	}
 	
+	@Test
+	public void shouldLoadDataCollectionsWithPendingStart() {
+		Date until = new Date();
+		QualityDataCollection future1 = qualityTestHelper.createDataCollectionWithStartInFuture();
+		QualityDataCollection future2 = qualityTestHelper.createDataCollectionWithStartInFuture();
+		QualityDataCollection pastReady1 = qualityTestHelper.createDataCollectionWithStartInPast();
+		pastReady1.setStatus(READY);
+		qualityService.updateDataCollection(pastReady1);
+		QualityDataCollection pastReady2 = qualityTestHelper.createDataCollectionWithStartInPast();
+		pastReady2.setStatus(READY);
+		qualityService.updateDataCollection(pastReady2);
+		QualityDataCollection pastPreparation = qualityTestHelper.createDataCollectionWithStartInPast();
+		pastPreparation.setStatus(PREPARATION);
+		qualityService.updateDataCollection(pastPreparation);
+		QualityDataCollection pastRunning = qualityTestHelper.createDataCollectionWithStartInPast();
+		pastRunning.setStatus(RUNNING);
+		qualityService.updateDataCollection(pastRunning);
+		QualityDataCollection pastFinished = qualityTestHelper.createDataCollectionWithStartInPast();
+		pastFinished.setStatus(RUNNING);
+		qualityService.updateDataCollection(pastFinished);
+		QualityDataCollection noStart = qualityTestHelper.createDataCollectionWithoutValues();
+		dbInstance.commitAndCloseSession();
+		
+		Collection<QualityDataCollection> dataCollections = sut.loadWithPendingStart(until);
+		
+		assertThat(dataCollections)
+				.containsExactlyInAnyOrder(pastReady1, pastReady2)
+				.doesNotContain(future1, future2, noStart, pastPreparation, pastRunning, pastFinished);
+	}
+	
+	@Test
+	public void shouldLoadDataCollectionsWithPendingDeadline() {
+		Date until = new Date();
+		QualityDataCollection future1 = qualityTestHelper.createDataCollectionWithDeadlineInFuture();
+		QualityDataCollection future2 = qualityTestHelper.createDataCollectionWithDeadlineInFuture();
+		QualityDataCollection pastRunning1 = qualityTestHelper.createDataCollectionWithDeadlineInPast();
+		pastRunning1.setStatus(RUNNING);
+		qualityService.updateDataCollection(pastRunning1);
+		QualityDataCollection pastRunning2 = qualityTestHelper.createDataCollectionWithDeadlineInPast();
+		pastRunning2.setStatus(RUNNING);
+		qualityService.updateDataCollection(pastRunning2);
+		QualityDataCollection pastPreparation = qualityTestHelper.createDataCollectionWithDeadlineInPast();
+		pastPreparation.setStatus(PREPARATION);
+		qualityService.updateDataCollection(pastPreparation);
+		QualityDataCollection pastReady = qualityTestHelper.createDataCollectionWithDeadlineInPast();
+		pastReady.setStatus(READY);
+		qualityService.updateDataCollection(pastReady);
+		QualityDataCollection pastFinished = qualityTestHelper.createDataCollectionWithDeadlineInPast();
+		pastFinished.setStatus(FINISHED);
+		qualityService.updateDataCollection(pastFinished);
+		QualityDataCollection noDeadline = qualityTestHelper.createDataCollectionWithoutValues();
+		dbInstance.commitAndCloseSession();
+		
+		Collection<QualityDataCollection> dataCollections = sut.loadWithPendingDeadline(until);
+		
+		assertThat(dataCollections)
+				.containsExactlyInAnyOrder(pastRunning1, pastRunning2, pastReady)
+				.doesNotContain(future1, future2, noDeadline, pastFinished, pastPreparation);
+	}
+
 	@Test
 	public void shouldGetDataCollectionCount() {
 		int numberOfDataCollections = 3;
diff --git a/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java b/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java
index 15c4612b802..bb02c799c3d 100644
--- a/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java
+++ b/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java
@@ -19,6 +19,7 @@
  */
 package org.olat.modules.quality.manager;
 
+import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
@@ -80,7 +81,7 @@ public class QualityTestHelper {
 		evaTestHelper.deleteAll();
 	}
 
-	public QualityDataCollection createDataCollection(String title) {
+	QualityDataCollection createDataCollection(String title) {
 		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
 		QualityDataCollection dataCollection = qualityService.createDataCollection(formEntry);
 		dataCollection.setTitle(title);
@@ -94,6 +95,51 @@ public class QualityTestHelper {
 	QualityDataCollection createDataCollection() {
 		return createDataCollection(UUID.randomUUID().toString());
 	}
+	
+	QualityDataCollection createDataCollectionWithoutValues() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		return qualityService.createDataCollection(formEntry);
+	}
+	
+	QualityDataCollection createDataCollectionWithStartInFuture() {
+		QualityDataCollection dataCollection = createDataCollection();
+		dataCollection.setStart(getDateInFuture());
+		qualityService.updateDataCollection(dataCollection);
+		return dataCollection;
+	}
+	
+	QualityDataCollection createDataCollectionWithStartInPast() {
+		QualityDataCollection dataCollection = createDataCollection();
+		dataCollection.setStart(getDateInPast());
+		qualityService.updateDataCollection(dataCollection);
+		return dataCollection;
+	}
+	
+	QualityDataCollection createDataCollectionWithDeadlineInFuture() {
+		QualityDataCollection dataCollection = createDataCollection();
+		dataCollection.setDeadline(getDateInFuture());
+		qualityService.updateDataCollection(dataCollection);
+		return dataCollection;
+	}
+	
+	QualityDataCollection createDataCollectionWithDeadlineInPast() {
+		QualityDataCollection dataCollection = createDataCollection();
+		dataCollection.setDeadline(getDateInPast());
+		qualityService.updateDataCollection(dataCollection);
+		return dataCollection;
+	}
+
+	private Date getDateInPast() {
+		Calendar calendar = Calendar.getInstance();
+		calendar.add(Calendar.YEAR, -1);
+		return calendar.getTime();
+	}
+
+	private Date getDateInFuture() {
+		Calendar calendar = Calendar.getInstance();
+		calendar.add(Calendar.YEAR, 1);
+		return calendar.getTime();
+	}
 
 	QualityContext createContext() {
 		return qualityContextDao.createContext(createDataCollection(), createParticipation(), QualityContextRole.owner,
@@ -153,11 +199,11 @@ public class QualityTestHelper {
 		return taxonomyService.createTaxonomyLevel(UUID.randomUUID().toString(), "d", "d", null, null, null, taxonomy);
 	}
 
-	public QualityReminder createReminder() {
+	QualityReminder createReminder() {
 		return createReminder(createDataCollection());
 	}
 
-	public QualityReminder createReminder(QualityDataCollectionRef dataCollectionRef) {
+	QualityReminder createReminder(QualityDataCollectionRef dataCollectionRef) {
 		Date sendDate = new Date();
 		QualityReminderType type = QualityReminderType.REMINDER1;
 		return reminderDao.create(dataCollectionRef, sendDate, type);
-- 
GitLab