Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r3adcc4e312e804bf86c3598e3bc744b64a46c21f -rde3706d9ff6d4d4768a805e5f40bd8851634174f --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 3adcc4e312e804bf86c3598e3bc744b64a46c21f) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision de3706d9ff6d4d4768a805e5f40bd8851634174f) @@ -281,16 +281,15 @@ index.emailnotifications.tooltip =Email notifications index.conditions =Conditions index.conditions.tooltip =Conditions of participation in the lesson -index.conditions.flag.tooltip =Participation in the lesson depends on completion of other lessons +index.conditions.flag.tooltip =Participation in the lesson is conditional label.conditions.box.title =Lesson "{0}" will only be available after these lesson(s) are completed: label.conditions.box.no.dependency =There are no dependencies for this lesson. label.conditions.box.add.dependency =Select lesson that will need to be completed first: label.conditions.box.remove.dependency =Remove lesson dependency -label.conditions.box.finish.date =Lesson finishes after {0} days from its start {1} -label.conditions.box.no.finish.date =Lesson does not have a fixed number days to finish -label.days =days +label.conditions.box.finish.global.date =Lesson finishes for everyone after {0} days from its start {1} +label.conditions.box.finish.individual.date =Lesson finishes after {0} days after user started participating +label.conditions.box.finish.no.date =Lesson does not have a fixed number days to finish label.set =Set -error.conditions.box.finish.date.number =Finish date must be a positive number error.conditions.box.finish.date =Error while setting lesson finish date: {0} label.portrait.please.wait =Please wait label.portrait.take.snapshot.from.webcamera =Take portrait using Webcam Index: lams_central/src/java/org/lamsfoundation/lams/web/LessonConditionsAction.java =================================================================== diff -u -r3adcc4e312e804bf86c3598e3bc744b64a46c21f -rde3706d9ff6d4d4768a805e5f40bd8851634174f --- lams_central/src/java/org/lamsfoundation/lams/web/LessonConditionsAction.java (.../LessonConditionsAction.java) (revision 3adcc4e312e804bf86c3598e3bc744b64a46c21f) +++ lams_central/src/java/org/lamsfoundation/lams/web/LessonConditionsAction.java (.../LessonConditionsAction.java) (revision de3706d9ff6d4d4768a805e5f40bd8851634174f) @@ -24,6 +24,7 @@ package org.lamsfoundation.lams.web; import java.util.ArrayList; +import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.List; @@ -44,10 +45,15 @@ import org.joda.time.Period; import org.joda.time.PeriodType; import org.lamsfoundation.lams.index.IndexLessonBean; +import org.lamsfoundation.lams.learningdesign.Group; +import org.lamsfoundation.lams.learningdesign.GroupUser; +import org.lamsfoundation.lams.learningdesign.dao.IGroupUserDAO; +import org.lamsfoundation.lams.lesson.LearnerProgress; import org.lamsfoundation.lams.lesson.Lesson; import org.lamsfoundation.lams.lesson.service.ILessonService; import org.lamsfoundation.lams.monitoring.service.IMonitoringService; import org.lamsfoundation.lams.tool.exception.DataMissingException; +import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.util.CentralConstants; import org.lamsfoundation.lams.util.WebUtil; @@ -74,9 +80,11 @@ private static final String PARAM_AVAILABLE_LESSONS = "availableLessons"; private static final String PARAM_LESSON_START_DATE = "lessonStartDate"; private static final String PARAM_LESSON_DAYS_TO_FINISH = "lessonDaysToFinish"; + private static final String PARAM_INDIVIDUAL_FINISH = "lessonIndividualFinish"; private static ILessonService lessonService; private static IMonitoringService monitoringService; + private static IGroupUserDAO groupUserDAO; /** * Prepares data for thickbox displayed on Index page. @@ -116,14 +124,21 @@ request.setAttribute(LessonConditionsAction.PARAM_AVAILABLE_LESSONS, availableLessons); Date endDate = lesson.getScheduleEndDate(); - if (endDate != null) { + if (endDate == null) { + Integer daysToLessonFinish = lesson.getScheduledNumberDaysToLessonFinish(); + if (daysToLessonFinish != null) { + request.setAttribute(LessonConditionsAction.PARAM_LESSON_DAYS_TO_FINISH, daysToLessonFinish); + request.setAttribute(LessonConditionsAction.PARAM_INDIVIDUAL_FINISH, true); + } + } else { Date startDate = (lesson.getStartDateTime() == null) ? lesson.getScheduleStartDate() : lesson .getStartDateTime(); Interval interval = new Interval(startDate.getTime(), endDate.getTime()); Period daysToLessonFinish = interval.toPeriod(PeriodType.days()); request.setAttribute(LessonConditionsAction.PARAM_LESSON_START_DATE, startDate); request.setAttribute(LessonConditionsAction.PARAM_LESSON_DAYS_TO_FINISH, daysToLessonFinish.getDays()); + request.setAttribute(LessonConditionsAction.PARAM_INDIVIDUAL_FINISH, false); } request.setAttribute(CentralConstants.PARAM_EDIT, canEdit(request, lesson)); @@ -179,38 +194,77 @@ } /** - * Sets a new lesson scheduled finish date, based on give number of days. + * Sets lesson finish date, either for individuals or lesson as a whole. If days<=0, schedule is removed. */ public ActionForward setDaysToLessonFinish(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { Long lessonId = WebUtil.readLongParam(request, CentralConstants.PARAM_LESSON_ID, false); - Integer daysToLessonFinish = null; + int daysToLessonFinish = WebUtil.readIntParam(request, LessonConditionsAction.PARAM_LESSON_DAYS_TO_FINISH, + false); + boolean individualFinish = WebUtil.readBooleanParam(request, LessonConditionsAction.PARAM_INDIVIDUAL_FINISH, + false); ActionMessages errors = new ActionMessages(); + + Lesson lesson = getLessonAndCheckPermissions(request, lessonId); + HttpSession session = SessionManager.getSession(); + UserDTO currentUser = (UserDTO) session.getAttribute(AttributeNames.USER); try { - daysToLessonFinish = WebUtil.readIntParam(request, LessonConditionsAction.PARAM_LESSON_DAYS_TO_FINISH, - false); - if (daysToLessonFinish <= 0) { - throw new IllegalArgumentException("Number of days to lesson finish is a nonpositive number"); + // (re)schedule the lesson or remove it when set for individual finish of daysToLessonFinish=0 + getMonitoringService().finishLessonOnSchedule(lessonId, individualFinish ? 0 : daysToLessonFinish, + currentUser.getUserID()); + // if finish date is individual, the field below is filled + lesson.setScheduledNumberDaysToLessonFinish(individualFinish && (daysToLessonFinish > 0) ? daysToLessonFinish + : null); + + // iterate through each GroupLearner and set/remove individual lesson finish date, if needed + Group learnerGroup = lesson.getLessonClass().getLearnersGroup(); + if (learnerGroup != null) { + for (User learner : (Set) learnerGroup.getUsers()) { + GroupUser groupUser = getGroupUserDAO().getGroupUser(lesson, learner.getUserId()); + if (groupUser != null) { + + if (individualFinish && (daysToLessonFinish > 0)) { + // set individual finish date based on start date + LearnerProgress learnerProgress = getLessonService().getUserProgressForLesson( + learner.getUserId(), lessonId); + if ((learnerProgress == null) || (learnerProgress.getStartDate() == null)) { + if (groupUser.getScheduledLessonEndDate() != null) { + LessonConditionsAction.logger.warn("Improper DB value: User with ID " + + learner.getUserId() + + " has scheduledLessonEndDate set but has not started the lesson yet."); + } + } else { + // set new finish date according to moment when user joined the lesson + Calendar calendar = Calendar.getInstance(); + calendar.setTime(learnerProgress.getStartDate()); + calendar.add(Calendar.DATE, daysToLessonFinish); + Date endDate = calendar.getTime(); + groupUser.setScheduledLessonEndDate(endDate); + + if (LessonConditionsAction.logger.isDebugEnabled()) { + LessonConditionsAction.logger.debug("Reset time limit for user: " + + learner.getLogin() + " in lesson: " + lesson.getLessonId() + " to " + + endDate); + } + } + } else if (groupUser.getScheduledLessonEndDate() != null) { + // remove individual finish date + groupUser.setScheduledLessonEndDate(null); + + if (LessonConditionsAction.logger.isDebugEnabled()) { + LessonConditionsAction.logger.debug("Remove time limit for user: " + learner.getLogin() + + " in lesson: " + lesson.getLessonId()); + } + } + } + } } - } catch (IllegalArgumentException e) { + } catch (Exception e) { LessonConditionsAction.logger.error(e); - errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.conditions.box.finish.date.number")); + errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.conditions.box.finish.date", + new Object[] { e.getMessage() })); } - if (daysToLessonFinish != null) { - Lesson lesson = getLessonAndCheckPermissions(request, lessonId); - HttpSession session = SessionManager.getSession(); - UserDTO currentUser = (UserDTO) session.getAttribute(AttributeNames.USER); - try { - // reschedule the lesson - getMonitoringService().finishLessonOnSchedule(lessonId, daysToLessonFinish, currentUser.getUserID()); - } catch (IllegalArgumentException e) { - LessonConditionsAction.logger.error(e); - errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.conditions.box.finish.date", - new Object[] { e.getMessage() })); - } - } - if (!errors.isEmpty()) { saveErrors(request, errors); } @@ -259,4 +313,13 @@ } return LessonConditionsAction.monitoringService; } -} + + private IGroupUserDAO getGroupUserDAO() { + if (LessonConditionsAction.groupUserDAO == null) { + WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() + .getServletContext()); + LessonConditionsAction.groupUserDAO = (IGroupUserDAO) ctx.getBean("groupUserDAO"); + } + return LessonConditionsAction.groupUserDAO; + } +} \ No newline at end of file Index: lams_central/web/groupContents.jsp =================================================================== diff -u -r3adcc4e312e804bf86c3598e3bc744b64a46c21f -rde3706d9ff6d4d4768a805e5f40bd8851634174f --- lams_central/web/groupContents.jsp (.../groupContents.jsp) (revision 3adcc4e312e804bf86c3598e3bc744b64a46c21f) +++ lams_central/web/groupContents.jsp (.../groupContents.jsp) (revision de3706d9ff6d4d4768a805e5f40bd8851634174f) @@ -28,7 +28,7 @@ " >  " >  " >  - " >  + " >  @@ -84,6 +84,7 @@ " >  " >  " >  + " >  Index: lams_central/web/indexLessonConditions.jsp =================================================================== diff -u -r3adcc4e312e804bf86c3598e3bc744b64a46c21f -rde3706d9ff6d4d4768a805e5f40bd8851634174f --- lams_central/web/indexLessonConditions.jsp (.../indexLessonConditions.jsp) (revision 3adcc4e312e804bf86c3598e3bc744b64a46c21f) +++ lams_central/web/indexLessonConditions.jsp (.../indexLessonConditions.jsp) (revision de3706d9ff6d4d4768a805e5f40bd8851634174f) @@ -60,7 +60,7 @@ - + - + + +
- + + + +

@@ -97,6 +97,7 @@ +

@@ -127,21 +128,27 @@ " />

- + + + + ${lessonDaysToFinish} + + - + ${lessonDaysToFinish} @@ -151,25 +158,35 @@

- - - value="30" - - - value="${lessonDaysToFinish}" - - - /> - + + + " />
+ + checked="checked" + + /> +
Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/Lesson.hbm.xml =================================================================== diff -u -rc989378786b026ab4d78cd8064eaa6d7e63c4db6 -rde3706d9ff6d4d4768a805e5f40bd8851634174f --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/Lesson.hbm.xml (.../Lesson.hbm.xml) (revision c989378786b026ab4d78cd8064eaa6d7e63c4db6) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/Lesson.hbm.xml (.../Lesson.hbm.xml) (revision de3706d9ff6d4d4768a805e5f40bd8851634174f) @@ -274,8 +274,10 @@ + SELECT l.lesson_id, l.name, l.description, l.lesson_state_id, lp.lesson_completed_flag, l.enable_lesson_notifications, - (SELECT TRUE FROM lams_lesson_dependency ld WHERE ld.lesson_id = l.lesson_id LIMIT 1) AS dependent + (SELECT TRUE FROM lams_lesson_dependency ld WHERE ld.lesson_id = l.lesson_id LIMIT 1) AS dependent, + l.schedule_end_date_time IS NOT NULL OR l.scheduled_number_days_to_lesson_finish IS NOT NULL AS scheduledFinish FROM (lams_lesson l, lams_learning_design ld, lams_group g, lams_user_group ug, lams_grouping gi) LEFT JOIN lams_learner_progress lp ON lp.user_id = ug.user_id AND lp.lesson_id = l.lesson_id WHERE l.learning_design_id = ld.learning_design_id @@ -297,8 +299,10 @@ + SELECT l.lesson_id, l.name, l.description, l.lesson_state_id, lp.lesson_completed_flag, l.enable_lesson_notifications, - (SELECT TRUE FROM lams_lesson_dependency ld WHERE ld.lesson_id = l.lesson_id LIMIT 1) AS dependent + (SELECT TRUE FROM lams_lesson_dependency ld WHERE ld.lesson_id = l.lesson_id LIMIT 1) AS dependent, + l.schedule_end_date_time IS NOT NULL OR l.scheduled_number_days_to_lesson_finish IS NOT NULL AS scheduledFinish FROM (lams_lesson l, lams_learning_design ld, lams_group g, lams_user_group ug, lams_grouping gi) LEFT JOIN lams_learner_progress lp ON lp.user_id = ug.user_id AND lp.lesson_id = l.lesson_id WHERE l.learning_design_id = ld.learning_design_id @@ -320,8 +324,10 @@ + SELECT l.lesson_id, l.name, l.description, l.lesson_state_id, lp.lesson_completed_flag, l.enable_lesson_notifications, - (SELECT TRUE FROM lams_lesson_dependency ld WHERE ld.lesson_id = l.lesson_id LIMIT 1) AS dependent + (SELECT TRUE FROM lams_lesson_dependency ld WHERE ld.lesson_id = l.lesson_id LIMIT 1) AS dependent, + l.schedule_end_date_time IS NOT NULL OR l.scheduled_number_days_to_lesson_finish IS NOT NULL AS scheduledFinish FROM (lams_lesson l, lams_learning_design ld) LEFT JOIN lams_learner_progress lp ON lp.user_id = :userId AND lp.lesson_id = l.lesson_id WHERE l.learning_design_id = ld.learning_design_id Index: lams_common/src/java/org/lamsfoundation/lams/index/IndexLessonBean.java =================================================================== diff -u -rc989378786b026ab4d78cd8064eaa6d7e63c4db6 -rde3706d9ff6d4d4768a805e5f40bd8851634174f --- lams_common/src/java/org/lamsfoundation/lams/index/IndexLessonBean.java (.../IndexLessonBean.java) (revision c989378786b026ab4d78cd8064eaa6d7e63c4db6) +++ lams_common/src/java/org/lamsfoundation/lams/index/IndexLessonBean.java (.../IndexLessonBean.java) (revision de3706d9ff6d4d4768a805e5f40bd8851634174f) @@ -44,6 +44,7 @@ private boolean completed; private boolean enableLessonNotifications; private boolean dependent; + private boolean scheduledFinish; private List links; public IndexLessonBean(Long id, String name) { @@ -56,14 +57,16 @@ this.url = url; } - public IndexLessonBean(Long id, String name, String description, Integer state, boolean completed, boolean enableLessonNotifications, boolean dependent) { + public IndexLessonBean(Long id, String name, String description, Integer state, boolean completed, + boolean enableLessonNotifications, boolean dependent, boolean scheduledFinish) { this.id = id; this.name = name; this.description = description; this.state = state; this.completed = completed; this.enableLessonNotifications = enableLessonNotifications; this.dependent = dependent; + this.scheduledFinish = scheduledFinish; } public IndexLessonBean(Long id, String name, String description, String url, Integer state, boolean completed, @@ -166,4 +169,12 @@ public void setDependent(boolean hasDependencies) { this.dependent = hasDependencies; } + + public boolean isScheduledFinish() { + return scheduledFinish; + } + + public void setScheduledFinish(boolean scheduledFinish) { + this.scheduledFinish = scheduledFinish; + } } Index: lams_common/src/java/org/lamsfoundation/lams/lesson/service/LessonService.java =================================================================== diff -u -rc989378786b026ab4d78cd8064eaa6d7e63c4db6 -rde3706d9ff6d4d4768a805e5f40bd8851634174f --- lams_common/src/java/org/lamsfoundation/lams/lesson/service/LessonService.java (.../LessonService.java) (revision c989378786b026ab4d78cd8064eaa6d7e63c4db6) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/service/LessonService.java (.../LessonService.java) (revision de3706d9ff6d4d4768a805e5f40bd8851634174f) @@ -779,8 +779,9 @@ enableLessonNotifications = enableLessonNotifications == null ? false : enableLessonNotifications.booleanValue(); Boolean dependent = (Boolean) tuple[6]; dependent = dependent == null ? false : dependent.booleanValue(); + Boolean scheduledFinish = (Boolean) tuple[7]; IndexLessonBean bean = new IndexLessonBean(lessonId, lessonName, lessonDescription, lessonState, - lessonCompleted, enableLessonNotifications, dependent); + lessonCompleted, enableLessonNotifications, dependent, scheduledFinish); map.put(new Long(lessonId), bean); } } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r3adcc4e312e804bf86c3598e3bc744b64a46c21f -rde3706d9ff6d4d4768a805e5f40bd8851634174f --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 3adcc4e312e804bf86c3598e3bc744b64a46c21f) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision de3706d9ff6d4d4768a805e5f40bd8851634174f) @@ -803,62 +803,80 @@ } checkOwnerOrStaffMember(userId, requestedLesson, "finish lesson on schedule"); - //calculate finish date - Date startDate = (requestedLesson.getStartDateTime() != null) ? requestedLesson.getStartDateTime() : requestedLesson.getScheduleStartDate(); - if (startDate == null) { - throw new MonitoringServiceException("Lesson with id=" + lessonId + " neither has been started nor scheduled to start. Unable to schedule lesson's finish."); - } - Calendar calendar = Calendar.getInstance(); - calendar.setTime(startDate); - calendar.add(Calendar.DATE, scheduledNumberDaysToLessonFinish); - Date endDate = calendar.getTime(); - - if (endDate.before(new Date())) { - throw new IllegalArgumentException("Lesson scheduled finish date is already in the past"); - } - - JobDetail finishLessonJob = null; - String finishLessonJobName = "finishLessonOnSchedule:" + lessonId; String triggerName = "finishLessonOnScheduleTrigger:" + lessonId; boolean alreadyScheduled = false; - try { - // if trigger exists, the job was already scheduled and we need to move the trigger + // if trigger exists, the job was already scheduled and we need to (re)move the trigger alreadyScheduled = scheduler.getTrigger(triggerName, Scheduler.DEFAULT_GROUP) != null; } catch (SchedulerException e) { log.error(e); } - - if (!alreadyScheduled) { - finishLessonJob = getFinishScheduleLessonJob(); - // setup the message for scheduling job - finishLessonJob.setName(finishLessonJobName); - finishLessonJob.setDescription(requestedLesson.getLessonName() + ":" - + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser().getFullName())); - finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)); - finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_USER_ID, new Integer(userId)); + + Trigger finishLessonTrigger = null; + String finishLessonJobName = "finishLessonOnSchedule:" + lessonId; + JobDetail finishLessonJob = null; + Date endDate = null; + + if (scheduledNumberDaysToLessonFinish > 0) { + // calculate finish date + Date startDate = (requestedLesson.getStartDateTime() != null) ? requestedLesson.getStartDateTime() + : requestedLesson.getScheduleStartDate(); + if (startDate == null) { + throw new MonitoringServiceException("Lesson with id=" + lessonId + + " neither has been started nor scheduled to start. Unable to schedule lesson's finish."); + } + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startDate); + calendar.add(Calendar.DATE, scheduledNumberDaysToLessonFinish); + endDate = calendar.getTime(); + + if (endDate.before(new Date())) { + throw new IllegalArgumentException("Lesson scheduled finish date is already in the past"); + } + + if (!alreadyScheduled) { + finishLessonJob = getFinishScheduleLessonJob(); + // setup the message for scheduling job + finishLessonJob.setName(finishLessonJobName); + finishLessonJob.setDescription(requestedLesson.getLessonName() + ":" + + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser().getFullName())); + finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)); + finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_USER_ID, new Integer(userId)); + } + + // create customized triggers + finishLessonTrigger = new SimpleTrigger(triggerName, Scheduler.DEFAULT_GROUP, endDate); + finishLessonTrigger.setJobName(finishLessonJobName); } - // create customized triggers - Trigger finishLessonTrigger = new SimpleTrigger(triggerName, Scheduler.DEFAULT_GROUP, endDate); - finishLessonTrigger.setJobName(finishLessonJobName); // start the scheduling job try { requestedLesson.setScheduleEndDate(endDate); lessonDAO.updateLesson(requestedLesson); if (alreadyScheduled) { - scheduler.rescheduleJob(triggerName, Scheduler.DEFAULT_GROUP, finishLessonTrigger); - } else { + if (scheduledNumberDaysToLessonFinish > 0) { + scheduler.rescheduleJob(triggerName, Scheduler.DEFAULT_GROUP, finishLessonTrigger); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Finish lesson [" + lessonId + "] job has been rescheduled to " + + endDate); + } + } else { + scheduler.deleteJob(finishLessonJobName, Scheduler.DEFAULT_GROUP); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Finish lesson [" + lessonId + "] job has been removed"); + } + } + } else if (scheduledNumberDaysToLessonFinish > 0) { scheduler.scheduleJob(finishLessonJob, finishLessonTrigger); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Finish lesson [" + lessonId + "] job has been scheduled to " + + endDate); + } } } catch (SchedulerException e) { throw new MonitoringServiceException("Error occurred at " + "[finishLessonOnSchedule]- fail to start scheduling", e); } - - if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("Finish lesson [" + lessonId + "] on schedule is configured"); - } } /**