Index: lams_common/src/java/org/lamsfoundation/lams/events/Event.java =================================================================== diff -u -r5773f84ed608838de3521ecde87c52f3c72d478c -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_common/src/java/org/lamsfoundation/lams/events/Event.java (.../Event.java) (revision 5773f84ed608838de3521ecde87c52f3c72d478c) +++ lams_common/src/java/org/lamsfoundation/lams/events/Event.java (.../Event.java) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -68,7 +68,7 @@ } /** - * Standard constructor used by EventNotificationService. + * Basic constructor used by EventNotificationService. * * @param scope * scope of the event @@ -99,9 +99,42 @@ this.htmlFormat = isHtmlContentType; } + /** + * Constructor used by EventNotificationService when the event is for resending (after an initial send now failure) + * + * @param scope + * scope of the event + * @param name + * name of the event + * @param eventSessionId + * session ID of the event + * @param defaultSubject + * subject of the message to send + * @param defaultMessage + * body of the message to send + * @param failTime + * @throws InvalidParameterException + * if scope is null or name is blank + */ + public Event(String scope, String name, Long sessionId, String subject, String message, boolean isHtmlContentType, Date failTime) throws InvalidParameterException { + if (scope == null) { + throw new InvalidParameterException("Event scope can not be null."); + } + if (StringUtils.isBlank(name)) { + throw new InvalidParameterException("Event name can not be blank."); + } + this.scope = scope; + this.name = name; + this.eventSessionId = sessionId; + this.subject = subject; + this.message = message; + this.htmlFormat = isHtmlContentType; + this.failTime = failTime; + } + @Override public Object clone() { - return new Event(scope, name, eventSessionId, subject, message, htmlFormat); + return new Event(scope, name, eventSessionId, subject, message, htmlFormat, failTime); } /** Index: lams_common/src/java/org/lamsfoundation/lams/events/EventNotificationService.java =================================================================== diff -u -r6588eb48d4acb2b8ce9dd74e76a7ddbaeab640a0 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_common/src/java/org/lamsfoundation/lams/events/EventNotificationService.java (.../EventNotificationService.java) (revision 6588eb48d4acb2b8ce9dd74e76a7ddbaeab640a0) +++ lams_common/src/java/org/lamsfoundation/lams/events/EventNotificationService.java (.../EventNotificationService.java) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -5,6 +5,7 @@ import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; @@ -114,6 +115,23 @@ } @Override + public void notifyLessonMonitors(Long lessonId, String subject, String message, boolean isHtmlFormat) { + Map monitoringUsers = lessonService.getUsersWithLessonParticipation(lessonId, "MONITOR", null, null, null, true); + if (monitoringUsers.isEmpty()) { + return; + } + + ArrayList monitoringUsersIds = new ArrayList(); + for ( Map.Entry entry : monitoringUsers.entrySet() ) { + if ( entry.getValue() ) + monitoringUsersIds.add(entry.getKey().getUserId()); + } + + sendMessage(null, monitoringUsersIds.toArray(new Integer[monitoringUsersIds.size()]), IEventNotificationService.DELIVERY_METHOD_MAIL, subject, message, + isHtmlFormat); + } + + @Override public void notifyLessonMonitors(Long sessionId, String message, boolean isHtmlFormat) { List monitoringUsers = lessonService.getMonitorsByToolSessionId(sessionId); if (monitoringUsers.isEmpty()) { @@ -159,6 +177,7 @@ * whether the message is of HTML content-type or plain text */ public void notifyUser(Subscription subscription, String subject, String message, boolean isHtmlFormat) { + log.debug("EventNotificationService notifyUser "+this.toString()); subscription.setLastOperationMessage( subscription.getDeliveryMethod().send(null, subscription.getUserId(), subject, message, isHtmlFormat)); } @@ -200,9 +219,8 @@ EventNotificationService.log.error("Error occured while sending message: " + result); Event event = new Event(IEventNotificationService.SINGLE_MESSAGE_SCOPE, - String.valueOf(System.currentTimeMillis()), null, subject, message, isHtmlFormat); + String.valueOf(System.currentTimeMillis()), null, subject, message, isHtmlFormat, new Date()); subscribe(event, toUserId, deliveryMethod); - event.setFailTime(new Date()); eventDAO.insertOrUpdate(event); return false; } @@ -221,20 +239,16 @@ new Thread(() -> { try { HibernateSessionManager.openSession(); - - Event event = null; for (Integer id : toUserIds) { String result = deliveryMethod.send(fromUserId, id, subject, message, isHtmlFormat); if (result != null) { - event = new Event(IEventNotificationService.SINGLE_MESSAGE_SCOPE, - String.valueOf(System.currentTimeMillis()), null, subject, message, isHtmlFormat); + Event event = new Event(IEventNotificationService.SINGLE_MESSAGE_SCOPE, + String.valueOf(System.currentTimeMillis()), null, subject, message, + isHtmlFormat, new Date()); subscribe(event, id, deliveryMethod); + log.debug("Set up new event "+event.getUid()+":"+event.getName()+" number of subscriptions "+event.getSubscriptions().size()); } } - if (event != null) { - event.setFailTime(new Date()); - eventDAO.insertOrUpdate(event); - } } finally { HibernateSessionManager.closeSession(); } @@ -341,7 +355,7 @@ while (subscriptionIterator.hasNext()) { Subscription subscription = subscriptionIterator.next(); notifyUser(subscription, subjectToSend, messageToSend, event.isHtmlFormat()); - if (subscription.getDeliveryMethod().lastOperationFailed(subscription)) { + if (! subscription.getDeliveryMethod().lastOperationFailed(subscription)) { if (event.getFailTime() != null) { subscriptionIterator.remove(); } @@ -353,6 +367,7 @@ } } if (event.getSubscriptions().isEmpty()) { + log.debug("Deleting event "+event.getUid()+" "+event.getFailTime()); eventDAO.delete(event); } else { eventDAO.insertOrUpdate(event); Index: lams_common/src/java/org/lamsfoundation/lams/events/IEventNotificationService.java =================================================================== diff -u -r5773f84ed608838de3521ecde87c52f3c72d478c -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_common/src/java/org/lamsfoundation/lams/events/IEventNotificationService.java (.../IEventNotificationService.java) (revision 5773f84ed608838de3521ecde87c52f3c72d478c) +++ lams_common/src/java/org/lamsfoundation/lams/events/IEventNotificationService.java (.../IEventNotificationService.java) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -127,6 +127,19 @@ */ void notifyLessonMonitors(Long sessionId, String message, boolean isHtmlFormat); + /** + * Notify lesson monitors with the specified message and subject + * + * @param lessonId + * tool session to which monitors belong + * @param subject + * subject to be sent + * @param message + * message to be sent + * @isHtmlFormat whether email is required to of HTML format + */ + void notifyLessonMonitors(Long lessonId, String subject, String message, boolean isHtmlFormat); + void resendMessages(); /** Index: lams_common/src/java/org/lamsfoundation/lams/util/hibernate/HibernateSessionManager.java =================================================================== diff -u -r96f6a69a4c0be7e1c083ba981bd3c6e04a4afcc7 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_common/src/java/org/lamsfoundation/lams/util/hibernate/HibernateSessionManager.java (.../HibernateSessionManager.java) (revision 96f6a69a4c0be7e1c083ba981bd3c6e04a4afcc7) +++ lams_common/src/java/org/lamsfoundation/lams/util/hibernate/HibernateSessionManager.java (.../HibernateSessionManager.java) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -1,5 +1,6 @@ package org.lamsfoundation.lams.util.hibernate; +import org.apache.log4j.Logger; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.context.internal.ManagedSessionContext; @@ -17,6 +18,7 @@ public class HibernateSessionManager { private static SessionFactory sessionFactory; + // private static Logger log = Logger.getLogger(HibernateSessionManager.class); /** * Makes sure that an open Hibernate session is bound to current thread. */ @@ -31,19 +33,25 @@ TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory); session = sessionFactory.getCurrentSession(); } +// log.debug("Opened new session "+session.toString()); +// } else { +// log.debug("Bound to open session "+session.toString()); +// } // binding to Context is not enough // an open session needs to be also manually bound to current thread SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); if (sessionHolder == null) { sessionHolder = new SessionHolder(session); TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder); +// log.debug("Linked to transaction "+session.getTransaction()); } } public static void closeSession() { Session session = HibernateSessionManager.getSessionFactory().getCurrentSession(); if (session.isOpen()) { +// log.debug("Closing session transaction "+session.getTransaction()); session.close(); } } Index: lams_monitoring/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rb67f087068e5b478fc4e3205dc308b470195fe42 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision b67f087068e5b478fc4e3205dc308b470195fe42) +++ lams_monitoring/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -375,5 +375,33 @@ tour.gradebook.show.dates.content=Show/Hide some of the date columns in the grids to make it easier to read. tour.gradebook.marks.chart=Marks Chart tour.histogram.help.content=Click on a bar in the graph and then use your mouse wheel to zoom in and out of the graph. Once you zoom in, the grey selection in the bottom graph can be dragged left or right to show a different set of marks. - +lesson.progress.email=Progress Email +progress.email.send=Send Email Now +progress.email.configure=Automatic Email Settings +progress.email.subject=Lesson Progress: {0} +progress.email.sent.success=[0] email(s) sent. +export.dateheader =Exported on: +progress.email.send.now.question=Send the current user progress by email to yourself only now? +progress.email.send.failed=Some problems occurred while sending emails. Please, contact your system administrator. +progress.email.select.date.first=Please select a date first. +progress.email.enter.two.dates.first=Please add your first two dates and then a list can be generated. +progress.email.would.you.like.to.generate=Would you like to generate a list of dates based on your first two dates? You can then select from the list. +progress.email.how.many.dates.to.generate=How many dates would you like generated? +progress.email.will.be.sent.on=Emails will be sent on: +progress.email.add.date=Add Date +progress.email.generate.date.list=Generate Date List +progress.email.select.date=Select Date: +progress.email.title=Email Progress Report +progress.email.heading.number.learners=# of Learners +progress.email.heading.overall.progress=Overall learner's progress +label.gate.title=Gate +label.unknown=Unknown +progress.email.sent.automatically=Email sent by automatically by LAMS on scheduled request +label.activity=Activity +label.status=Status +tour.progress.email.send.title=Send Learner Progress Email +tour.progress.email.send.content=Send an email with a summary of the learner progress to yourself immediately. +tour.progress.email.configure.title=Configure Learner Progress Email Sending +tour.progress.email.confure.content=Set up the dates to automatically send emails with a summary of learner progress to all monitors for this lesson. +error.date.in.past=Selected date is in the past. #======= End labels: Exported 352 labels for en AU ===== Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/dto/EmailProgressActivityDTO.java =================================================================== diff -u --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/dto/EmailProgressActivityDTO.java (revision 0) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/dto/EmailProgressActivityDTO.java (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -0,0 +1,60 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +package org.lamsfoundation.lams.monitoring.dto; + +import org.lamsfoundation.lams.learningdesign.Activity; + +/** + * DTO used to return the activity details needed for the learner progress email + * + * @author Fiona Malikoff + */ +public class EmailProgressActivityDTO { + + private Activity activity; + private Integer numberOfLearners; + private int depth; + + public EmailProgressActivityDTO(Activity activity, int depth, Integer numberOfUsers) { + this.activity = activity; + this.depth = depth; + this.numberOfLearners = numberOfUsers; + } + + /** + * @return Returns the activity + */ + public Activity getActivity() { + return activity; + } + + public Integer getNumberOfLearners() { + return numberOfLearners; + } + + public int getDepth() { + return depth; + } + +} Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/monitoringApplicationContext.xml =================================================================== diff -u -rbcf87555c8215ef22b2541145c4f0ae5b3e29592 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/monitoringApplicationContext.xml (.../monitoringApplicationContext.xml) (revision bcf87555c8215ef22b2541145c4f0ae5b3e29592) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/monitoringApplicationContext.xml (.../monitoringApplicationContext.xml) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -78,6 +78,8 @@ PROPAGATION_REQUIRED PROPAGATION_REQUIRED PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/EmailProgressMessageJob.java =================================================================== diff -u --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/EmailProgressMessageJob.java (revision 0) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/EmailProgressMessageJob.java (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -0,0 +1,52 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +package org.lamsfoundation.lams.monitoring.quartz.job; + +import java.util.Map; + +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.web.util.AttributeNames; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * Email messages with the current learner progress at the specified date to all the monitors in a lesson + */ +public class EmailProgressMessageJob extends MonitoringJob { + private static Logger log = Logger.getLogger(EmailProgressMessageJob.class); + + @SuppressWarnings("rawtypes") + @Override + protected void executeInternal(JobExecutionContext context) throws JobExecutionException { + + Map properties = context.getJobDetail().getJobDataMap(); + Long lessonId = (Long) properties.get(AttributeNames.PARAM_LESSON_ID); + if (log.isDebugEnabled()) { + log.debug("Lesson [" + lessonId + "] sending progress email"); + } + + String[] parts = getMonitoringService(context).generateLessonProgressEmail(lessonId,null); + log.debug("EmailProgressMessageJob "+getEventNotificationService(context)); + getEventNotificationService(context).notifyLessonMonitors(lessonId, parts[0], parts[1], true); + } +} \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/EmailScheduleMessageJob.java =================================================================== diff -u -r5e319c889c9776238ecaf338e3b2d330b2186914 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/EmailScheduleMessageJob.java (.../EmailScheduleMessageJob.java) (revision 5e319c889c9776238ecaf338e3b2d330b2186914) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/EmailScheduleMessageJob.java (.../EmailScheduleMessageJob.java) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -33,9 +33,6 @@ import org.lamsfoundation.lams.web.util.AttributeNames; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; -import org.quartz.SchedulerContext; -import org.quartz.SchedulerException; -import org.springframework.context.ApplicationContext; /** * Email messages at the specified date. List of recipients is being constructed real time based on specified search @@ -83,16 +80,4 @@ } } - private IEventNotificationService getEventNotificationService(JobExecutionContext context) - throws JobExecutionException { - try { - final String CONTEXT_NAME = "context.central"; - - SchedulerContext sc = context.getScheduler().getContext(); - ApplicationContext cxt = (ApplicationContext) sc.get(CONTEXT_NAME); - return (IEventNotificationService) cxt.getBean("eventNotificationService"); - } catch (SchedulerException e) { - throw new JobExecutionException("Failed look up the Scheduler" + e.toString()); - } - } } \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/MonitoringJob.java =================================================================== diff -u -r3399163940c61c9132223c758d274486e57ff9b7 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/MonitoringJob.java (.../MonitoringJob.java) (revision 3399163940c61c9132223c758d274486e57ff9b7) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/quartz/job/MonitoringJob.java (.../MonitoringJob.java) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -24,6 +24,7 @@ package org.lamsfoundation.lams.monitoring.quartz.job; +import org.lamsfoundation.lams.events.IEventNotificationService; import org.lamsfoundation.lams.monitoring.service.IMonitoringService; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; @@ -33,23 +34,31 @@ import org.springframework.scheduling.quartz.QuartzJobBean; /** - * All Quartz Job Bean super classes in monitoring. It provides a simple helper method to get monitoringService. - * + * All Quartz Job Bean super classes in monitoring. It provides a simple helper methods to get monitoringService, eventNotificationService. * @author Steve.Ni - * - * @version $Revision$ */ public abstract class MonitoringJob extends QuartzJobBean { private static final String CONTEXT_NAME = "context.central"; - private static final String SERVICE_NAME = "monitoringService"; + private static final String MONITORING_SERVICE_NAME = "monitoringService"; + private static final String EVENT_SERVICE_NAME = "eventNotificationService"; - protected IMonitoringService getMonitoringService(JobExecutionContext context) throws JobExecutionException { + protected Object getService(JobExecutionContext context, String serviceName) throws JobExecutionException { try { SchedulerContext sc = context.getScheduler().getContext(); ApplicationContext cxt = (ApplicationContext) sc.get(MonitoringJob.CONTEXT_NAME); - return (IMonitoringService) cxt.getBean(MonitoringJob.SERVICE_NAME); + return cxt.getBean(serviceName); } catch (SchedulerException e) { - throw new JobExecutionException("Failed look up the Scheduler" + e.toString()); + throw new JobExecutionException("Failed look up the " + serviceName + " " + e.toString()); } } + + protected IMonitoringService getMonitoringService(JobExecutionContext context) throws JobExecutionException { + return (IMonitoringService) getService(context, MonitoringJob.MONITORING_SERVICE_NAME); + } + + protected IEventNotificationService getEventNotificationService(JobExecutionContext context) + throws JobExecutionException { + return (IEventNotificationService) getService(context, MonitoringJob.EVENT_SERVICE_NAME); + } + } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/EmailProgressActivitiesProcessor.java =================================================================== diff -u --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/EmailProgressActivitiesProcessor.java (revision 0) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/EmailProgressActivitiesProcessor.java (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -0,0 +1,107 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + + +package org.lamsfoundation.lams.monitoring.service; + +import java.util.Map; +import java.util.Vector; + +import org.apache.commons.collections.ArrayStack; +import org.lamsfoundation.lams.learningdesign.Activity; +import org.lamsfoundation.lams.learningdesign.ComplexActivity; +import org.lamsfoundation.lams.learningdesign.LearningDesign; +import org.lamsfoundation.lams.learningdesign.LearningDesignProcessor; +import org.lamsfoundation.lams.learningdesign.SimpleActivity; +import org.lamsfoundation.lams.learningdesign.dao.IActivityDAO; +import org.lamsfoundation.lams.learningdesign.exception.LearningDesignProcessorException; +import org.lamsfoundation.lams.monitoring.dto.EmailProgressActivityDTO; + +/** + * Generate a hierarchy of activities to report the learner progress in an email. + * + * Note: The collections used by this class are not synchronized. + * + * @author Fiona Malikoff + */ +public class EmailProgressActivitiesProcessor extends LearningDesignProcessor { + + Vector activityList; + ArrayStack activityListStack; + Vector currentActivityList; + Map numberOfUsersInActivity; + int depth; + + public EmailProgressActivitiesProcessor(LearningDesign design, IActivityDAO activityDAO, Map numberOfUsersInActivity) { + super(design, activityDAO); + this.numberOfUsersInActivity = numberOfUsersInActivity; + this.activityList = new Vector(); + this.activityListStack = new ArrayStack(5); + this.currentActivityList = activityList; + depth=0; + } + + public Vector getActivityList() { + return activityList; + } + + @Override + public boolean startComplexActivity(ComplexActivity activity) { + + Integer numLearners = numberOfUsersInActivity.get(activity.getActivityId()); + currentActivityList.add(new EmailProgressActivityDTO(activity, depth, numLearners != null ? numLearners : 0)); + + if ( activity.isParallelActivity() ) { + return false; + } else { + // Create a new current activity list, putting the old current one on the stack. + activityListStack.push(currentActivityList); + currentActivityList = new Vector(); + depth++; + return true; + } + } + + @SuppressWarnings("unchecked") + @Override + public void endComplexActivity(ComplexActivity activity) { + if (!activity.isParallelActivity()) { + Vector topActivityList = (Vector) activityListStack + .pop(); + topActivityList.addAll(currentActivityList); + currentActivityList = topActivityList; + depth--; + } + } + + @Override + public void startSimpleActivity(SimpleActivity activity) { + Integer numLearners = numberOfUsersInActivity.get(activity.getActivityId()); + currentActivityList.add(new EmailProgressActivityDTO(activity, depth, numLearners != null ? numLearners : 0)); + } + + @Override + public void endSimpleActivity(SimpleActivity activity) throws LearningDesignProcessorException { + // nothing to do - everything done by the start + } +} \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java =================================================================== diff -u -r8daec7e047f7ea8404c6f16218b63a8e855a15b2 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision 8daec7e047f7ea8404c6f16218b63a8e855a15b2) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -651,6 +651,10 @@ void removeLearnerContent(Long lessonId, Integer learnerId); + + /** Generate an email containing the progress details for individual activities in a lesson. + * @return String[] {subject, email body} */ + String[] generateLessonProgressEmail(Long lessonId, Integer userId); /** * Get list of users who completed the given lesson. Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r8daec7e047f7ea8404c6f16218b63a8e855a15b2 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 8daec7e047f7ea8404c6f16218b63a8e855a15b2) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -35,6 +35,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.SortedSet; @@ -84,6 +85,7 @@ import org.lamsfoundation.lams.logevent.service.ILogEventService; import org.lamsfoundation.lams.monitoring.MonitoringConstants; import org.lamsfoundation.lams.monitoring.dto.ContributeActivityDTO; +import org.lamsfoundation.lams.monitoring.dto.EmailProgressActivityDTO; import org.lamsfoundation.lams.monitoring.quartz.job.CloseScheduleGateJob; import org.lamsfoundation.lams.monitoring.quartz.job.FinishScheduleLessonJob; import org.lamsfoundation.lams.monitoring.quartz.job.OpenScheduleGateJob; @@ -106,6 +108,7 @@ import org.lamsfoundation.lams.usermanagement.util.LastNameAlphabeticComparator; import org.lamsfoundation.lams.util.DateUtil; import org.lamsfoundation.lams.util.MessageService; +import org.lamsfoundation.lams.util.NumberUtil; import org.lamsfoundation.lams.util.audit.AuditService; import org.lamsfoundation.lams.web.session.SessionManager; import org.lamsfoundation.lams.web.util.AttributeNames; @@ -2255,7 +2258,121 @@ } } + @SuppressWarnings("unchecked") + private EmailProgressActivitiesProcessor getEmailProgressActivitiesProcessor(Long lessonId) { + + // TODO custom SQL to get the ids, number of users & marks in one go + Lesson lesson = lessonService.getLesson(lessonId); + LearningDesign ld = lesson.getLearningDesign(); + Long[] activityIds = new Long[ld.getActivities().size()]; + int i=0; + Iterator activityIterator = (Iterator) ld.getActivities().iterator(); + while ( activityIterator.hasNext() ) { + Activity activity = activityIterator.next(); + activityIds[i] = activity.getActivityId(); + i++; + } + Map numberOfUsersInActivity = getCountLearnersCurrentActivities(activityIds); + + EmailProgressActivitiesProcessor processor = new EmailProgressActivitiesProcessor(ld, activityDAO, numberOfUsersInActivity); + processor.parseLearningDesign(); + return processor; + } + @Override + public String[] generateLessonProgressEmail(Long lessonId, Integer userId) { + + Lesson lesson = lessonService.getLesson(lessonId); + EmailProgressActivitiesProcessor activityProcessor = getEmailProgressActivitiesProcessor(lessonId); + Integer completedLearnersCount = getCountLearnersCompletedLesson(lessonId); + Integer startedLearnersCount = lessonService.getCountActiveLessonLearners(lessonId) - completedLearnersCount; + Integer possibleLearnersCount = lessonService.getCountLessonLearners(lessonId, null); + Integer notStarted = possibleLearnersCount - completedLearnersCount - startedLearnersCount; + + StringBuilder progress = new StringBuilder(); + progress.append("

Lesson ").append(lesson.getLessonName()).append("

") + .append(getMessageService().getMessage("label.started")).append(" ") + .append(lesson.getStartDateTime()).append("

") + .append(getMessageService().getMessage("label.grouping.learners")).append("

") + .append("") + .append("") + .append("") + .append("") + .append("
").append(getMessageService().getMessage("label.status")).append("") + .append(getMessageService().getMessage("progress.email.heading.number.learners")) + .append("%
").append(getMessageService().getMessage("label.not.started")).append("") + .append(notStarted).append("") + .append(asPercentage(notStarted, possibleLearnersCount)).append("%
").append(getMessageService().getMessage("lesson.chart.started")).append("") + .append(startedLearnersCount).append("") + .append(asPercentage(startedLearnersCount, possibleLearnersCount)).append("%
").append(getMessageService().getMessage("lesson.chart.completed")).append("") + .append(completedLearnersCount).append("") + .append(asPercentage(completedLearnersCount, possibleLearnersCount)).append("%
").append(possibleLearnersCount).append("") + .append("100%
") + .append("

").append(getMessageService().getMessage("progress.email.heading.overall.progress")).append("

") + .append("") + .append("").append(""); + + int numLearnersProcessed = notStarted; + + Vector activities = activityProcessor.getActivityList(); + for (EmailProgressActivityDTO dto : activities) { + Activity activity = dto.getActivity(); + if (!activity.isFloatingActivity()) { + progress.append(""); + } else { + progress.append(dto.getNumberOfLearners()).append(""); + numLearnersProcessed += dto.getNumberOfLearners(); + } + } + } + numLearnersProcessed += completedLearnersCount; + progress.append("").append("").append("").append("").append("
").append(getMessageService().getMessage("label.activity")).append("") + .append(getMessageService().getMessage("progress.email.heading.number.learners")) + .append("%
") + .append(getMessageService().getMessage("label.not.started")).append("").append(notStarted) + .append("").append(asPercentage(notStarted, possibleLearnersCount)) + .append("%
"); + for (int i = 0; i < dto.getDepth(); i++) { + progress.append("• "); + } + String title = activity.getTitle(); // null for gates + if (title == null) { + title = activity.isGateActivity() ? getMessageService().getMessage("label.gate.title") : + getMessageService().getMessage("label.unknown"); + } + + if (activity.isBranchingActivity() || activity.isOptionsActivity()) { + progress.append("").append(title).append(""); + } else { + progress.append("").append(title).append(""); + } + // output the headings for branching/options but only output numbers if there are learners stuck in the complex activity. + if ((activity.isBranchingActivity() || activity.isOptionsActivity() || activity.isSequenceActivity()) + && (dto.getNumberOfLearners() == null || dto.getNumberOfLearners() == 0)) { + progress.append("
") + .append(asPercentage(dto.getNumberOfLearners(), possibleLearnersCount)) + .append("%
Finished").append(completedLearnersCount) + .append("").append(asPercentage(completedLearnersCount, possibleLearnersCount)) + .append("%
").append(numLearnersProcessed) + .append("").append(asPercentage(numLearnersProcessed, possibleLearnersCount)) + .append("%
").append("

 

").append("

 

") + .append("

") + .append(getMessageService().getMessage("progress.email.sent.automatically")) + .append("

"); + + String subject = getMessageService().getMessage("progress.email.subject", + new Object[] { lesson.getLessonName() }); + + return new String[] { subject, progress.toString() }; + + } + + private String asPercentage(Integer numerator, Integer denominator ) { + double raw = numerator.doubleValue() / denominator * 100; + return NumberUtil.formatLocalisedNumber(raw, (Locale)null, 2); + } + + @Override public Long cloneLesson(Long lessonId, Integer creatorId, Boolean addAllStaff, Boolean addAllLearners, String[] staffIds, String[] learnerIds, Organisation group) throws MonitoringServiceException { Lesson newLesson = null; @@ -2378,4 +2495,5 @@ return resetReadOnly; } + } \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/EmailProgressAction.java =================================================================== diff -u --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/EmailProgressAction.java (revision 0) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/EmailProgressAction.java (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -0,0 +1,325 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + + +package org.lamsfoundation.lams.monitoring.web; + +import java.io.IOException; +import java.security.InvalidParameterException; +import java.util.Date; +import java.util.Locale; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.apache.tomcat.util.json.JSONArray; +import org.apache.tomcat.util.json.JSONException; +import org.apache.tomcat.util.json.JSONObject; +import org.lamsfoundation.lams.events.IEventNotificationService; +import org.lamsfoundation.lams.monitoring.quartz.job.EmailProgressMessageJob; +import org.lamsfoundation.lams.monitoring.service.IMonitoringService; +import org.lamsfoundation.lams.security.ISecurityService; +import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.lamsfoundation.lams.util.DateUtil; +import org.lamsfoundation.lams.util.WebUtil; +import org.lamsfoundation.lams.web.action.LamsDispatchAction; +import org.lamsfoundation.lams.web.session.SessionManager; +import org.lamsfoundation.lams.web.util.AttributeNames; +import org.quartz.JobBuilder; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import org.quartz.impl.matchers.GroupMatcher; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +/** + * Responsible for "Email Progress" functionality. + */ +public class EmailProgressAction extends LamsDispatchAction { + + // --------------------------------------------------------------------- + // Class level constants + // --------------------------------------------------------------------- + + private static final String TRIGGER_PREFIX_NAME = "emailProgressMessageTrigger:"; + private static final String JOB_PREFIX_NAME = "emailProgressMessageJob:"; + + private static IEventNotificationService eventNotificationService; + private static IMonitoringService monitoringService; + private static ISecurityService securityService; + + // --------------------------------------------------------------------- + // Struts Dispatch Method + // --------------------------------------------------------------------- + + /** + * Gets learners or monitors of the lesson and organisation containing it. + * + * @throws SchedulerException + */ + public ActionForward getEmailProgressDates(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, JSONException, SchedulerException { + Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + if (!getSecurityService().isLessonMonitor(lessonId, getCurrentUser().getUserID(), "get class members", false)) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson"); + return null; + } + + HttpSession ss = SessionManager.getSession(); + UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); + + JSONObject responseJSON = new JSONObject(); + JSONArray datesJSON = new JSONArray(); + + // find all the current dates set up to send the emails + Scheduler scheduler = getScheduler(); + String triggerPrefix = getTriggerPrefix(lessonId); + SortedSet currentDatesSet = new TreeSet(); + Set triggerKeys = scheduler + .getTriggerKeys(GroupMatcher.triggerGroupEquals(Scheduler.DEFAULT_GROUP)); + for (TriggerKey triggerKey : triggerKeys) { + String triggerName = triggerKey.getName(); + if (triggerName.startsWith(triggerPrefix)) { + Trigger trigger = scheduler.getTrigger(triggerKey); + JobDetail jobDetail = scheduler.getJobDetail(trigger.getJobKey()); + JobDataMap jobDataMap = jobDetail.getJobDataMap(); + + // get only the trigger for the current lesson + + Object jobLessonId = jobDataMap.get(AttributeNames.PARAM_LESSON_ID); + if (lessonId.equals(jobLessonId)) { + + Date triggerDate = trigger.getNextFireTime(); + currentDatesSet.add(triggerDate); + } + } + } + + for (Date date : currentDatesSet) { + datesJSON.put(createDateJSON(request.getLocale(), user, date, null)); + } + responseJSON.put("dates", datesJSON); + + response.setContentType("application/json;charset=utf-8"); + response.getWriter().write(responseJSON.toString()); + return null; + } + + private JSONObject createDateJSON(Locale locale, UserDTO user, Date date, String result) throws JSONException { + JSONObject dateJSON = new JSONObject(); + if (result != null) { + dateJSON.put("result", result); + } + if (date != null) { + dateJSON.put("id", date.getTime()); + dateJSON.put("ms", date.getTime()); + dateJSON.put("date", DateUtil.convertToStringForJSON(date, locale) ); + } + return dateJSON; + } + + private String getTriggerPrefix(Long lessonId) { + return new StringBuilder(EmailProgressAction.TRIGGER_PREFIX_NAME).append(lessonId).append(":").toString(); + } + + private String getTriggerName(Long lessonId, Date date) { + return new StringBuilder(EmailProgressAction.TRIGGER_PREFIX_NAME).append(lessonId).append(":") + .append(date.getTime()).toString(); + } + + private String getJobName(Long lessonId, Date date) { + return new StringBuilder(EmailProgressAction.JOB_PREFIX_NAME).append(lessonId).append(":") + .append(date.getTime()).toString(); + } + + /** + * Add or remove a date for the email progress + */ + public ActionForward updateEmailProgressDate(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, JSONException { + Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + if (!getSecurityService().isLessonMonitor(lessonId, getCurrentUser().getUserID(), "get class members", false)) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson"); + return null; + } + + HttpSession ss = SessionManager.getSession(); + UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); + + // as we are using ms since UTC 0 calculated on the client, this will be correctly set up as server date + // and does not need changing (assuming the user's LAMS timezone matches the user's computer timezone). + long dateId = WebUtil.readLongParam(request, "id"); + Date newDate = new Date(dateId); + boolean add = WebUtil.readBooleanParam(request, "add"); + + // calculate scheduleDate + String scheduledTriggerName = getTriggerName(lessonId, newDate); + JSONObject dateJSON = null; + + try { + Scheduler scheduler = getScheduler(); + Set triggerKeys = scheduler.getTriggerKeys(GroupMatcher + .triggerGroupEquals(Scheduler.DEFAULT_GROUP)); + Trigger trigger = null; + + for (TriggerKey triggerKey : triggerKeys) { + if (scheduledTriggerName.equals(triggerKey.getName())) { + trigger = scheduler.getTrigger(triggerKey); + break; + } + } + + if (add) { + if (trigger == null) { + String desc = new StringBuilder("Send progress email. Lesson ") + .append(lessonId).append(" on ").append(newDate).toString(); + // build job detail based on the bean class + JobDetail EmailProgressMessageJob = JobBuilder.newJob(EmailProgressMessageJob.class) + .withIdentity(getJobName(lessonId, newDate)) + .withDescription(desc) + .usingJobData(AttributeNames.PARAM_LESSON_ID, lessonId).build(); + + // create customized triggers + Trigger startLessonTrigger = TriggerBuilder.newTrigger().withIdentity(scheduledTriggerName) + .startAt(newDate).build(); + // start the scheduling job + scheduler.scheduleJob(EmailProgressMessageJob, startLessonTrigger); + + dateJSON = createDateJSON(request.getLocale(), user, newDate, "added"); + + } else { + dateJSON = createDateJSON(request.getLocale(), user, newDate, "none"); + } + } else if (!add) { + if (trigger != null) { + // remove trigger + scheduler.deleteJob(trigger.getJobKey()); + + dateJSON = createDateJSON(request.getLocale(), user, null, "deleted"); + } else { + dateJSON = createDateJSON(request.getLocale(), user, null, "none"); + } + } + } catch (SchedulerException e) { + LamsDispatchAction.log.error("Error occurred at " + "[EmailProgressAction]- fail to email scheduling", e); + } + + if (dateJSON != null) { + response.setContentType("application/json;charset=utf-8"); + response.getWriter().write(dateJSON.toString()); + } + return null; + } + + public ActionForward sendLessonProgressEmail(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException, JSONException { + + Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + Integer monitorUserId = getCurrentUser().getUserID(); + if (!getSecurityService().isLessonMonitor(lessonId, monitorUserId, "get lesson progress", false)) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson"); + return null; + } + + String parts[] = getMonitoringService().generateLessonProgressEmail(lessonId, monitorUserId); + String error = null; + int sent = 0; + + try { + if (getEventNotificationService().sendMessage(null, monitorUserId, + IEventNotificationService.DELIVERY_METHOD_MAIL, parts[0], parts[1], true)) { + sent = 1; + } + + } catch (InvalidParameterException ipe) { + error = ipe.getMessage(); + } catch (Exception e) { + error = e.getMessage(); + } + + JSONObject responseJSON = new JSONObject(); + responseJSON.put("sent", sent); + if (error != null) { + responseJSON.put("error", error); + } + + response.setContentType("application/json;charset=utf-8"); + response.getWriter().write(responseJSON.toString()); + return null; + } + + private UserDTO getCurrentUser() { + HttpSession ss = SessionManager.getSession(); + return (UserDTO) ss.getAttribute(AttributeNames.USER); + } + + private IEventNotificationService getEventNotificationService() { + if (eventNotificationService == null) { + WebApplicationContext ctx = WebApplicationContextUtils + .getRequiredWebApplicationContext(getServlet().getServletContext()); + eventNotificationService = (IEventNotificationService) ctx.getBean("eventNotificationService"); + } + return eventNotificationService; + } + + private IMonitoringService getMonitoringService() { + if (EmailProgressAction.monitoringService == null) { + WebApplicationContext ctx = WebApplicationContextUtils + .getRequiredWebApplicationContext(getServlet().getServletContext()); + EmailProgressAction.monitoringService = (IMonitoringService) ctx.getBean("monitoringService"); + } + return EmailProgressAction.monitoringService; + } + + private ISecurityService getSecurityService() { + if (securityService == null) { + WebApplicationContext webContext = WebApplicationContextUtils + .getRequiredWebApplicationContext(getServlet().getServletContext()); + securityService = (ISecurityService) webContext.getBean("securityService"); + } + + return securityService; + } + + /** + * + * @return the bean that defines Scheduler. + */ + private Scheduler getScheduler() { + WebApplicationContext ctx = WebApplicationContextUtils + .getRequiredWebApplicationContext(getServlet().getServletContext()); + return (Scheduler) ctx.getBean("scheduler"); + } +} Index: lams_monitoring/web/WEB-INF/struts-config.xml =================================================================== diff -u -rae5e9eacbfbf13097410637b6e8990dfb909177a -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/web/WEB-INF/struts-config.xml (.../struts-config.xml) (revision ae5e9eacbfbf13097410637b6e8990dfb909177a) +++ lams_monitoring/web/WEB-INF/struts-config.xml (.../struts-config.xml) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -214,6 +214,15 @@ redirect="false" /> + + + Index: lams_monitoring/web/includes/javascript/monitorLesson.js =================================================================== diff -u -r050a231052c87da93630886a48c6fa5fa4cafe51 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision 050a231052c87da93630886a48c6fa5fa4cafe51) +++ lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -148,9 +148,25 @@ } }); + var emailProgressDialog = showDialog('emailProgressDialog',{ + 'autoOpen' : false, + 'height' : 700, + 'width' : 510, + 'title' : LABELS.PROGRESS_EMAIL_TITLE, + 'resizable' : true, + 'open' : function(){ + autoRefreshBlocked = true; + }, + 'close' : function(){ + autoRefreshBlocked = false; + } + }, false); + $('.modal-body', emailProgressDialog).empty().append($('#emailProgressDialogContents').show()); + //initialize datetimepicker + $("#emaildatePicker").datetimepicker(); + } - /** * Shows all learners in the lesson class. */ @@ -498,6 +514,196 @@ } } +/** + * Set up when the progress emails should be sent to monitors + */ +function configureProgressEmail(){ + fillEmailProgress(); + $('#emailProgressDialog').modal('show'); +} + +/** + * Adds/removes date to the set of progress report emailing dates + */ +function editEmailProgressDate(dateCheckbox){ + var dateid = dateCheckbox.parent().attr('dateid'), + add = dateCheckbox.is(':checked'); + + $.ajax({ + url : LAMS_URL + 'monitoring/emailProgress.do', + type : 'POST', + cache : false, + data : { + 'method' : 'updateEmailProgressDate', + 'lessonID' : lessonId, + 'id' : dateid, + 'add' : add + }, + success : function( dateObj ) { + dateCheckbox.parent().attr('dateid', dateObj.id); + dateCheckbox.parent().attr('datems', dateObj.ms); + dateCheckbox.parent().children().last().html(dateObj.date); + } + }); +} + +/** + * Fills the dates from the server for the email progress + */ +function fillEmailProgress() { + var dialog = $('#emailProgressDialog'), + table = $('#emailProgressDialogTable', dialog), + list = $('.dialogList', table).empty(), + dates = null; + ajaxProperties = dialog.data('ajaxProperties'), + dates = null; + + if (!ajaxProperties) { + // initialise ajax config + ajaxProperties = { + dataType : 'json', + url : LAMS_URL + 'monitoring/emailProgress.do', + cache : false, + async : false, + data : { + 'method' : 'getEmailProgressDates', + 'lessonID' : lessonId + }}; + dialog.data('ajaxProperties', ajaxProperties); + } + + ajaxProperties.success = function(response) { + dates = response.dates; + } + + $.ajax(ajaxProperties); + + $.each(dates, function(dateIndex, date) { + addCheckbox(date, list, true); + }); + + colorDialogList(table); +} + +function addCheckbox(dateObj, list, checked) { + // check for an existing matching date + var alreadyExists = false; + var existingDivs = $("div", list); + $.each(existingDivs, function(divIndex, div) { + if ( div.getAttribute('dateid') == dateObj.id ) { + alreadyExists = true; + return false; + } + }); + if ( alreadyExists ) + return; + + // does not exist so add to list + var checkbox = $('').attr({ + 'type' : 'checkbox' + }).change(function(){ + editEmailProgressDate($(this)); + }), + + dateString = $('').html(dateObj.date), + + dateDiv = $('
').attr({ + 'dateid' : dateObj.id, + 'datems' : dateObj.ms + }) + .addClass('dialogListItem') + .append(dateString) + .prepend(checkbox) + .appendTo(list); + + checkbox.prop('checked', checked); + return checkbox; +} + +function sendProgressEmail() { + if ( confirm(LABELS.PROGRESS_EMAIL_SEND_NOW_QUESTION) ) { + $.ajax({ + dataType : 'json', + url : LAMS_URL + 'monitoring/emailProgress.do', + cache : false, + data : { + 'method' : 'sendLessonProgressEmail', + 'lessonID' : lessonId + }, + success : function(response) { + if ( response.error || ! response.sent > 0 ) + alert(LABELS.PROGRESS_EMAIL_SEND_FAILED+"\n"+(response.error ? response.error : "")); + else + alert(LABELS.PROGRESS_EMAIL_SUCCESS.replace('[0]',response.sent)); + } + }); + } +} + +function addEmailProgressDate() { + var table = $('#emailProgressDialogTable', '#emailProgressDialog'), + list = $('.dialogList', table), + newDateMS = $('#emaildatePicker').datetimepicker('getDate'); + + if ( newDateMS != null ) { + if ( newDateMS.getTime() < Date.now() ) { + alert(LABELS.ERROR_DATE_IN_PAST); + } else { + var dateObj = { id: newDateMS.getTime(), date: getEmailDateString(newDateMS)}, + checkbox = addCheckbox(dateObj, list, true); + editEmailProgressDate(checkbox); // update back end + addEmailProgressSeries(false, table); + } + } else { + alert(LABELS.PROGRESS_SELECT_DATE_FIRST); + } +} + + + +function getEmailDateString(date) { + return date.toLocaleDateString('en', {year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: false }); +} + +function addEmailProgressSeries(forceQuestion, table) { + if ( ! table ) { + table = $('#emailProgressDialogTable', '#emailProgressDialog'); + } + var list = $('.dialogList', table), + items = $('.dialogListItem', list); + + if ( forceQuestion && items.length < 2 ) { + alert(LABELS.PROGRESS_ENTER_TWO_DATES_FIRST); + } else if ( items.length == 2 || forceQuestion ) { + var numDates = prompt(LABELS.PROGRESS_EMAIL_GENERATE_ONE+"\n\n"+LABELS.PROGRESS_EMAIL_GENERATE_TWO); + if ( numDates > 0 ) { + var dates=[]; + var maxDate = 0; + items.each( function() { + var nextDate = $(this).attr('dateid'); + dates.push($(this).attr('dateid')); + if ( maxDate < nextDate ) + maxDate = nextDate; + }); + if ( dates[1] < dates[0] ) { + var swap = dates[1]; + dates[1] = dates[0]; + dates[0] = swap; + } + var diff = dates[1] - dates[0]; + if ( diff > 0 ) { + var genDateMS = maxDate; + for (var i = 0; i < numDates; i++) { + genDateMS = +genDateMS + +diff; + var genDateObj = { id: genDateMS, date: getEmailDateString(new Date(genDateMS))}; + var checkbox = addCheckbox(genDateObj, list, false); + } + } + } + } + colorDialogList(table); +} + //********** SEQUENCE TAB FUNCTIONS ********** /** Index: lams_monitoring/web/monitor.jsp =================================================================== diff -u -r050a231052c87da93630886a48c6fa5fa4cafe51 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/web/monitor.jsp (.../monitor.jsp) (revision 050a231052c87da93630886a48c6fa5fa4cafe51) +++ lams_monitoring/web/monitor.jsp (.../monitor.jsp) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -178,8 +178,26 @@ EMAIL_TITLE : '', - TOUR_DISABLED_ELEMENT : '' - } + TOUR_DISABLED_ELEMENT : '', + + PROGRESS_EMAIL_SUCCESS : '', + + PROGRESS_EMAIL_SEND_NOW_QUESTION : '', + + PROGRESS_EMAIL_SEND_FAILED : '', + + PROGRESS_SELECT_DATE_FIRST : '', + + PROGRESS_ENTER_TWO_DATES_FIRST : '', + + PROGRESS_EMAIL_GENERATE_ONE : '', + + PROGRESS_EMAIL_GENERATE_TWO : '', + + PROGRESS_EMAIL_TITLE : '', + + ERROR_DATE_IN_PAST : '' + } $(document).ready(function(){ initLessonTab(); @@ -409,6 +427,24 @@
+ + +
:
+
+
+ + +
+
+
@@ -772,6 +808,46 @@
+
+
+
+
+ + + + + + + +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
Index: lams_monitoring/web/monitorTour.jsp =================================================================== diff -u -rb67f087068e5b478fc4e3205dc308b470195fe42 -r8714ac689fdad46746bbb7f28005ec080d1d4ba6 --- lams_monitoring/web/monitorTour.jsp (.../monitorTour.jsp) (revision b67f087068e5b478fc4e3205dc308b470195fe42) +++ lams_monitoring/web/monitorTour.jsp (.../monitorTour.jsp) (revision 8714ac689fdad46746bbb7f28005ec080d1d4ba6) @@ -66,6 +66,16 @@ onShow: revealIM, onHide: hideIM },{ + element: "#sendProgressEmail", + title: "", + content: "", + placement: "bottom" + },{ + element: "#configureProgressEmail", + title: "", + content: "", + placement: "bottom" + },{ element: "#chartDiv", title: "", content: "",