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) {
+ EmailProgressController.log.error("Error occurred at " + "[EmailProgressAction]- fail to email scheduling",
+ e);
+ }
+
+ if (dateJSON != null) {
+ response.setContentType("application/json;charset=utf-8");
+ }
+ return dateJSON.toString();
+ }
+
+ @RequestMapping("/sendLessonProgressEmail")
+ @ResponseBody
+ public String sendLessonProgressEmail(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ 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[] = monitoringService.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();
+ }
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.put("sent", sent);
+ if (error != null) {
+ responseJSON.put("error", error);
+ }
+
+ response.setContentType("application/json;charset=utf-8");
+ return responseJSON.toString();
+ }
+
+ private UserDTO getCurrentUser() {
+ HttpSession ss = SessionManager.getSession();
+ return (UserDTO) ss.getAttribute(AttributeNames.USER);
+ }
+
+ private IEventNotificationService getEventNotificationService() {
+ if (eventNotificationService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ eventNotificationService = (IEventNotificationService) ctx.getBean("eventNotificationService");
+ }
+ return eventNotificationService;
+ }
+
+ private ISecurityService getSecurityService() {
+ if (securityService == null) {
+ WebApplicationContext webContext = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ securityService = (ISecurityService) webContext.getBean("securityService");
+ }
+
+ return securityService;
+ }
+
+ /**
+ *
+ * @return the bean that defines Scheduler.
+ */
+ private Scheduler getScheduler() {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ return (Scheduler) ctx.getBean("scheduler");
+ }
+}
Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/FileUploadForm.java
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r6e7ab7890111cde84e6557c3507a1e9d3682e318
--- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/FileUploadForm.java (.../FileUploadForm.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/FileUploadForm.java (.../FileUploadForm.java) (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -21,45 +21,49 @@
* ****************************************************************
*/
-
package org.lamsfoundation.lams.monitoring.web;
-import org.apache.struts.action.ActionForm;
-import org.apache.struts.upload.FormFile;
+import org.springframework.web.multipart.MultipartFile;
-public class FileUploadForm extends ActionForm {
+public class FileUploadForm {
private static final long serialVersionUID = -2841551690414943725L;
-
+
private Integer organisationID;
private Long lessonID;
private Long activityID;
- private FormFile attachmentFile;
+ private MultipartFile attachmentFile;
+
public Integer getOrganisationID() {
- return organisationID;
+ return organisationID;
}
+
public void setOrganisationID(Integer organisationID) {
- this.organisationID = organisationID;
+ this.organisationID = organisationID;
}
+
public Long getLessonID() {
- return lessonID;
+ return lessonID;
}
+
public void setLessonID(Long lessonID) {
- this.lessonID = lessonID;
+ this.lessonID = lessonID;
}
+
public Long getActivityID() {
- return activityID;
+ return activityID;
}
+
public void setActivityID(Long activityID) {
- this.activityID = activityID;
+ this.activityID = activityID;
}
- public FormFile getAttachmentFile() {
- return attachmentFile;
+
+ public MultipartFile getAttachmentFile() {
+ return attachmentFile;
}
- public void setAttachmentFile(FormFile attachmentFile) {
- this.attachmentFile = attachmentFile;
+
+ public void setAttachmentFile(MultipartFile attachmentFile) {
+ this.attachmentFile = attachmentFile;
}
-
-
}
Fisheye: Tag 6e7ab7890111cde84e6557c3507a1e9d3682e318 refers to a dead (removed) revision in file `lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateController.java
===================================================================
diff -u
--- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateController.java (revision 0)
+++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateController.java (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -0,0 +1,295 @@
+/****************************************************************
+ * 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.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.lang.StringUtils;
+import org.lamsfoundation.lams.learning.service.ICoreLearnerService;
+import org.lamsfoundation.lams.learningdesign.GateActivity;
+import org.lamsfoundation.lams.learningdesign.Group;
+import org.lamsfoundation.lams.learningdesign.ScheduleGateActivity;
+import org.lamsfoundation.lams.lesson.Lesson;
+import org.lamsfoundation.lams.lesson.service.ILessonService;
+import org.lamsfoundation.lams.monitoring.service.IMonitoringService;
+import org.lamsfoundation.lams.monitoring.service.MonitoringServiceException;
+import org.lamsfoundation.lams.monitoring.service.MonitoringServiceProxy;
+import org.lamsfoundation.lams.usermanagement.User;
+import org.lamsfoundation.lams.usermanagement.dto.UserDTO;
+import org.lamsfoundation.lams.util.WebUtil;
+import org.lamsfoundation.lams.web.session.SessionManager;
+import org.lamsfoundation.lams.web.util.AttributeNames;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ *
+ * The action servlet that allows the teacher to view the status of sync gate, scheduling gate and permission gate. The
+ * teacher can also force the gate to open through this servlet.
+ *
+ *
+ *
+ * Regarding view gate status, followings contents should be shown by calling this action servlet:
+ *
1.View the status of an sync gate, the lams should show how many learners are waiting and the size of the total
+ * class.
+ * 2.View the status of the permission gate, the lams shows the number of the learners waiting in front of the
+ * gates.
+ * 3.View the status of the schedule gate, the lams shows the gate status. If the schedule has been triggerred. The
+ * teacher should be able to change the trigger.
+ *
+ *
+ * @author Jacky Fang
+ * @since 2005-4-15
+ * @version 1.1
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+@Controller
+@RequestMapping("/gate")
+public class GateController {
+
+ @Autowired
+ @Qualifier("monitoringService")
+ private IMonitoringService monitoringService;
+
+ @Autowired
+ private WebApplicationContext applicationContext;
+
+ // ---------------------------------------------------------------------
+ // Instance variables
+ // ---------------------------------------------------------------------
+ // private static Logger log = Logger.getLogger(GateAction.class);
+
+ private ICoreLearnerService learnerService;
+ private ILessonService lessonService;
+
+ private static final DateFormat SCHEDULING_DATETIME_FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm");
+
+ // ---------------------------------------------------------------------
+ // Method
+ // ---------------------------------------------------------------------
+ /**
+ *
+ * The dispatch method that allows the teacher to view the status of the gate. It is expecting the caller passed in
+ * lesson id and gate activity id as http parameter. Otherwise, the utility method will generate some exception.
+ *
+ *
+ *
+ * Based on the lesson id and gate activity id, it sets up the gate form to show the waiting learners and the total
+ * waiting learners. Regarding schedule gate, it also shows the estimated gate opening time and gate closing time.
+ *
+ *
+ * Note: gate form attribute waitingLearners
got setup after the view is dispatch to ensure
+ * there won't be casting exception occur if the activity id is not a gate by chance.
+ *
+ */
+ @RequestMapping("/viewGate")
+ public String viewGate(@ModelAttribute GateForm gateForm, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ // if this is the initial call then activity id will be in the request, otherwise
+ // get it from the form (if being called from openGate.jsp
+ Long gateIdLong = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID, true);
+ if (gateIdLong == null) {
+ gateIdLong = gateForm.getActivityId();
+ }
+ long gateId = gateIdLong != null ? gateIdLong.longValue() : -1;
+
+ learnerService = MonitoringServiceProxy.getLearnerService(applicationContext.getServletContext());
+
+ GateActivity gate = (GateActivity) monitoringService.getActivityById(gateId);
+
+ if (gate == null) {
+ throw new MonitoringServiceException("Gate activity missing. Activity id" + gateId);
+ }
+
+ gateForm.setActivityId(gateIdLong);
+
+ return findViewByGateType(gateForm, gate);
+ }
+
+ /**
+ * Open the gate if is closed.
+ */
+ @RequestMapping("/openGate")
+ public String openGate(@ModelAttribute GateForm gateForm, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ GateActivity gate = monitoringService.openGate(gateForm.getActivityId(), getUserId());
+
+ return findViewByGateType(gateForm, gate);
+ }
+
+ /**
+ * Allows a single learner to pass the gate.
+ */
+ @RequestMapping("/openGateForSingleUser")
+ public String openGateForSingleUser(@ModelAttribute GateForm gateForm, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ Long gateIdLong = gateForm.getActivityId();
+ String userId = gateForm.getUserId();
+ String[] userIdsString = userId.split(",");
+ List userIds = new LinkedList<>();
+ for (String userIdString : userIdsString) {
+ if (StringUtils.isNotBlank(userIdString)) {
+ userIds.add(Integer.valueOf(userIdString));
+ }
+ }
+ GateActivity gate = monitoringService.openGateForSingleUser(gateIdLong, userIds.toArray(new Integer[] {}));
+ return findViewByGateType(gateForm, gate);
+ }
+
+ @RequestMapping("/scheduleGate")
+ public String scheduleGate(@ModelAttribute GateForm gateForm, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException, ParseException {
+
+ Long gateId = gateForm.getActivityId();
+ String dateAsString = gateForm.getScheduleDate();
+ GateActivity gate = null;
+ if (dateAsString != null && dateAsString.trim().length() > 0) {
+ gate = monitoringService.scheduleGate(gateId, SCHEDULING_DATETIME_FORMAT.parse(dateAsString), getUserId());
+ } else {
+ gate = (GateActivity) monitoringService.getActivityById(gateId);
+ }
+ return findViewByGateType(gateForm, gate);
+ }
+
+ // ---------------------------------------------------------------------
+ // Helper Methods
+ // ---------------------------------------------------------------------
+ private Integer getUserId() {
+ HttpSession ss = SessionManager.getSession();
+ UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER);
+ return user != null ? user.getUserID() : null;
+ }
+
+ /**
+ * Dispatch view the according to the gate type.
+ *
+ */
+ @RequestMapping("/findViewByGateType")
+ private String findViewByGateType(@ModelAttribute GateForm gateForm, GateActivity gate) {
+
+ lessonService = MonitoringServiceProxy.getLessonService(applicationContext.getServletContext());
+
+ // reset all the other fields, so that the following code only has to set up its own values (LDEV-1237)
+ gateForm.setGate(null);
+ gateForm.setWaitingLearnerList(null);
+ gateForm.setAllowedToPassLearnerList(null);
+ gateForm.setForbiddenLearnerList(null);
+ gateForm.setStartingTime(null);
+ gateForm.setEndingTime(null);
+ gateForm.setScheduleDate(null);
+
+ gateForm.setGate(gate);
+
+ // setup the total learners
+ int totalLearners = 0;
+ for (Group group : learnerService.getGroupsForGate(gate)) {
+ // users collection is extra-lazy, so checking its size will not trigger full load
+ totalLearners += group.getUsers().size();
+ }
+ gateForm.setTotalLearners(totalLearners);
+
+ // dispatch the view according to the type of the gate.
+ if (gate.isSynchGate()) {
+ Integer waitingLearnerCount = lessonService.getCountLearnersInCurrentActivity(gate);
+ gateForm.setWaitingLearners(waitingLearnerCount);
+ return "gate/sychGateContent";
+ } else if (gate.isScheduleGate()) {
+ Integer waitingLearnerCount = lessonService.getCountLearnersInCurrentActivity(gate);
+ gateForm.setWaitingLearners(waitingLearnerCount);
+ return viewScheduleGate(gateForm, (ScheduleGateActivity) gate);
+ } else if (gate.isPermissionGate() || gate.isSystemGate() || gate.isConditionGate()) {
+ List waitingLearnersList = monitoringService.getLearnersAttemptedActivity(gate);
+ gateForm.setWaitingLearners(waitingLearnersList.size());
+ gateForm.setWaitingLearnerList(waitingLearnersList);
+ gateForm.setAllowedToPassLearnerList(gate.getAllowedToPassLearners());
+ Set learnerGroups = learnerService.getGroupsForGate(gate);
+ Collection forbiddenUsers = new HashSet<>();
+ for (Group learnerGroup : learnerGroups) {
+ // only here users are fetched from DB as it is an extra-lazy collection
+ forbiddenUsers.addAll(learnerGroup.getUsers());
+ }
+ forbiddenUsers.removeAll(gate.getAllowedToPassLearners());
+ gateForm.setForbiddenLearnerList(forbiddenUsers);
+ if (gate.isConditionGate()) {
+ return "gate/conditionGateContent";
+ }
+
+ return "gate/permissionGateContent";
+ } else {
+ throw new MonitoringServiceException("Invalid gate activity. " + "gate id [" + gate.getActivityId()
+ + "] - the type [" + gate.getActivityTypeId() + "] is not a gate type");
+ }
+ }
+
+ /**
+ * Set up the form attributes specific to the schedule gate and navigate to the schedule gate view.
+ */
+ @RequestMapping("/viewScheduleGate")
+ public String viewScheduleGate(@ModelAttribute GateForm gateForm, ScheduleGateActivity scheduleGate) {
+
+ if (Boolean.TRUE.equals(scheduleGate.getGateActivityCompletionBased())) {
+ gateForm.setActivityCompletionBased(true);
+ } else {
+ gateForm.setActivityCompletionBased(false);
+ learnerService = MonitoringServiceProxy.getLearnerService(applicationContext.getServletContext());
+ Lesson lesson = learnerService.getLessonByActivity(scheduleGate);
+ Calendar startingTime = new GregorianCalendar(TimeZone.getDefault());
+ startingTime.setTime(lesson.getStartDateTime());
+ startingTime.add(Calendar.MINUTE, scheduleGate.getGateStartTimeOffset().intValue());
+ gateForm.setStartingTime(startingTime.getTime());
+ }
+
+ return "gate/scheduleGateContent";
+ }
+}
\ No newline at end of file
Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateForm.java
===================================================================
diff -u
--- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateForm.java (revision 0)
+++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateForm.java (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -0,0 +1,130 @@
+package org.lamsfoundation.lams.monitoring.web;
+
+import java.util.Collection;
+import java.util.Date;
+
+import org.lamsfoundation.lams.learningdesign.GateActivity;
+
+public class GateForm {
+
+ private GateActivity gate;
+
+ private Long activityId;
+
+ private Integer waitingLearners;
+
+ private Integer totalLearners;
+
+ private Date startingTime;
+
+ private Date endingTime;
+
+ private boolean activityCompletionBased;
+
+ private Collection waitingLearnerList;
+
+ private Collection forbiddenLearnerList;
+
+ private Collection allowedToPassLearnerList;
+
+ private String userId;
+
+ private String scheduleDate;
+
+ public GateActivity getGate() {
+ return gate;
+ }
+
+ public void setGate(GateActivity gate) {
+ this.gate = gate;
+ }
+
+ public Long getActivityId() {
+ return activityId;
+ }
+
+ public void setActivityId(Long activityId) {
+ this.activityId = activityId;
+ }
+
+ public Integer getWaitingLearners() {
+ return waitingLearners;
+ }
+
+ public void setWaitingLearners(Integer waitingLearners) {
+ this.waitingLearners = waitingLearners;
+ }
+
+ public Integer getTotalLearners() {
+ return totalLearners;
+ }
+
+ public void setTotalLearners(Integer totalLearners) {
+ this.totalLearners = totalLearners;
+ }
+
+ public Date getStartingTime() {
+ return startingTime;
+ }
+
+ public void setStartingTime(Date startingTime) {
+ this.startingTime = startingTime;
+ }
+
+ public Date getEndingTime() {
+ return endingTime;
+ }
+
+ public void setEndingTime(Date endingTime) {
+ this.endingTime = endingTime;
+ }
+
+ public boolean isActivityCompletionBased() {
+ return activityCompletionBased;
+ }
+
+ public void setActivityCompletionBased(boolean activityCompletionBased) {
+ this.activityCompletionBased = activityCompletionBased;
+ }
+
+ public Collection getWaitingLearnerList() {
+ return waitingLearnerList;
+ }
+
+ public void setWaitingLearnerList(Collection waitingLearnerList) {
+ this.waitingLearnerList = waitingLearnerList;
+ }
+
+ public Collection getForbiddenLearnerList() {
+ return forbiddenLearnerList;
+ }
+
+ public void setForbiddenLearnerList(Collection forbiddenLearnerList) {
+ this.forbiddenLearnerList = forbiddenLearnerList;
+ }
+
+ public Collection getAllowedToPassLearnerList() {
+ return allowedToPassLearnerList;
+ }
+
+ public void setAllowedToPassLearnerList(Collection allowedToPassLearnerList) {
+ this.allowedToPassLearnerList = allowedToPassLearnerList;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getScheduleDate() {
+ return scheduleDate;
+ }
+
+ public void setScheduleDate(String scheduleDate) {
+ this.scheduleDate = scheduleDate;
+ }
+
+}
Fisheye: Tag 6e7ab7890111cde84e6557c3507a1e9d3682e318 refers to a dead (removed) revision in file `lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingAJAXAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingAJAXController.java
===================================================================
diff -u
--- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingAJAXController.java (revision 0)
+++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingAJAXController.java (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -0,0 +1,442 @@
+/****************************************************************
+ * 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.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+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.commons.lang.StringUtils;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.lamsfoundation.lams.learning.web.action.GroupingAction;
+import org.lamsfoundation.lams.learningdesign.Activity;
+import org.lamsfoundation.lams.learningdesign.Group;
+import org.lamsfoundation.lams.learningdesign.GroupComparator;
+import org.lamsfoundation.lams.learningdesign.Grouping;
+import org.lamsfoundation.lams.learningdesign.GroupingActivity;
+import org.lamsfoundation.lams.lesson.service.LessonServiceException;
+import org.lamsfoundation.lams.monitoring.service.IMonitoringService;
+import org.lamsfoundation.lams.monitoring.service.MonitoringServiceProxy;
+import org.lamsfoundation.lams.security.ISecurityService;
+import org.lamsfoundation.lams.usermanagement.OrganisationGroup;
+import org.lamsfoundation.lams.usermanagement.OrganisationGrouping;
+import org.lamsfoundation.lams.usermanagement.Role;
+import org.lamsfoundation.lams.usermanagement.User;
+import org.lamsfoundation.lams.usermanagement.dto.UserDTO;
+import org.lamsfoundation.lams.usermanagement.service.IUserManagementService;
+import org.lamsfoundation.lams.usermanagement.util.FirstNameAlphabeticComparator;
+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.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * The action servlet that provides the support for the
+ *
+ * - AJAX based Chosen Grouping screen
+ * - forwards to the learner's view grouping screen for Random Grouping.
+ *
+ *
+ * @author Fiona Malikoff
+ */
+public class GroupingAJAXAction extends LamsDispatchAction {
+
+ // ---------------------------------------------------------------------
+
+ private static final String CHOSEN_GROUPING_SCREEN = "chosenGrouping";
+ private static final String VIEW_GROUPS_SCREEN = "viewGroups";
+ private static final String PARAM_ACTIVITY_TITLE = "title";
+ private static final String PARAM_ACTIVITY_DESCRIPTION = "description";
+ public static final String PARAM_MAX_NUM_GROUPS = "maxNumberOfGroups";
+ public static final String PARAM_NAME = "name";
+ public static final String PARAM_MEMBERS = "members";
+ public static final String PARAM_MAY_DELETE = "mayDelete";
+ public static final String PARAM_USED_FOR_BRANCHING = "usedForBranching";
+ public static final String PARAM_VIEW_MODE = "viewMode";
+
+ private static ISecurityService securityService;
+
+ /**
+ * Start the process of doing the chosen grouping
+ *
+ * Input parameters: activityID
+ */
+ public ActionForward startGrouping(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ return startGrouping(mapping, form, request, response, false);
+ }
+
+ private ActionForward startGrouping(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response, boolean forcePrintView) throws IOException, ServletException {
+
+ Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID);
+ Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ IMonitoringService monitoringService = MonitoringServiceProxy
+ .getMonitoringService(getServlet().getServletContext());
+ Activity activity = monitoringService.getActivityById(activityID);
+
+ Grouping grouping = null;
+ if (activity.isChosenBranchingActivity()) {
+ grouping = activity.getGrouping();
+ monitoringService.createChosenBranchingGroups(activityID);
+ } else {
+ grouping = ((GroupingActivity) activity).getCreateGrouping();
+ }
+
+ request.setAttribute(AttributeNames.PARAM_ACTIVITY_ID, activityID);
+ request.setAttribute(AttributeNames.PARAM_LESSON_ID, lessonId);
+ request.setAttribute(GroupingAJAXAction.PARAM_ACTIVITY_TITLE, activity.getTitle());
+ request.setAttribute(GroupingAJAXAction.PARAM_ACTIVITY_DESCRIPTION, activity.getDescription());
+
+ if ( !forcePrintView && grouping.isChosenGrouping()) {
+ // can I remove groups/users - can't if tool sessions have been created
+ Set groups = grouping.getGroups();
+ Iterator iter = groups.iterator();
+ boolean mayDelete = true;
+ while (mayDelete && iter.hasNext()) {
+ Group group = iter.next();
+ mayDelete = group.mayBeDeleted();
+ }
+
+ // is this grouping used for branching. If it is, must honour the groups
+ // set in authoring or some groups won't have a branch. mayDelete can still
+ // be true or false as you can remove users from groups, you just can't remove
+ // groups due to the branching relationship.
+ boolean usedForBranching = grouping.isUsedForBranching();
+
+ request.setAttribute(GroupingAJAXAction.PARAM_MAY_DELETE, mayDelete);
+ request.setAttribute(GroupingAJAXAction.PARAM_USED_FOR_BRANCHING, usedForBranching);
+ request.setAttribute(GroupingAJAXAction.PARAM_MAX_NUM_GROUPS, grouping.getMaxNumberOfGroups());
+ request.setAttribute(GroupingAJAXAction.PARAM_VIEW_MODE, Boolean.FALSE);
+
+ return mapping.findForward(GroupingAJAXAction.CHOSEN_GROUPING_SCREEN);
+ }
+
+ SortedSet groups = new TreeSet<>(new GroupComparator());
+ groups.addAll(grouping.getGroups());
+
+ // sort users with first, then last name, then login
+ Comparator userComparator = new FirstNameAlphabeticComparator();
+ for (Group group : groups) {
+ Set sortedUsers = new TreeSet<>(userComparator);
+ sortedUsers.addAll(group.getUsers());
+ group.setUsers(sortedUsers);
+ }
+
+ request.setAttribute(GroupingAction.GROUPS, groups);
+ // go to a view only screen for random grouping
+ return mapping.findForward(GroupingAJAXAction.VIEW_GROUPS_SCREEN);
+ }
+
+ /**
+ * Called by the chosen grouping / course grouping screen to show a print version of the grouping.
+ */
+ public ActionForward printGrouping(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID, true);
+ if ( activityID != null ) {
+ // normal activity based processing, startGrouping can handle it as it supports the normal view screen in monitoring
+ return startGrouping(mapping, form, request, response, true);
+ }
+
+ // Not activity? Then it must be the course grouping print view request
+ Long orgGroupingId = WebUtil.readLongParam(request, "groupingId", true);
+ OrganisationGrouping orgGrouping = null;
+ if (orgGroupingId != null) {
+ IUserManagementService userManagementService = MonitoringServiceProxy
+ .getUserManagementService(getServlet().getServletContext());
+ orgGrouping = (OrganisationGrouping) userManagementService.findById(OrganisationGrouping.class,
+ orgGroupingId);
+ }
+
+ SortedSet groups = new TreeSet();
+ if ( orgGrouping != null ) {
+ groups.addAll(orgGrouping.getGroups());
+
+ // sort users with first, then last name, then login
+ Comparator userComparator = new FirstNameAlphabeticComparator();
+ for (OrganisationGroup group : groups) {
+ Set sortedUsers = new TreeSet(userComparator);
+ sortedUsers.addAll(group.getUsers());
+ group.setUsers(sortedUsers);
+ }
+ }
+
+ request.setAttribute(GroupingAction.GROUPS, groups);
+ request.setAttribute("isCourseGrouping", true); // flag to page it is a course grouping so use the field names for OrganisationGroup
+ return mapping.findForward(GroupingAJAXAction.VIEW_GROUPS_SCREEN);
+ }
+ /**
+ * Moves users between groups, removing them from previous group and creating a new one, if needed.
+ */
+ public ActionForward addMembers(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+ response.setContentType("application/json;charset=utf-8");
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ boolean result = true;
+
+ Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID);
+ String membersParam = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_MEMBERS, true);
+ String[] members = StringUtils.isBlank(membersParam) ? null : membersParam.split(",");
+
+ // remove users from current group
+ IMonitoringService monitoringService = MonitoringServiceProxy
+ .getMonitoringService(getServlet().getServletContext());
+ if (members != null) {
+ Activity activity = monitoringService.getActivityById(activityID);
+ Grouping grouping = activity.isChosenBranchingActivity() ? activity.getGrouping()
+ : ((GroupingActivity) activity).getCreateGrouping();
+ User exampleUser = (User) MonitoringServiceProxy.getUserManagementService(getServlet().getServletContext())
+ .findById(User.class, Integer.valueOf(members[0]));
+ Group group = grouping.getGroupBy(exampleUser);
+ // null group means that user is not assigned anywhere in this grouping
+ if (!group.isNull()) {
+ // check if user can be moved outside of this group
+ result = group.mayBeDeleted();
+
+ if (result) {
+ if (LamsDispatchAction.log.isDebugEnabled()) {
+ LamsDispatchAction.log.debug("Removing users " + membersParam.toString() + " from group "
+ + group.getGroupId() + " in activity " + activityID);
+ }
+
+ try {
+ monitoringService.removeUsersFromGroup(activityID, group.getGroupId(), members);
+ } catch (LessonServiceException e) {
+ LamsDispatchAction.log.error(e);
+ result = false;
+ }
+ }
+
+ if (!result) {
+ // let JSP page know that this group became immutable
+ responseJSON.put("locked", true);
+ }
+ }
+ }
+
+ Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID, true);
+ // no group ID means that it has to be created
+ // group ID = -1 means that user is not being assigned to any new group, i.e. becomse unassigned
+ if (result && ((groupID == null) || (groupID > 0))) {
+ if (groupID == null) {
+ String name = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_NAME);
+ if (LamsDispatchAction.log.isDebugEnabled()) {
+ LamsDispatchAction.log.debug("Creating group with name \"" + name + "\" in activity " + activityID);
+ }
+ Group group = monitoringService.addGroup(activityID, name, true);
+ if (group == null) {
+ // group creation failed
+ result = false;
+ } else {
+ groupID = group.getGroupId();
+ // let JSP page know that the group was given this ID
+ responseJSON.put("groupId", groupID);
+ }
+ }
+
+ if (result && (members != null)) {
+ if (LamsDispatchAction.log.isDebugEnabled()) {
+ LamsDispatchAction.log.debug("Adding users " + membersParam.toString() + " to group " + groupID
+ + " in activity " + activityID);
+ }
+
+ // add users to the given group
+ try {
+ monitoringService.addUsersToGroup(activityID, groupID, members);
+ } catch (LessonServiceException e) {
+ LamsDispatchAction.log.error(e);
+ result = false;
+ }
+ }
+ }
+
+ responseJSON.put("result", result);
+ response.getWriter().write(responseJSON.toString());
+ return null;
+ }
+
+ /**
+ * Stores lesson grouping as a course grouping.
+ */
+ public ActionForward saveAsCourseGrouping(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+ IMonitoringService monitoringService = MonitoringServiceProxy
+ .getMonitoringService(getServlet().getServletContext());
+ IUserManagementService userManagementService = MonitoringServiceProxy
+ .getUserManagementService(getServlet().getServletContext());
+ HttpSession ss = SessionManager.getSession();
+ Integer userId = ((UserDTO) ss.getAttribute(AttributeNames.USER)).getUserID();
+ Integer organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID);
+ String newGroupingName = request.getParameter("name");
+
+ // check if user is allowed to view and edit groupings
+ if (!getSecurityService().hasOrgRole(organisationId, userId,
+ new String[] { Role.GROUP_ADMIN, Role.GROUP_MANAGER, Role.MONITOR, Role.AUTHOR },
+ "view organisation groupings", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a participant in the organisation");
+ return null;
+ }
+
+ Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID);
+ Activity activity = monitoringService.getActivityById(activityID);
+ Grouping grouping = activity.isChosenBranchingActivity() ? activity.getGrouping()
+ : ((GroupingActivity) activity).getCreateGrouping();
+
+ // iterate over groups
+ List orgGroups = new LinkedList<>();
+ for (Group group : grouping.getGroups()) {
+ OrganisationGroup orgGroup = new OrganisationGroup();
+ //groupId and GroupingId will be set during userManagementService.saveOrganisationGrouping() call
+ orgGroup.setName(group.getGroupName());
+ HashSet users = new HashSet<>();
+ users.addAll(group.getUsers());
+ orgGroup.setUsers(users);
+
+ orgGroups.add(orgGroup);
+ }
+
+ OrganisationGrouping orgGrouping = new OrganisationGrouping();
+ orgGrouping.setOrganisationId(organisationId);
+ orgGrouping.setName(newGroupingName);
+
+ userManagementService.saveOrganisationGrouping(orgGrouping, orgGroups);
+
+ response.setContentType("application/json;charset=utf-8");
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.put("result", true);
+ response.getWriter().write(responseJSON.toString());
+ return null;
+ }
+
+ /**
+ * Renames the group.
+ */
+ public ActionForward changeGroupName(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) {
+ Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID);
+ String name = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_NAME);
+ if (name != null) {
+ if (LamsDispatchAction.log.isDebugEnabled()) {
+ LamsDispatchAction.log.debug("Renaming group " + groupID + " to \"" + name + "\"");
+ }
+ IMonitoringService monitoringService = MonitoringServiceProxy
+ .getMonitoringService(getServlet().getServletContext());
+ monitoringService.setGroupName(groupID, name);
+ }
+ return null;
+ }
+
+ /**
+ * Checks if a course grouping name is unique inside of this organisation and thus whether the new group can be
+ * named using it
+ */
+ @SuppressWarnings("unchecked")
+ public ActionForward checkGroupingNameUnique(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+ IUserManagementService userManagementService = MonitoringServiceProxy
+ .getUserManagementService(getServlet().getServletContext());
+
+ Integer organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID);
+ String newGroupingName = request.getParameter("name");
+
+ // Checks if a course grouping name is unique inside of this group and thus new group can have it
+ HashMap properties = new HashMap<>();
+ properties.put("organisationId", organisationId);
+ properties.put("name", newGroupingName);
+ List orgGroupings = userManagementService.findByProperties(OrganisationGrouping.class,
+ properties);
+ boolean isGroupingNameUnique = orgGroupings.isEmpty();
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.put("isGroupingNameUnique", isGroupingNameUnique);
+ response.setContentType("application/json;charset=utf-8");
+ response.getWriter().write(responseJSON.toString());
+ return null;
+ }
+
+ /**
+ * Checks if a group can be removed and performs it.
+ */
+ public ActionForward removeGroup(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+ response.setContentType("application/json;charset=utf-8");
+ Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID);
+ Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID);
+ IMonitoringService monitoringService = MonitoringServiceProxy
+ .getMonitoringService(getServlet().getServletContext());
+ boolean result = true;
+
+ // check if the group can be removed
+ Group group = (Group) MonitoringServiceProxy.getUserManagementService(getServlet().getServletContext())
+ .findById(Group.class, groupID);
+ result = group.mayBeDeleted();
+
+ if (result) {
+ try {
+ if (LamsDispatchAction.log.isDebugEnabled()) {
+ LamsDispatchAction.log.debug("Removing group " + groupID + " from activity " + activityID);
+ }
+ monitoringService.removeGroup(activityID, groupID);
+ } catch (LessonServiceException e) {
+ LamsDispatchAction.log.error(e);
+ result = false;
+ }
+ }
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.put("result", result);
+ response.getWriter().write(responseJSON.toString());
+ return null;
+ }
+
+ private ISecurityService getSecurityService() {
+ if (securityService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(getServlet().getServletContext());
+ securityService = (ISecurityService) ctx.getBean("securityService");
+ }
+ return securityService;
+ }
+}
Fisheye: Tag 6e7ab7890111cde84e6557c3507a1e9d3682e318 refers to a dead (removed) revision in file `lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringController.java
===================================================================
diff -u
--- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringController.java (revision 0)
+++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringController.java (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -0,0 +1,1794 @@
+/****************************************************************
+ * 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.io.PrintWriter;
+import java.security.InvalidParameterException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.log4j.Logger;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.lamsfoundation.lams.authoring.ObjectExtractor;
+import org.lamsfoundation.lams.authoring.service.IAuthoringService;
+import org.lamsfoundation.lams.learning.service.ILearnerService;
+import org.lamsfoundation.lams.learningdesign.Activity;
+import org.lamsfoundation.lams.learningdesign.BranchingActivity;
+import org.lamsfoundation.lams.learningdesign.ChosenBranchingActivity;
+import org.lamsfoundation.lams.learningdesign.ComplexActivity;
+import org.lamsfoundation.lams.learningdesign.ContributionTypes;
+import org.lamsfoundation.lams.learningdesign.Group;
+import org.lamsfoundation.lams.learningdesign.GroupingActivity;
+import org.lamsfoundation.lams.learningdesign.LearningDesign;
+import org.lamsfoundation.lams.learningdesign.OptionsWithSequencesActivity;
+import org.lamsfoundation.lams.learningdesign.SequenceActivity;
+import org.lamsfoundation.lams.learningdesign.ToolActivity;
+import org.lamsfoundation.lams.learningdesign.Transition;
+import org.lamsfoundation.lams.learningdesign.exception.LearningDesignException;
+import org.lamsfoundation.lams.lesson.LearnerProgress;
+import org.lamsfoundation.lams.lesson.Lesson;
+import org.lamsfoundation.lams.lesson.dto.LessonDetailsDTO;
+import org.lamsfoundation.lams.lesson.service.ILessonService;
+import org.lamsfoundation.lams.logevent.LogEvent;
+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.service.IMonitoringService;
+import org.lamsfoundation.lams.monitoring.service.MonitoringServiceProxy;
+import org.lamsfoundation.lams.security.ISecurityService;
+import org.lamsfoundation.lams.tool.exception.LamsToolServiceException;
+import org.lamsfoundation.lams.tool.service.ILamsToolService;
+import org.lamsfoundation.lams.usermanagement.Organisation;
+import org.lamsfoundation.lams.usermanagement.Role;
+import org.lamsfoundation.lams.usermanagement.User;
+import org.lamsfoundation.lams.usermanagement.dto.UserDTO;
+import org.lamsfoundation.lams.usermanagement.exception.UserException;
+import org.lamsfoundation.lams.usermanagement.service.IUserManagementService;
+import org.lamsfoundation.lams.util.CentralConstants;
+import org.lamsfoundation.lams.util.DateUtil;
+import org.lamsfoundation.lams.util.JsonUtil;
+import org.lamsfoundation.lams.util.MessageService;
+import org.lamsfoundation.lams.util.ValidationUtil;
+import org.lamsfoundation.lams.util.WebUtil;
+import org.lamsfoundation.lams.web.session.SessionManager;
+import org.lamsfoundation.lams.web.util.AttributeNames;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+import org.springframework.web.util.HtmlUtils;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * The action servlet that provide all the monitoring functionalities. It interact with the teacher via JSP monitoring
+ * interface.
+ *
+ * @author Jacky Fang
+ */
+@Controller
+@RequestMapping("/monitoring")
+public class MonitoringController {
+
+ @Autowired
+ private WebApplicationContext applicationContext;
+
+ private static Logger log = Logger.getLogger(MonitoringController.class);
+
+ // ---------------------------------------------------------------------
+ // Instance variables
+ // ---------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------
+ // Class level constants - Struts forward
+ // ---------------------------------------------------------------------
+ private static final String NOT_SUPPORTED_SCREEN = "notsupported";
+ private static final String TIME_CHART_SCREEN = "timeChart";
+ private static final String ERROR = "error";
+ private static final DateFormat LESSON_SCHEDULING_DATETIME_FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm");
+
+ private static final int LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT = 53;
+ private static final int LATEST_LEARNER_PROGRESS_ACTIVITY_DISPLAY_LIMIT = 7;
+ private static final int USER_PAGE_SIZE = 10;
+
+ private static ILogEventService logEventService;
+
+ private static ILessonService lessonService;
+
+ private static ISecurityService securityService;
+
+ private static IMonitoringService monitoringService;
+
+ private static IUserManagementService userManagementService;
+
+ private static ILearnerService learnerService;
+
+ private static ILamsToolService toolService;
+
+ private static MessageService messageService;
+
+ private Integer getUserId() {
+ HttpSession ss = SessionManager.getSession();
+ UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER);
+ return user != null ? user.getUserID() : null;
+ }
+
+ private String redirectToURL(HttpServletResponse response, String url) throws IOException {
+ if (url == null) {
+ return "notsupported";
+ }
+
+ String fullURL = WebUtil.convertToFullURL(url);
+ response.sendRedirect(response.encodeRedirectURL(fullURL));
+ return null;
+ }
+
+ /**
+ * Initializes a lesson for specific learning design with the given lesson title and lesson description. If
+ * initialization is successful, this method will the ID of new lesson.
+ *
+ * Currently used only in TestHarness and Authoring Preview.
+ */
+ @RequestMapping("/initializeLesson")
+ public String initializeLesson(HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ String title = WebUtil.readStrParam(request, "lessonName");
+ if (title == null) {
+ title = "lesson";
+ }
+ String desc = WebUtil.readStrParam(request, "lessonDescription", true);
+ if (desc == null) {
+ desc = "description";
+ }
+ Integer organisationId = WebUtil.readIntParam(request, "organisationID", true);
+ long ldId = WebUtil.readLongParam(request, AttributeNames.PARAM_LEARNINGDESIGN_ID);
+ Integer copyType = WebUtil.readIntParam(request, "copyType", true);
+ String customCSV = request.getParameter("customCSV");
+ Boolean learnerPresenceAvailable = WebUtil.readBooleanParam(request, "learnerPresenceAvailable", false);
+ Boolean learnerImAvailable = WebUtil.readBooleanParam(request, "learnerImAvailable", false);
+ Boolean liveEditEnabled = WebUtil.readBooleanParam(request, "liveEditEnabled", false);
+ Boolean forceRestart = WebUtil.readBooleanParam(request, "forceRestart", false);
+ Boolean allowRestart = WebUtil.readBooleanParam(request, "allowRestart", false);
+ boolean gradebookOnComplete = WebUtil.readBooleanParam(request, "gradebookOnComplete", false);
+
+ Lesson newLesson = null;
+ if ((copyType != null) && copyType.equals(LearningDesign.COPY_TYPE_PREVIEW)) {
+ newLesson = getMonitoringService().initializeLessonForPreview(title, desc, ldId, getUserId(), customCSV,
+ learnerPresenceAvailable, learnerImAvailable, liveEditEnabled);
+ } else {
+ try {
+ newLesson = getMonitoringService().initializeLesson(title, desc, ldId, organisationId, getUserId(),
+ customCSV, false, false, learnerPresenceAvailable, learnerImAvailable, liveEditEnabled, false,
+ forceRestart, allowRestart, gradebookOnComplete, null, null);
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the organisation");
+ return null;
+ }
+ }
+
+ return (newLesson.getLessonId()).toString();
+ }
+
+ /**
+ * The Struts dispatch method that starts a lesson that has been created beforehand. Most likely, the request to
+ * start lesson should be triggered by the Monitoring This method will delegate to the Spring service bean to
+ * complete all the steps for starting a lesson.
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
+ @RequestMapping("/startLesson")
+ public String startLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ try {
+ getMonitoringService().startLesson(lessonId, getUserId());
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ response.setContentType("text/plain;charset=utf-8");
+ response.getWriter().write("true");
+ return null;
+ }
+
+ @RequestMapping("/")
+ public String createLessonClass(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ int organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID);
+ Integer userID = WebUtil.readIntParam(request, AttributeNames.PARAM_USER_ID);
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+
+ IUserManagementService userManagementService = MonitoringServiceProxy
+ .getUserManagementService(applicationContext.getServletContext());
+
+ Organisation organisation = (Organisation) userManagementService.findById(Organisation.class, organisationId);
+ List allUsers = userManagementService.getUsersFromOrganisation(organisationId);
+ String learnerGroupName = organisation.getName() + " learners";
+ String staffGroupName = organisation.getName() + " staff";
+ List learners = parseUserList(request, "learners", allUsers);
+ List staff = parseUserList(request, "monitors", allUsers);
+
+ try {
+ getMonitoringService().createLessonClassForLesson(lessonId, organisation, learnerGroupName, learners,
+ staffGroupName, staff, userID);
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "The user is not a monitor in the lesson");
+ return null;
+ }
+
+ return null;
+ }
+
+ @RequestMapping("/")
+ public String addLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException, ParseException {
+ String lessonName = request.getParameter("lessonName");
+ if (!ValidationUtil.isOrgNameValid(lessonName)) {
+ throw new IOException("Lesson name contains invalid characters");
+ }
+
+ int organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID);
+ long ldId = WebUtil.readLongParam(request, AttributeNames.PARAM_LEARNINGDESIGN_ID);
+
+ boolean introEnable = WebUtil.readBooleanParam(request, "introEnable", false);
+ String introDescription = introEnable ? request.getParameter("introDescription") : null;
+ boolean introImage = introEnable && WebUtil.readBooleanParam(request, "introImage", false);
+ boolean startMonitor = WebUtil.readBooleanParam(request, "startMonitor", false);
+ boolean enableLiveEdit = WebUtil.readBooleanParam(request, "liveEditEnable", false);
+ boolean notificationsEnable = WebUtil.readBooleanParam(request, "notificationsEnable", false);
+ boolean presenceEnable = WebUtil.readBooleanParam(request, "presenceEnable", false);
+ boolean imEnable = WebUtil.readBooleanParam(request, "imEnable", false);
+ Integer splitNumberLessons = WebUtil.readIntParam(request, "splitNumberLessons", true);
+ boolean schedulingEnable = WebUtil.readBooleanParam(request, "schedulingEnable", false);
+ Date schedulingDatetime = null;
+ Date schedulingEndDatetime = null;
+ if (schedulingEnable) {
+ String dateString = request.getParameter("schedulingDatetime");
+ if (dateString != null && dateString.length() > 0) {
+ schedulingDatetime = MonitoringController.LESSON_SCHEDULING_DATETIME_FORMAT.parse(dateString);
+ }
+ dateString = request.getParameter("schedulingEndDatetime");
+ if (dateString != null && dateString.length() > 0) {
+ schedulingEndDatetime = MonitoringController.LESSON_SCHEDULING_DATETIME_FORMAT.parse(dateString);
+ }
+ }
+ boolean forceRestart = WebUtil.readBooleanParam(request, "forceRestart", false);
+ boolean allowRestart = WebUtil.readBooleanParam(request, "allowRestart", false);
+
+ boolean precedingLessonEnable = WebUtil.readBooleanParam(request, "precedingLessonEnable", false);
+ Long precedingLessonId = precedingLessonEnable ? WebUtil.readLongParam(request, "precedingLessonId", true)
+ : null;
+ boolean timeLimitEnable = WebUtil.readBooleanParam(request, "timeLimitEnable", false);
+ Integer timeLimitDays = WebUtil.readIntParam(request, "timeLimitDays", true);
+ boolean timeLimitIndividualField = WebUtil.readBooleanParam(request, "timeLimitIndividual", false);
+ Integer timeLimitIndividual = timeLimitEnable && timeLimitIndividualField ? timeLimitDays : null;
+ Integer timeLimitLesson = timeLimitEnable && !timeLimitIndividualField ? timeLimitDays : null;
+ boolean gradebookOnComplete = WebUtil.readBooleanParam(request, "gradebookOnComplete", false);
+
+ IUserManagementService userManagementService = MonitoringServiceProxy
+ .getUserManagementService(applicationContext.getServletContext());
+
+ Organisation organisation = (Organisation) userManagementService.findById(Organisation.class, organisationId);
+ Integer userId = getUserId();
+ User creator = (User) userManagementService.findById(User.class, userId);
+
+ List allUsers = userManagementService.getUsersFromOrganisation(organisationId);
+ List learners = parseUserList(request, "learners", allUsers);
+ String learnerGroupName = organisation.getName() + " learners";
+
+ List staff = parseUserList(request, "monitors", allUsers);
+ // add the creator as staff, if not already done
+ if (!staff.contains(creator)) {
+ staff.add(creator);
+ }
+ String staffGroupName = organisation.getName() + " staff";
+
+ // either all users participate in a lesson, or we split them among instances
+ List lessonInstanceLearners = splitNumberLessons == null ? learners
+ : new ArrayList<>((learners.size() / splitNumberLessons) + 1);
+ for (int lessonIndex = 1; lessonIndex <= (splitNumberLessons == null ? 1 : splitNumberLessons); lessonIndex++) {
+ String lessonInstanceName = lessonName;
+ String learnerGroupInstanceName = learnerGroupName;
+ String staffGroupInstanceName = staffGroupName;
+
+ if (splitNumberLessons != null) {
+ // prepare data for lesson split
+ lessonInstanceName += " " + lessonIndex;
+ learnerGroupInstanceName += " " + lessonIndex;
+ staffGroupInstanceName += " " + lessonIndex;
+ lessonInstanceLearners.clear();
+ for (int learnerIndex = lessonIndex - 1; learnerIndex < learners
+ .size(); learnerIndex += splitNumberLessons) {
+ lessonInstanceLearners.add(learners.get(learnerIndex));
+ }
+ }
+
+ if (MonitoringController.log.isDebugEnabled()) {
+ MonitoringController.log.debug("Creating lesson "
+ + (splitNumberLessons == null ? "" : "(" + lessonIndex + "/" + splitNumberLessons + ") ") + "\""
+ + lessonInstanceName + "\"");
+ }
+
+ Lesson lesson = null;
+ try {
+ lesson = getMonitoringService().initializeLesson(lessonInstanceName, introDescription, ldId,
+ organisationId, userId, null, introEnable, introImage, presenceEnable, imEnable, enableLiveEdit,
+ notificationsEnable, forceRestart, allowRestart, gradebookOnComplete, timeLimitIndividual,
+ precedingLessonId);
+
+ getMonitoringService().createLessonClassForLesson(lesson.getLessonId(), organisation,
+ learnerGroupInstanceName, lessonInstanceLearners, staffGroupInstanceName, staff, userId);
+ } catch (SecurityException e) {
+ try {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN,
+ "User is not a monitor in the organisation or lesson");
+ } catch (IllegalStateException e1) {
+ MonitoringController.log
+ .warn("Tried to tell user that \"User is not a monitor in the organisation or lesson\","
+ + "but the HTTP response was already written, probably by some other error");
+ }
+ return null;
+ }
+
+ if (!startMonitor) {
+ try {
+ if (schedulingDatetime == null) {
+ getMonitoringService().startLesson(lesson.getLessonId(), userId);
+ } else {
+ // if lesson should start in few days, set it here
+ getMonitoringService().startLessonOnSchedule(lesson.getLessonId(), schedulingDatetime, userId);
+ }
+
+ // monitor has given an end date/time for the lesson
+ if (schedulingEndDatetime != null) {
+ getMonitoringService().finishLessonOnSchedule(lesson.getLessonId(), schedulingEndDatetime,
+ userId);
+ // if lesson should finish in few days, set it here
+ } else if (timeLimitLesson != null) {
+ getMonitoringService().finishLessonOnSchedule(lesson.getLessonId(), timeLimitLesson, userId);
+ }
+
+ } catch (SecurityException e) {
+ try {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ } catch (IllegalStateException e1) {
+ MonitoringController.log.warn("Tried to tell user that \"User is not a monitor in the lesson\","
+ + "but the HTTP response was already written, probably by some other error");
+ }
+ return null;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds all course learners to the given lesson.
+ */
+ @RequestMapping("/")
+ public String addAllOrganisationLearnersToLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+ Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ if (!getSecurityService().isLessonMonitor(lessonId, getUserId(), "add all lesson learners to lesson", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+ Lesson lesson = getLessonService().getLesson(lessonId);
+ Vector learners = getUserManagementService()
+ .getUsersFromOrganisationByRole(lesson.getOrganisation().getOrganisationId(), Role.LEARNER, true);
+ getLessonService().addLearners(lesson, learners);
+ return null;
+ }
+
+ @RequestMapping("/")
+ public String startOnScheduleLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws ParseException, IOException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ String dateStr = WebUtil.readStrParam(request, MonitoringConstants.PARAM_LESSON_START_DATE);
+ Date startDate = MonitoringController.LESSON_SCHEDULING_DATETIME_FORMAT.parse(dateStr);
+ try {
+ getMonitoringService().startLessonOnSchedule(lessonId, startDate, getUserId());
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ }
+
+ return null;
+ }
+
+ /**
+ * The Struts dispatch method to archive a lesson.
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
+ @RequestMapping("/")
+ public String archiveLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ try {
+ getMonitoringService().archiveLesson(lessonId, getUserId());
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ }
+
+ return null;
+ }
+
+ /**
+ * The Struts dispatch method to "unarchive" a lesson. Returns it back to its previous state.
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
+ @RequestMapping("/")
+ public String unarchiveLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ try {
+ getMonitoringService().unarchiveLesson(lessonId, getUserId());
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ }
+ return null;
+ }
+
+ /**
+ * The purpose of suspending is to hide the lesson from learners temporarily. It doesn't make any sense to suspend a
+ * created or a not started (ie scheduled) lesson as they will not be shown on the learner interface anyway! If the
+ * teacher tries to suspend a lesson that is not in the STARTED_STATE, then an error should be returned to UI.
+ */
+ @RequestMapping("/")
+ public String suspendLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException, ParseException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ String dateStr = WebUtil.readStrParam(request, MonitoringConstants.PARAM_LESSON_END_DATE, true);
+ try {
+ if (dateStr == null || dateStr.length() == 0) {
+ getMonitoringService().suspendLesson(lessonId, getUserId(), true);
+ } else {
+ getMonitoringService().finishLessonOnSchedule(lessonId,
+ MonitoringController.LESSON_SCHEDULING_DATETIME_FORMAT.parse(dateStr), getUserId());
+ }
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ }
+ return null;
+ }
+
+ /**
+ * Unsuspend a lesson which state must be Lesson.SUPSENDED_STATE. Otherwise a error message will return to UI
+ * client.
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ * @throws IOException
+ * @throws ServletException
+ */
+ @RequestMapping("/")
+ public String unsuspendLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ try {
+ getMonitoringService().unsuspendLesson(lessonId, getUserId());
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ }
+ return null;
+ }
+
+ /**
+ *
+ * The STRUTS action will send back a JSON message after marking the lesson by the given lesson ID as
+ * Lesson.REMOVED_STATE
status.
+ *
+ *
+ * This action need a lession ID as input.
+ *
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
+ @RequestMapping("/")
+ public String removeLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ Integer userId = getUserId();
+ boolean permanently = WebUtil.readBooleanParam(request, "permanently", false);
+
+ if (permanently) {
+ getMonitoringService().removeLessonPermanently(lessonId, userId);
+ response.setContentType("text/plain;charset=utf-8");
+ response.getWriter().print("OK");
+ return null;
+ }
+
+ ObjectNode jsonObject = JsonNodeFactory.instance.objectNode();
+
+ try {
+ // if this method throws an Exception, there will be no removeLesson=true in the JSON reply
+ getMonitoringService().removeLesson(lessonId, userId);
+ jsonObject.put("removeLesson", true);
+
+ } catch (Exception e) {
+ String[] msg = new String[1];
+ msg[0] = e.getMessage();
+ jsonObject.put("removeLesson",
+ getMonitoringService().getMessageService().getMessage("error.system.error", msg));
+ }
+
+ response.setContentType("application/json;charset=utf-8");
+ response.getWriter().print(jsonObject);
+ return null;
+ }
+
+ /**
+ *
+ * This action need a lession ID, Learner ID and Activity ID as input. Activity ID is optional, if it is null, all
+ * activities for this learner will complete to as end as possible.
+ *
+ *
+ * @param form
+ * @param request
+ * @param response
+ * @return An ActionForward
+ * @throws IOException
+ * @throws ServletException
+ * @throws JSONException
+ */
+ @RequestMapping("/")
+ public String forceComplete(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ // get parameters
+ Long activityId = null;
+ String actId = request.getParameter(AttributeNames.PARAM_ACTIVITY_ID);
+ if (actId != null) {
+ try {
+ activityId = new Long(Long.parseLong(actId));
+ } catch (Exception e) {
+ activityId = null;
+ }
+ }
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ String learnerIDs = request.getParameter(MonitoringConstants.PARAM_LEARNER_ID);
+ Integer requesterId = getUserId();
+ boolean removeLearnerContent = WebUtil.readBooleanParam(request,
+ MonitoringConstants.PARAM_REMOVE_LEARNER_CONTENT, false);
+
+ ObjectNode jsonCommand = JsonNodeFactory.instance.objectNode();
+ jsonCommand.put("message", getMessageService().getMessage("force.complete.learner.command.message"));
+ jsonCommand.put("redirectURL", "/lams/learning/learner.do?method=joinLesson&lessonID=" + lessonId);
+ String command = jsonCommand.toString();
+
+ String activityDescription = null;
+ if (activityId != null) {
+ Activity activity = getMonitoringService().getActivityById(activityId);
+ activityDescription = new StringBuffer(activity.getTitle()).append(" (").append(activityId).append(")")
+ .toString();
+ }
+
+ StringBuffer learnerIdNameBuf = new StringBuffer();
+ String message = null;
+ User learner = null;
+ boolean oneOrMoreProcessed = false;
+ try {
+ for (String learnerIDString : learnerIDs.split(",")) {
+ if (oneOrMoreProcessed) {
+ learnerIdNameBuf.append(", ");
+ } else {
+ oneOrMoreProcessed = true;
+ }
+ Integer learnerID = Integer.valueOf(learnerIDString);
+ message = getMonitoringService().forceCompleteActivitiesByUser(learnerID, requesterId, lessonId,
+ activityId, removeLearnerContent);
+
+ learner = (User) getUserManagementService().findById(User.class, learnerID);
+ getLearnerService().createCommandForLearner(lessonId, learner.getLogin(), command);
+ learnerIdNameBuf.append(learner.getLogin()).append(" (").append(learnerID).append(")");
+ }
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ if (MonitoringController.log.isDebugEnabled()) {
+ MonitoringController.log.debug("Force complete for learners " + learnerIdNameBuf.toString() + " lesson "
+ + lessonId + ". " + message);
+ }
+
+ // audit log force completion attempt
+ String messageKey = (activityId == null) ? "audit.force.complete.end.lesson" : "audit.force.complete";
+
+ Object[] args = new Object[] { learnerIdNameBuf.toString(), activityDescription, lessonId };
+ String auditMessage = getMonitoringService().getMessageService().getMessage(messageKey, args);
+ getLogEventService().logEvent(LogEvent.TYPE_FORCE_COMPLETE, requesterId, null, lessonId, activityId,
+ auditMessage + " " + message);
+
+ PrintWriter writer = response.getWriter();
+ writer.println(message);
+ return null;
+ }
+
+ /**
+ * Get learners who are part of the lesson class.
+ */
+ @RequestMapping("/getLessonLearners")
+ public String getLessonLearners(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ if (!getSecurityService().isLessonMonitor(lessonId, getUserId(), "get lesson learners", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ String searchPhrase = request.getParameter("searchPhrase");
+ Integer pageNumber = WebUtil.readIntParam(request, "pageNumber", true);
+ if (pageNumber == null) {
+ pageNumber = 1;
+ }
+ boolean orderAscending = WebUtil.readBooleanParam(request, "orderAscending", true);
+
+ List learners = getLessonService().getLessonLearners(lessonId, searchPhrase,
+ MonitoringController.USER_PAGE_SIZE, (pageNumber - 1) * MonitoringController.USER_PAGE_SIZE,
+ orderAscending);
+ ArrayNode learnersJSON = JsonNodeFactory.instance.arrayNode();
+ for (User learner : learners) {
+ learnersJSON.add(WebUtil.userToJSON(learner));
+ }
+
+ Integer learnerCount = getLessonService().getCountLessonLearners(lessonId, searchPhrase);
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.set("learners", learnersJSON);
+ responseJSON.put("learnerCount", learnerCount);
+ response.setContentType("application/json;charset=utf-8");
+ return responseJSON.toString();
+ }
+
+ /**
+ * Gets learners or monitors of the lesson and organisation containing it.
+ */
+ @RequestMapping("/getClassMembers")
+ @ResponseBody
+ public String getClassMembers(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ if (!getSecurityService().isLessonMonitor(lessonId, getUserId(), "get class members", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ Lesson lesson = getLessonService().getLesson(lessonId);
+ String role = WebUtil.readStrParam(request, AttributeNames.PARAM_ROLE);
+ boolean isMonitor = role.equals(Role.MONITOR);
+ User creator = isMonitor ? lesson.getUser() : null;
+ Integer currentUserId = isMonitor ? getUserId() : null;
+ String searchPhrase = request.getParameter("searchPhrase");
+ Integer pageNumber = WebUtil.readIntParam(request, "pageNumber", true);
+ if (pageNumber == null) {
+ pageNumber = 1;
+ }
+ boolean orderAscending = WebUtil.readBooleanParam(request, "orderAscending", true);
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+
+ // find organisation users and whether they participate in the current lesson
+ Map users = getLessonService().getUsersWithLessonParticipation(lessonId, role, searchPhrase,
+ MonitoringController.USER_PAGE_SIZE, (pageNumber - 1) * MonitoringController.USER_PAGE_SIZE,
+ orderAscending);
+
+ // if the result is less then page size, then no need for full check of user count
+ Integer userCount = users.size() < MonitoringController.USER_PAGE_SIZE ? users.size()
+ : getUserManagementService().getCountRoleForOrg(lesson.getOrganisation().getOrganisationId(),
+ isMonitor ? Role.ROLE_MONITOR : Role.ROLE_LEARNER, searchPhrase);
+
+ responseJSON.put("userCount", userCount);
+
+ ArrayNode usersJSON = JsonNodeFactory.instance.arrayNode();
+ for (Entry userEntry : users.entrySet()) {
+ User user = userEntry.getKey();
+ ObjectNode userJSON = WebUtil.userToJSON(user);
+ userJSON.put("classMember", userEntry.getValue());
+ // teacher can't remove lesson creator and himself from the lesson staff
+ if (isMonitor && (creator.getUserId().equals(user.getUserId()) || currentUserId.equals(user.getUserId()))) {
+ userJSON.put("readonly", true);
+ }
+ usersJSON.add(userJSON);
+ }
+ responseJSON.set("users", usersJSON);
+
+ response.setContentType("application/json;charset=utf-8");
+ return responseJSON.toString();
+ }
+
+ /**
+ * Gets users in JSON format who are at the given activity at the moment or finished the given lesson.
+ */
+ @RequestMapping("/getCurrentLearners")
+ @ResponseBody
+ public String getCurrentLearners(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+ ArrayNode learnersJSON = JsonNodeFactory.instance.arrayNode();
+ Integer learnerCount = null;
+
+ Integer pageNumber = WebUtil.readIntParam(request, "pageNumber", true);
+ if (pageNumber == null) {
+ pageNumber = 1;
+ }
+ boolean orderAscending = WebUtil.readBooleanParam(request, "orderAscending", true);
+ // if activity ID is provided, lesson ID is ignored
+ Long activityId = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID, true);
+ if (activityId == null) {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ if (!getSecurityService().isLessonMonitor(lessonId, getUserId(), "get lesson completed learners", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ List learners = getMonitoringService().getUsersCompletedLesson(lessonId,
+ MonitoringController.USER_PAGE_SIZE, (pageNumber - 1) * MonitoringController.USER_PAGE_SIZE,
+ orderAscending);
+ for (User learner : learners) {
+ learnersJSON.add(WebUtil.userToJSON(learner));
+ }
+
+ learnerCount = getMonitoringService().getCountLearnersCompletedLesson(lessonId);
+ } else {
+ Activity activity = getMonitoringService().getActivityById(activityId);
+ Lesson lesson = (Lesson) activity.getLearningDesign().getLessons().iterator().next();
+ if (!getSecurityService().isLessonMonitor(lesson.getLessonId(), getUserId(), "get activity learners",
+ false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ Set activities = new TreeSet<>();
+ activities.add(activityId);
+
+ List learners = getMonitoringService().getLearnersByActivities(activities.toArray(new Long[] {}),
+ MonitoringController.USER_PAGE_SIZE, (pageNumber - 1) * MonitoringController.USER_PAGE_SIZE,
+ orderAscending);
+ for (User learner : learners) {
+ learnersJSON.add(WebUtil.userToJSON(learner));
+ }
+ learnerCount = getMonitoringService().getCountLearnersCurrentActivities(new Long[] { activityId })
+ .get(activityId);
+ }
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.set("learners", learnersJSON);
+ responseJSON.put("learnerCount", learnerCount);
+ response.setContentType("application/json;charset=utf-8");
+ return responseJSON.toString();
+ }
+
+ /**
+ * Adds/removes learners and monitors to/from lesson class.
+ */
+ @RequestMapping("/updateLessonClass")
+ public String updateLessonClass(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ if (!getSecurityService().isLessonMonitor(lessonId, getUserId(), "update lesson class", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ int userId = WebUtil.readIntParam(request, AttributeNames.PARAM_USER_ID);
+ String role = WebUtil.readStrParam(request, AttributeNames.PARAM_ROLE);
+ boolean add = WebUtil.readBooleanParam(request, "add");
+ boolean result = false;
+
+ if (role.equals(Role.MONITOR)) {
+ if (add) {
+ result = getLessonService().addStaffMember(lessonId, userId);
+ } else {
+ result = getLessonService().removeStaffMember(lessonId, userId);
+ }
+ } else if (role.equals(Role.LEARNER)) {
+ if (add) {
+ result = getLessonService().addLearner(lessonId, userId);
+ } else {
+ getLessonService().removeLearnerProgress(lessonId, userId);
+ result = getLessonService().removeLearner(lessonId, userId);
+ }
+ }
+
+ if (result) {
+ MonitoringController.log.info((add ? "Added a " : "Removed a ") + role + " with ID " + userId
+ + (add ? " to" : " from") + " lesson " + lessonId);
+ } else {
+ MonitoringController.log.warn("Failed when trying to " + (add ? "add a " : "remove a ") + role + " with ID "
+ + userId + (add ? " to" : " from") + " lesson " + lessonId);
+ }
+
+ return null;
+ }
+
+ @RequestMapping("/getDictionaryXML")
+ public String getDictionaryXML(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ MessageService messageService = getMonitoringService().getMessageService();
+
+ String module = WebUtil.readStrParam(request, "module", false);
+
+ ArrayList languageCollection = new ArrayList<>();
+ if (module.equals("timechart")) {
+
+ languageCollection.add(new String("sys.error"));
+ languageCollection.add(new String("chart.btn.activity.split"));
+ languageCollection.add(new String("chart.btn_completion.rate"));
+ languageCollection.add(new String("chart.series.completed.time"));
+ languageCollection.add(new String("chart.series.average.time"));
+ languageCollection.add(new String("chart.series.duration"));
+ languageCollection.add(new String("chart.legend.average"));
+ languageCollection.add(new String("show.average.checkbox"));
+ languageCollection.add(new String("search.learner.textbox"));
+ languageCollection.add(new String("chart.learner.linear.axis.title"));
+ languageCollection.add(new String("chart.learner.category.axis.title"));
+ languageCollection.add(new String("chart.learner.datatip.average"));
+
+ languageCollection.add(new String("label.learner"));
+ languageCollection.add(new String("time.chart.panel.title"));
+ languageCollection.add(new String("chart.time.format.hours"));
+ languageCollection.add(new String("chart.time.format.minutes"));
+ languageCollection.add(new String("chart.time.format.seconds"));
+ languageCollection.add(new String("label.completed"));
+ languageCollection.add(new String("advanced.tab.form.validation.schedule.date.error"));
+ languageCollection.add(new String("alert.no.learner.data"));
+ }
+
+ String languageOutput = "";
+
+ for (int i = 0; i < languageCollection.size(); i++) {
+ languageOutput += ""
+ + messageService.getMessage(languageCollection.get(i)) + "";
+ }
+
+ languageOutput += "";
+
+ response.setContentType("text/xml");
+ response.setCharacterEncoding("UTF-8");
+
+ return languageOutput;
+ }
+
+ /**
+ * Calls the server to bring up the learner progress page. Assumes destination is a new window. The userid that
+ * comes from UI is the user id of the learner for which we are calculating the url. This is different to all the
+ * other calls.
+ */
+ @RequestMapping("/")
+ public String getLearnerActivityURL(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, LamsToolServiceException {
+
+ Integer learnerUserID = new Integer(WebUtil.readIntParam(request, AttributeNames.PARAM_USER_ID));
+ Long activityID = new Long(WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID));
+ Long lessonID = new Long(WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID));
+ try {
+ String url = getMonitoringService().getLearnerActivityURL(lessonID, activityID, learnerUserID, getUserId());
+ return redirectToURL(response, url);
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+ }
+
+ /** Calls the server to bring up the activity's monitoring page. Assumes destination is a new window */
+ @RequestMapping("/getActivityMonitorURL")
+ @ResponseBody
+ public String getActivityMonitorURL(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, LamsToolServiceException {
+ Long activityID = new Long(WebUtil.readLongParam(request, "activityID"));
+ Long lessonID = new Long(WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID));
+ String contentFolderID = WebUtil.readStrParam(request, "contentFolderID");
+ try {
+ String url = getMonitoringService().getActivityMonitorURL(lessonID, activityID, contentFolderID,
+ getUserId());
+ return redirectToURL(response, url);
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+ }
+
+ /**
+ * Displays Monitor Lesson page.
+ */
+ @RequestMapping("/monitorLesson")
+ public String monitorLesson(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ LessonDetailsDTO lessonDTO = getLessonService().getLessonDetails(lessonId);
+
+ HttpSession ss = SessionManager.getSession();
+ UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER);
+ if (lessonDTO.getCreateDateTime() != null) {
+ DateFormat sfm = new SimpleDateFormat("yyyyMMdd_HHmmss");
+ lessonDTO.setCreateDateTimeStr(sfm.format(lessonDTO.getCreateDateTime()));
+ }
+ // prepare encoded lessonId for shortened learner URL
+ lessonDTO.setEncodedLessonID(WebUtil.encodeLessonId(lessonId));
+
+ if (!getSecurityService().isLessonMonitor(lessonId, user.getUserID(), "monitor lesson", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ // should info box on Sequence tab be displayed?
+ Short sequenceTabInfoShowCount = (Short) ss.getAttribute("sequenceTabInfoShowCount");
+ if (sequenceTabInfoShowCount == null) {
+ sequenceTabInfoShowCount = 0;
+ }
+ // only few times per session
+ if (sequenceTabInfoShowCount < MonitoringConstants.SEQUENCE_TAB_SHOW_INFO_MAX_COUNT) {
+ sequenceTabInfoShowCount++;
+ ss.setAttribute("sequenceTabInfoShowCount", sequenceTabInfoShowCount);
+ request.setAttribute("sequenceTabShowInfo", true);
+ }
+
+ IUserManagementService userManagementService = MonitoringServiceProxy
+ .getUserManagementService(applicationContext.getServletContext());
+ Organisation organisation = (Organisation) userManagementService.findById(Organisation.class,
+ lessonDTO.getOrganisationID());
+ request.setAttribute("notificationsAvailable", organisation.getEnableCourseNotifications());
+ boolean enableLiveEdit = organisation.getEnableLiveEdit() && getUserManagementService()
+ .isUserInRole(user.getUserID(), organisation.getOrganisationId(), Role.AUTHOR);
+ request.setAttribute("enableLiveEdit", enableLiveEdit);
+ request.setAttribute("lesson", lessonDTO);
+ request.setAttribute("isTBLSequence", isTBLSequence(lessonId));
+
+ return "monitor";
+ }
+
+ /**
+ * If learning design contains the following activities Grouping->(MCQ or Assessment)->Leader Selection->Scratchie
+ * (potentially with some other gates or activities in the middle), there is a good chance this is a TBL sequence
+ * and all activities must be grouped.
+ */
+ private boolean isTBLSequence(Long lessonId) {
+
+ Lesson lesson = getLessonService().getLesson(lessonId);
+ Long firstActivityId = lesson.getLearningDesign().getFirstActivity().getActivityId();
+ //Hibernate CGLIB is failing to load the first activity in the sequence as a ToolActivity
+ Activity firstActivity = getMonitoringService().getActivityById(firstActivityId);
+
+ return verifyNextActivityFitsTbl(firstActivity, "Grouping");
+ }
+
+ /**
+ * Traverses the learning design verifying it follows typical TBL structure
+ *
+ * @param activity
+ * @param anticipatedActivity
+ * could be either "Grouping", "MCQ or Assessment", "Leaderselection" or "Scratchie"
+ */
+ private boolean verifyNextActivityFitsTbl(Activity activity, String anticipatedActivity) {
+
+ Transition transitionFromActivity = activity.getTransitionFrom();
+ //TBL can finish with the Scratchie
+ if (transitionFromActivity == null && !"Scratchie".equals(anticipatedActivity)) {
+ return false;
+ }
+ // query activity from DB as transition holds only proxied activity object
+ Long nextActivityId = transitionFromActivity == null ? null
+ : transitionFromActivity.getToActivity().getActivityId();
+ Activity nextActivity = nextActivityId == null ? null : monitoringService.getActivityById(nextActivityId);
+
+ switch (anticipatedActivity) {
+ case "Grouping":
+ //the first activity should be a grouping
+ if (activity instanceof GroupingActivity) {
+ return verifyNextActivityFitsTbl(nextActivity, "MCQ or Assessment");
+
+ } else {
+ return verifyNextActivityFitsTbl(nextActivity, "Grouping");
+ }
+
+ case "MCQ or Assessment":
+ //the second activity shall be a MCQ or Assessment
+ if (activity.isToolActivity() && (CentralConstants.TOOL_SIGNATURE_ASSESSMENT
+ .equals(((ToolActivity) activity).getTool().getToolSignature())
+ || CentralConstants.TOOL_SIGNATURE_MCQ
+ .equals(((ToolActivity) activity).getTool().getToolSignature()))) {
+ return verifyNextActivityFitsTbl(nextActivity, "Leaderselection");
+
+ } else {
+ return verifyNextActivityFitsTbl(nextActivity, "MCQ or Assessment");
+ }
+
+ case "Leaderselection":
+ //the third activity shall be a Leader Selection
+ if (activity.isToolActivity() && CentralConstants.TOOL_SIGNATURE_LEADERSELECTION
+ .equals(((ToolActivity) activity).getTool().getToolSignature())) {
+ return verifyNextActivityFitsTbl(nextActivity, "Scratchie");
+
+ } else {
+ return verifyNextActivityFitsTbl(nextActivity, "Leaderselection");
+ }
+
+ case "Scratchie":
+ //the fourth activity shall be Scratchie
+ if (activity.isToolActivity() && CentralConstants.TOOL_SIGNATURE_SCRATCHIE
+ .equals(((ToolActivity) activity).getTool().getToolSignature())) {
+ return true;
+
+ } else if (nextActivity == null) {
+ return false;
+
+ } else {
+ return verifyNextActivityFitsTbl(nextActivity, "Scratchie");
+ }
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Gets users whose progress bars will be displayed in Learner tab in Monitor.
+ */
+ @RequestMapping("/getLearnerProgressPage")
+ @ResponseBody
+ public String getLearnerProgressPage(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ if (!getSecurityService().isLessonMonitor(lessonId, getUserId(), "get learner progress page", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ String searchPhrase = request.getParameter("searchPhrase");
+ Integer pageNumber = WebUtil.readIntParam(request, "pageNumber", true);
+ if (pageNumber == null) {
+ pageNumber = 1;
+ }
+ // are the learners sorted by the most completed first?
+ boolean isProgressSorted = WebUtil.readBooleanParam(request, "isProgressSorted", false);
+
+ // either sort by name or how much a learner progressed into the lesson
+ List learners = isProgressSorted
+ ? getMonitoringService().getLearnersByMostProgress(lessonId, searchPhrase, 10, (pageNumber - 1) * 10)
+ : getLessonService().getLessonLearners(lessonId, searchPhrase, 10, (pageNumber - 1) * 10, true);
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ for (User learner : learners) {
+ responseJSON.withArray("learners").add(WebUtil.userToJSON(learner));
+ }
+
+ // get all possible learners matching the given phrase, if any; used for max page number
+ responseJSON.put("learnerPossibleNumber", getLessonService().getCountLessonLearners(lessonId, searchPhrase));
+ response.setContentType("application/json;charset=utf-8");
+ return responseJSON.toString();
+ }
+
+ /**
+ * Produces data to update Lesson tab in Monitor.
+ */
+ @RequestMapping("/getLessonDetails")
+ @ResponseBody
+ public String getLessonDetails(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ HttpSession ss = SessionManager.getSession();
+ UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER);
+
+ if (!getSecurityService().isLessonMonitor(lessonId, user.getUserID(), "get lesson details", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ Lesson lesson = getLessonService().getLesson(lessonId);
+ LearningDesign learningDesign = lesson.getLearningDesign();
+
+ Locale userLocale = new Locale(user.getLocaleLanguage(), user.getLocaleCountry());
+
+ responseJSON.put(AttributeNames.PARAM_LEARNINGDESIGN_ID, learningDesign.getLearningDesignId());
+ responseJSON.put("numberPossibleLearners", getLessonService().getCountLessonLearners(lessonId, null));
+ responseJSON.put("lessonStateID", lesson.getLessonStateId());
+
+ responseJSON.put("lessonName", HtmlUtils.htmlEscape(lesson.getLessonName()));
+ responseJSON.put("lessonDescription", lesson.getLessonDescription());
+
+ Date startOrScheduleDate = lesson.getStartDateTime() == null ? lesson.getScheduleStartDate()
+ : lesson.getStartDateTime();
+ Date finishDate = lesson.getScheduleEndDate();
+ DateFormat indfm = null;
+
+ if (startOrScheduleDate != null || finishDate != null) {
+ indfm = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", userLocale);
+ }
+
+ if (startOrScheduleDate != null) {
+ Date tzStartDate = DateUtil.convertToTimeZoneFromDefault(user.getTimeZone(), startOrScheduleDate);
+ responseJSON.put("startDate",
+ indfm.format(tzStartDate) + " " + user.getTimeZone().getDisplayName(userLocale));
+ }
+
+ if (finishDate != null) {
+ Date tzFinishDate = DateUtil.convertToTimeZoneFromDefault(user.getTimeZone(), finishDate);
+ responseJSON.put("finishDate",
+ indfm.format(tzFinishDate) + " " + user.getTimeZone().getDisplayName(userLocale));
+ }
+
+ List contributeActivities = getContributeActivities(lessonId, false);
+ if (contributeActivities != null) {
+ responseJSON.set("contributeActivities", JsonUtil.readArray(contributeActivities));
+ }
+
+ response.setContentType("application/json;charset=utf-8");
+ return responseJSON.toString();
+ }
+
+ @RequestMapping("/getLessonChartData")
+ @ResponseBody
+ public String getLessonChartData(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+
+ Integer possibleLearnersCount = getLessonService().getCountLessonLearners(lessonId, null);
+ Integer completedLearnersCount = getMonitoringService().getCountLearnersCompletedLesson(lessonId);
+ Integer startedLearnersCount = getLessonService().getCountActiveLessonLearners(lessonId)
+ - completedLearnersCount;
+ Integer notCompletedLearnersCount = possibleLearnersCount - completedLearnersCount - startedLearnersCount;
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ ObjectNode notStartedJSON = JsonNodeFactory.instance.objectNode();
+ notStartedJSON.put("name", getMessageService().getMessage("lesson.chart.not.completed"));
+ notStartedJSON.put("value", Math.round(notCompletedLearnersCount.doubleValue() / possibleLearnersCount * 100));
+ responseJSON.withArray("data").add(notStartedJSON);
+
+ ObjectNode startedJSON = JsonNodeFactory.instance.objectNode();
+ startedJSON.put("name", getMessageService().getMessage("lesson.chart.started"));
+ startedJSON.put("value", Math.round((startedLearnersCount.doubleValue()) / possibleLearnersCount * 100));
+ responseJSON.withArray("data").add(startedJSON);
+
+ ObjectNode completedJSON = JsonNodeFactory.instance.objectNode();
+ completedJSON.put("name", getMessageService().getMessage("lesson.chart.completed"));
+ completedJSON.put("value", Math.round(completedLearnersCount.doubleValue() / possibleLearnersCount * 100));
+ responseJSON.withArray("data").add(completedJSON);
+
+ response.setContentType("application/json;charset=utf-8");
+ return responseJSON.toString();
+ }
+
+ /**
+ * Produces data for Sequence tab in Monitor.
+ */
+ @RequestMapping("/getLessonProgress")
+ @ResponseBody
+ public String getLessonProgress(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ Integer monitorUserId = 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;
+ }
+ Integer searchedLearnerId = WebUtil.readIntParam(request, "searchedLearnerId", true);
+
+ Lesson lesson = getLessonService().getLesson(lessonId);
+ LearningDesign learningDesign = lesson.getLearningDesign();
+ String contentFolderId = learningDesign.getContentFolderID();
+
+ Set activities = new HashSet<>();
+ // filter activities that are interesting for further processing
+ for (Activity activity : (Set) learningDesign.getActivities()) {
+ if (activity.isSequenceActivity()) {
+ // skip sequence activities as they are just for grouping
+ continue;
+ }
+ // get real activity object, not proxy
+ activities.add(getMonitoringService().getActivityById(activity.getActivityId()));
+ }
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ List contributeActivities = getContributeActivities(lessonId, true);
+ if (contributeActivities != null) {
+ responseJSON.set("contributeActivities", JsonUtil.readArray(contributeActivities));
+ }
+
+ // check if the searched learner has started the lesson
+ LearnerProgress searchedLearnerProgress = null;
+ if (searchedLearnerId != null) {
+ searchedLearnerProgress = getLessonService().getUserProgressForLesson(searchedLearnerId, lessonId);
+ responseJSON.put("searchedLearnerFound", searchedLearnerProgress != null);
+ }
+
+ // Fetch number of learners at each activity
+ ArrayList activityIds = new ArrayList<>();
+ Set leaders = new TreeSet<>();
+ for (Activity activity : activities) {
+ activityIds.add(activity.getActivityId());
+ // find leaders from Leader Selection Tool
+ if (activity.isToolActivity()) {
+ ToolActivity toolActivity = (ToolActivity) activity;
+ if (ILamsToolService.LEADER_SELECTION_TOOL_SIGNATURE
+ .equals(toolActivity.getTool().getToolSignature())) {
+ leaders.addAll(getToolService().getLeaderUserId(activity.getActivityId()));
+ }
+ }
+ }
+
+ Map learnerCounts = getMonitoringService()
+ .getCountLearnersCurrentActivities(activityIds.toArray(new Long[activityIds.size()]));
+
+ ArrayNode activitiesJSON = JsonNodeFactory.instance.arrayNode();
+ for (Activity activity : activities) {
+ Long activityId = activity.getActivityId();
+ ObjectNode activityJSON = JsonNodeFactory.instance.objectNode();
+ activityJSON.put("id", activityId);
+ activityJSON.put("uiid", activity.getActivityUIID());
+ activityJSON.put("title", activity.getTitle());
+ activityJSON.put("type", activity.getActivityTypeId());
+
+ Activity parentActivity = activity.getParentActivity();
+ if (activity.isBranchingActivity()) {
+ BranchingActivity ba = (BranchingActivity) monitoringService.getActivityById(activity.getActivityId());
+ activityJSON.put("x", MonitoringController.getActivityCoordinate(ba.getStartXcoord()));
+ activityJSON.put("y", MonitoringController.getActivityCoordinate(ba.getStartYcoord()));
+ } else if (activity.isOptionsWithSequencesActivity()) {
+ activityJSON.put("x", MonitoringController
+ .getActivityCoordinate(((OptionsWithSequencesActivity) activity).getStartXcoord()));
+ activityJSON.put("y", MonitoringController
+ .getActivityCoordinate(((OptionsWithSequencesActivity) activity).getStartYcoord()));
+ } else if ((parentActivity != null) && (parentActivity.isOptionsActivity()
+ || parentActivity.isParallelActivity() || parentActivity.isFloatingActivity())) {
+ // Optional Activity children had coordinates relative to parent
+ activityJSON.put("x", MonitoringController.getActivityCoordinate(parentActivity.getXcoord())
+ + MonitoringController.getActivityCoordinate(activity.getXcoord()));
+ activityJSON.put("y", MonitoringController.getActivityCoordinate(parentActivity.getYcoord())
+ + MonitoringController.getActivityCoordinate(activity.getYcoord()));
+ } else {
+ activityJSON.put("x", MonitoringController.getActivityCoordinate(activity.getXcoord()));
+ activityJSON.put("y", MonitoringController.getActivityCoordinate(activity.getYcoord()));
+ }
+
+ String monitorUrl = getMonitoringService().getActivityMonitorURL(lessonId, activityId, contentFolderId,
+ monitorUserId);
+ if (monitorUrl != null && !activity.isBranchingActivity()) {
+ // whole activity monitor URL
+ activityJSON.put("url", monitorUrl);
+ }
+
+ // find few latest users and count of all users for each activity
+ int learnerCount = learnerCounts.get(activityId);
+ if (!activity.isBranchingActivity() && !activity.isOptionsWithSequencesActivity()) {
+ List latestLearners = getMonitoringService().getLearnersLatestByActivity(activity.getActivityId(),
+ MonitoringController.LATEST_LEARNER_PROGRESS_ACTIVITY_DISPLAY_LIMIT, null);
+
+ // insert leaders as first of learners
+ for (Long leaderId : leaders) {
+ for (User learner : latestLearners) {
+ if (learner.getUserId().equals(leaderId.intValue())) {
+ latestLearners = MonitoringController.insertHighlightedLearner(learner, latestLearners,
+ MonitoringController.LATEST_LEARNER_PROGRESS_ACTIVITY_DISPLAY_LIMIT);
+ break;
+ }
+ }
+ }
+
+ // insert the searched learner as the first one
+ if ((searchedLearnerProgress != null) && (searchedLearnerProgress.getCurrentActivity() != null)
+ && activity.getActivityId()
+ .equals(searchedLearnerProgress.getCurrentActivity().getActivityId())) {
+ // put the searched learner in front
+ latestLearners = MonitoringController.insertHighlightedLearner(searchedLearnerProgress.getUser(),
+ latestLearners, MonitoringController.LATEST_LEARNER_PROGRESS_ACTIVITY_DISPLAY_LIMIT);
+ }
+
+ // parse learners into JSON format
+ if (!latestLearners.isEmpty()) {
+ ArrayNode learnersJSON = JsonNodeFactory.instance.arrayNode();
+ for (User learner : latestLearners) {
+ ObjectNode userJSON = WebUtil.userToJSON(learner);
+ if (leaders.contains(learner.getUserId().longValue())) {
+ userJSON.put("leader", true);
+ }
+ learnersJSON.add(userJSON);
+ }
+
+ activityJSON.set("learners", learnersJSON);
+ }
+ }
+ activityJSON.put("learnerCount", learnerCount);
+
+ activitiesJSON.add(activityJSON);
+ }
+ responseJSON.set("activities", activitiesJSON);
+
+ // find learners who completed the lesson
+ List completedLearners = getMonitoringService().getLearnersLatestCompleted(lessonId,
+ MonitoringController.LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT, null);
+ Integer completedLearnerCount = null;
+ if (completedLearners.size() < MonitoringController.LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT) {
+ completedLearnerCount = completedLearners.size();
+ } else {
+ completedLearnerCount = getMonitoringService().getCountLearnersCompletedLesson(lessonId);
+ }
+ responseJSON.put("completedLearnerCount", completedLearnerCount);
+
+ if ((searchedLearnerProgress != null) && searchedLearnerProgress.isComplete()) {
+ // put the searched learner in front
+ completedLearners = MonitoringController.insertHighlightedLearner(searchedLearnerProgress.getUser(),
+ completedLearners, MonitoringController.LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT);
+ }
+ for (User learner : completedLearners) {
+ ObjectNode learnerJSON = WebUtil.userToJSON(learner);
+ // no more details are needed for learners who completed the lesson
+ responseJSON.withArray("completedLearners").add(learnerJSON);
+ }
+
+ responseJSON.put("numberPossibleLearners", getLessonService().getCountLessonLearners(lessonId, null));
+
+ // on first fetch get transitions metadata so Monitoring can set their SVG elems IDs
+ if (WebUtil.readBooleanParam(request, "getTransitions", false)) {
+ ArrayNode transitions = JsonNodeFactory.instance.arrayNode();
+ for (Transition transition : (Set) learningDesign.getTransitions()) {
+ ObjectNode transitionJSON = JsonNodeFactory.instance.objectNode();
+ transitionJSON.put("uiid", transition.getTransitionUIID());
+ transitionJSON.put("fromID", transition.getFromActivity().getActivityId());
+ transitionJSON.put("toID", transition.getToActivity().getActivityId());
+
+ transitions.add(transitionJSON);
+ }
+ responseJSON.set("transitions", transitions);
+ }
+
+ response.setContentType("application/json;charset=utf-8");
+
+ return responseJSON.toString();
+ }
+
+ /**
+ * Gives suggestions when a Monitor searches for Learners and staff members.
+ */
+ @RequestMapping("/autocomplete")
+ @ResponseBody
+ public String autocomplete(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ if (!getSecurityService().isLessonMonitor(lessonId, getUserId(), "autocomplete in monitoring", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ String searchPhrase = request.getParameter("term");
+ boolean isOrganisationSearch = WebUtil.readStrParam(request, "scope").equalsIgnoreCase("organisation");
+
+ Collection users = null;
+ if (isOrganisationSearch) {
+ // search for Learners in the organisation
+ Map result = getLessonService().getUsersWithLessonParticipation(lessonId, Role.LEARNER,
+ searchPhrase, MonitoringController.USER_PAGE_SIZE, null, true);
+ users = result.keySet();
+ } else {
+ // search for Learners in the lesson
+ users = getLessonService().getLessonLearners(lessonId, searchPhrase, MonitoringController.USER_PAGE_SIZE,
+ null, true);
+ }
+
+ ArrayNode responseJSON = JsonNodeFactory.instance.arrayNode();
+ for (User user : users) {
+ ObjectNode userJSON = JsonNodeFactory.instance.objectNode();
+ userJSON.put("label", user.getFirstName() + " " + user.getLastName() + " " + user.getLogin());
+ userJSON.put("value", user.getUserId());
+
+ responseJSON.add(userJSON);
+ }
+
+ response.setContentType("application/json;charset=utf-8");
+ return responseJSON.toString();
+ }
+
+ /**
+ * Checks if activity A is before activity B in a sequence.
+ */
+ @RequestMapping("/isActivityPreceding")
+ @ResponseBody
+ public String isActivityPreceding(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ long activityAid = WebUtil.readLongParam(request, "activityA");
+ long activityBid = WebUtil.readLongParam(request, "activityB");
+ boolean result = false;
+
+ IMonitoringService monitoringService = MonitoringServiceProxy
+ .getMonitoringService(applicationContext.getServletContext());
+ Activity precedingActivity = monitoringService.getActivityById(activityBid);
+
+ // move back an look for activity A
+ while (!result && (precedingActivity != null)) {
+ if (precedingActivity.getTransitionTo() != null) {
+ precedingActivity = precedingActivity.getTransitionTo().getFromActivity();
+ } else if (precedingActivity.getParentActivity() != null) {
+ // this is a nested activity; move up
+ precedingActivity = precedingActivity.getParentActivity();
+ continue;
+ } else {
+ precedingActivity = null;
+ }
+
+ if ((precedingActivity != null) && !precedingActivity.isSequenceActivity()) {
+ if (precedingActivity.getActivityId().equals(activityAid)) {
+ // found it
+ result = true;
+ } else if (precedingActivity.isComplexActivity()) {
+ // check descendants of a complex activity
+ ComplexActivity complexActivity = (ComplexActivity) monitoringService
+ .getActivityById(precedingActivity.getActivityId());
+ if (containsActivity(complexActivity, activityAid, monitoringService)) {
+ result = true;
+ }
+ }
+ }
+ }
+
+ response.setContentType("text/plain;charset=utf-8");
+ response.getWriter().write(Boolean.toString(result));
+ return null;
+ }
+
+ /**
+ * Checks if a complex activity or its descendats contain an activity with the given ID.
+ */
+ @SuppressWarnings("unchecked")
+ private boolean containsActivity(ComplexActivity complexActivity, long targetActivityId,
+ IMonitoringService monitoringService) {
+ for (Activity childActivity : (Set) complexActivity.getActivities()) {
+ if (childActivity.getActivityId().equals(targetActivityId)) {
+ return true;
+ }
+ if (childActivity.isComplexActivity()) {
+ ComplexActivity childComplexActivity = (ComplexActivity) monitoringService
+ .getActivityById(childActivity.getActivityId());
+ if (containsActivity(childComplexActivity, targetActivityId, monitoringService)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @RequestMapping("/startPreviewLesson")
+ public String startPreviewLesson(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ Integer userID = getUserId();
+ long lessonID = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+
+ try {
+ getMonitoringService().createPreviewClassForLesson(userID, lessonID);
+ getMonitoringService().startLesson(lessonID, getUserId());
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "The user is not a monitor in the lesson");
+ }
+ return null;
+ }
+
+ @RequestMapping("/startLiveEdit")
+ @ResponseBody
+ public String startLiveEdit(HttpServletRequest request, HttpServletResponse response)
+ throws LearningDesignException, UserException, IOException {
+
+ long learningDesignId = WebUtil.readLongParam(request, CentralConstants.PARAM_LEARNING_DESIGN_ID);
+ LearningDesign learningDesign = (LearningDesign) getUserManagementService().findById(LearningDesign.class,
+ learningDesignId);
+ if (learningDesign.getLessons().isEmpty()) {
+ throw new InvalidParameterException(
+ "There are no lessons associated with learning design: " + learningDesignId);
+ }
+ Integer organisationID = ((Lesson) learningDesign.getLessons().iterator().next()).getOrganisation()
+ .getOrganisationId();
+ Integer userID = getUserId();
+ if (!getSecurityService().hasOrgRole(organisationID, userID, new String[] { Role.AUTHOR }, "start live edit",
+ false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not an author in the organisation");
+ return null;
+ }
+
+ IAuthoringService authoringService = MonitoringServiceProxy
+ .getAuthoringService(applicationContext.getServletContext());
+
+ if (authoringService.setupEditOnFlyLock(learningDesignId, userID)) {
+ authoringService.setupEditOnFlyGate(learningDesignId, userID);
+ } else {
+ response.getWriter().write("Someone else is editing the design at the moment.");
+ }
+
+ return null;
+ }
+
+ private ILogEventService getLogEventService() {
+ if (MonitoringController.logEventService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ MonitoringController.logEventService = (ILogEventService) ctx.getBean("logEventService");
+ }
+ return MonitoringController.logEventService;
+ }
+
+ private ILessonService getLessonService() {
+ if (MonitoringController.lessonService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ MonitoringController.lessonService = (ILessonService) ctx.getBean("lessonService");
+ }
+ return MonitoringController.lessonService;
+ }
+
+ private IMonitoringService getMonitoringService() {
+ if (MonitoringController.monitoringService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ MonitoringController.monitoringService = (IMonitoringService) ctx.getBean("monitoringService");
+ }
+ return MonitoringController.monitoringService;
+ }
+
+ private ISecurityService getSecurityService() {
+ if (MonitoringController.securityService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ MonitoringController.securityService = (ISecurityService) ctx.getBean("securityService");
+ }
+ return MonitoringController.securityService;
+ }
+
+ private IUserManagementService getUserManagementService() {
+ if (MonitoringController.userManagementService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ MonitoringController.userManagementService = (IUserManagementService) ctx.getBean("userManagementService");
+ }
+ return MonitoringController.userManagementService;
+ }
+
+ private ILearnerService getLearnerService() {
+ if (MonitoringController.learnerService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ MonitoringController.learnerService = (ILearnerService) ctx.getBean("learnerService");
+ }
+ return MonitoringController.learnerService;
+ }
+
+ private MessageService getMessageService() {
+ if (MonitoringController.messageService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ MonitoringController.messageService = (MessageService) ctx.getBean("monitoringMessageService");
+ }
+ return MonitoringController.messageService;
+ }
+
+ private ILamsToolService getToolService() {
+ if (MonitoringController.toolService == null) {
+ WebApplicationContext ctx = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(applicationContext.getServletContext());
+ MonitoringController.toolService = (ILamsToolService) ctx.getBean("lamsToolService");
+ }
+ return MonitoringController.toolService;
+ }
+
+ /**
+ * Set whether or not the presence available button is available in learner. Expects parameters lessonID and
+ * presenceAvailable.
+ */
+ @RequestMapping("/presenceAvailable")
+ public String presenceAvailable(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ Long lessonID = new Long(WebUtil.readLongParam(request, "lessonID"));
+ Integer userID = getUserId();
+ Boolean presenceAvailable = WebUtil.readBooleanParam(request, "presenceAvailable", false);
+
+ try {
+ getMonitoringService().togglePresenceAvailable(lessonID, userID, presenceAvailable);
+
+ if (!presenceAvailable) {
+ getMonitoringService().togglePresenceImAvailable(lessonID, userID, false);
+ }
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ }
+ return null;
+ }
+
+ /**
+ * Set whether or not the presence available button is available in learner. Expects parameters lessonID and
+ * presenceImAvailable.
+ */
+ @RequestMapping("/presenceImAvailable")
+ public String presenceImAvailable(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+ Long lessonID = new Long(WebUtil.readLongParam(request, "lessonID"));
+ Integer userID = getUserId();
+ Boolean presenceImAvailable = WebUtil.readBooleanParam(request, "presenceImAvailable", false);
+
+ try {
+ getMonitoringService().togglePresenceImAvailable(lessonID, userID, presenceImAvailable);
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ }
+ return null;
+ }
+
+ /**
+ * Set whether or not the activity scores / gradebook values are shown to the learner at the end of the lesson.
+ * Expects parameters lessonID and presenceAvailable.
+ */
+ @RequestMapping("/gradebookOnComplete")
+ public String gradebookOnComplete(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ Long lessonID = new Long(WebUtil.readLongParam(request, "lessonID"));
+ Integer userID = getUserId();
+ Boolean gradebookOnComplete = WebUtil.readBooleanParam(request, "gradebookOnComplete", false);
+
+ try {
+ getMonitoringService().toggleGradebookOnComplete(lessonID, userID, gradebookOnComplete);
+ } catch (SecurityException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ }
+ return null;
+ }
+
+ /** Open Time Chart display */
+ @RequestMapping("/viewTimeChart")
+ public String viewTimeChart(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+ try {
+
+ long lessonID = WebUtil.readLongParam(request, "lessonID");
+
+ // check monitor privledges
+ if (!getSecurityService().isLessonMonitor(lessonID, getUserId(), "open time chart", false)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User is not a monitor in the lesson");
+ return null;
+ }
+
+ request.setAttribute("lessonID", lessonID);
+ request.setAttribute("learnerID", WebUtil.readLongParam(request, "learnerID", true));
+
+ } catch (Exception e) {
+ request.setAttribute("errorName", "MonitoringAction");
+ request.setAttribute("errorMessage", e.getMessage());
+
+ return "systemErrorContent";
+ }
+
+ return "timeChart";
+ }
+
+ /**
+ * Creates a list of users out of string with comma-delimited user IDs.
+ */
+ private List parseUserList(HttpServletRequest request, String paramName, Collection users) {
+ String userIdList = request.getParameter(paramName);
+ String[] userIdArray = userIdList.split(",");
+ List result = new ArrayList<>(userIdArray.length);
+
+ for (User user : users) {
+ Integer userId = user.getUserId();
+ for (String includeId : userIdArray) {
+ if (userId.toString().equals(includeId)) {
+ result.add(user);
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ private List getContributeActivities(Long lessonId, boolean skipCompletedBranching) {
+ List contributeActivities = getMonitoringService().getAllContributeActivityDTO(lessonId);
+ Lesson lesson = getLessonService().getLesson(lessonId);
+
+ if (contributeActivities != null) {
+ List resultContributeActivities = new ArrayList<>();
+ for (ContributeActivityDTO contributeActivity : contributeActivities) {
+ if (contributeActivity.getContributeEntries() != null) {
+ Iterator entryIterator = contributeActivity
+ .getContributeEntries().iterator();
+ while (entryIterator.hasNext()) {
+ ContributeActivityDTO.ContributeEntry contributeEntry = entryIterator.next();
+
+ // extra filtering for chosen branching: do not show in Sequence tab if all users were assigned
+ if (skipCompletedBranching
+ && ContributionTypes.CHOSEN_BRANCHING.equals(contributeEntry.getContributionType())) {
+ Set learners = new HashSet<>(lesson.getLessonClass().getLearners());
+ ChosenBranchingActivity branching = (ChosenBranchingActivity) getMonitoringService()
+ .getActivityById(contributeActivity.getActivityID());
+ for (SequenceActivity branch : (Set) branching.getActivities()) {
+ Group group = branch.getSoleGroupForBranch();
+ if (group != null) {
+ learners.removeAll(group.getUsers());
+ }
+ }
+ contributeEntry.setIsComplete(learners.isEmpty());
+ }
+
+ if (!contributeEntry.getIsRequired() || contributeEntry.getIsComplete()) {
+ entryIterator.remove();
+ }
+ }
+
+ if (!contributeActivity.getContributeEntries().isEmpty()) {
+ resultContributeActivities.add(contributeActivity);
+ }
+ }
+ }
+ return resultContributeActivities;
+ }
+ return null;
+ }
+
+ private static int getActivityCoordinate(Integer coord) {
+ return (coord == null) || (coord < 0) ? ObjectExtractor.DEFAULT_COORD : coord;
+ }
+
+ /**
+ * Puts the searched learner in front of other learners in the list.
+ */
+ private static List insertHighlightedLearner(User searchedLearner, List latestLearners, int limit) {
+ latestLearners.remove(searchedLearner);
+ LinkedList updatedLatestLearners = new LinkedList<>(latestLearners);
+ updatedLatestLearners.addFirst(searchedLearner);
+ if (updatedLatestLearners.size() > limit) {
+ updatedLatestLearners.removeLast();
+ }
+ return updatedLatestLearners;
+ }
+}
\ No newline at end of file
Fisheye: Tag 6e7ab7890111cde84e6557c3507a1e9d3682e318 refers to a dead (removed) revision in file `lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/SequenceAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/SequenceController.java
===================================================================
diff -u
--- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/SequenceController.java (revision 0)
+++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/SequenceController.java (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -0,0 +1,94 @@
+/****************************************************************
+ * 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.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.lamsfoundation.lams.learningdesign.SequenceActivity;
+import org.lamsfoundation.lams.monitoring.service.IMonitoringService;
+import org.lamsfoundation.lams.usermanagement.User;
+import org.lamsfoundation.lams.util.WebUtil;
+import org.lamsfoundation.lams.web.util.AttributeNames;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * The action servlet that provides the support for the Sequence activities. At present, this is only a basic view
+ * screen that lists the user's in the sequence.
+ *
+ *
+ *
+ *
+ * @author Fiona Malikoff
+ */
+@Controller
+public class SequenceController {
+
+ @Autowired
+ @Qualifier("monitoringService")
+ private IMonitoringService monitoringService;
+
+ public static final String VIEW_SEQUENCE = "viewSequence";
+ public static final String PARAM_LEARNERS = "learners";
+ public static final String PARAM_LOCAL_FILES = "localFiles";
+
+ /**
+ * Display the view screen.
+ */
+ @RequestMapping("/sequence")
+ public String viewSequence(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ long activityId = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID);
+
+ SequenceActivity activity = (SequenceActivity) monitoringService.getActivityById(activityId,
+ SequenceActivity.class);
+ return viewSequence(activity, lessonId, false, request, monitoringService);
+ }
+
+ protected String viewSequence(SequenceActivity activity, Long lessonId, boolean useLocalFiles,
+ HttpServletRequest request, IMonitoringService monitoringService) throws IOException, ServletException {
+
+ // in general the progress engine expects the activity and lesson id to be in the request,
+ // so follow that standard.
+ request.setAttribute(AttributeNames.PARAM_ACTIVITY_ID, activity.getActivityId());
+ request.setAttribute(AttributeNames.PARAM_LESSON_ID, lessonId);
+ request.setAttribute(AttributeNames.PARAM_TITLE, activity.getTitle());
+ request.setAttribute(SequenceController.PARAM_LOCAL_FILES, useLocalFiles);
+
+ // only show the group names if this is a group based branching activity - the names
+ // are meaningless for chosen and tool based branching
+ List learners = monitoringService.getLearnersAttemptedOrCompletedActivity(activity);
+ request.setAttribute(SequenceController.PARAM_LEARNERS, learners);
+ return "viewSequence";
+ }
+
+}
Fisheye: Tag 6e7ab7890111cde84e6557c3507a1e9d3682e318 refers to a dead (removed) revision in file `lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/TblMonitoringAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/TblMonitoringController.java
===================================================================
diff -u
--- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/TblMonitoringController.java (revision 0)
+++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/TblMonitoringController.java (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -0,0 +1,470 @@
+package org.lamsfoundation.lams.monitoring.web;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.lamsfoundation.lams.gradebook.GradebookUserActivity;
+import org.lamsfoundation.lams.gradebook.service.IGradebookService;
+import org.lamsfoundation.lams.learningdesign.Activity;
+import org.lamsfoundation.lams.learningdesign.ActivityOrderComparator;
+import org.lamsfoundation.lams.learningdesign.BranchingActivity;
+import org.lamsfoundation.lams.learningdesign.ComplexActivity;
+import org.lamsfoundation.lams.learningdesign.ContributionTypes;
+import org.lamsfoundation.lams.learningdesign.GateActivity;
+import org.lamsfoundation.lams.learningdesign.Group;
+import org.lamsfoundation.lams.learningdesign.Grouping;
+import org.lamsfoundation.lams.learningdesign.GroupingActivity;
+import org.lamsfoundation.lams.learningdesign.PermissionGateActivity;
+import org.lamsfoundation.lams.learningdesign.SequenceActivity;
+import org.lamsfoundation.lams.learningdesign.ToolActivity;
+import org.lamsfoundation.lams.learningdesign.Transition;
+import org.lamsfoundation.lams.learningdesign.dao.IActivityDAO;
+import org.lamsfoundation.lams.lesson.Lesson;
+import org.lamsfoundation.lams.lesson.service.ILessonService;
+import org.lamsfoundation.lams.monitoring.dto.ContributeActivityDTO;
+import org.lamsfoundation.lams.monitoring.dto.PermissionGateDTO;
+import org.lamsfoundation.lams.monitoring.dto.TblGroupDTO;
+import org.lamsfoundation.lams.monitoring.dto.TblUserDTO;
+import org.lamsfoundation.lams.monitoring.service.IMonitoringService;
+import org.lamsfoundation.lams.tool.ToolSession;
+import org.lamsfoundation.lams.tool.service.ILamsCoreToolService;
+import org.lamsfoundation.lams.tool.service.ILamsToolService;
+import org.lamsfoundation.lams.usermanagement.User;
+import org.lamsfoundation.lams.util.CentralConstants;
+import org.lamsfoundation.lams.util.WebUtil;
+import org.lamsfoundation.lams.web.action.LamsDispatchAction;
+import org.lamsfoundation.lams.web.util.AttributeNames;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Displays TBL monitor.
+ *
+ * @author Andrey Balan
+ */
+public class TblMonitoringAction extends LamsDispatchAction {
+
+ private static Logger log = Logger.getLogger(TblMonitoringAction.class);
+
+ private static ILessonService lessonService;
+ private static IMonitoringService monitoringService;
+ private static ILamsCoreToolService coreToolService;
+ private static ILamsToolService toolService;
+ private static IActivityDAO activityDAO;
+ private static IGradebookService gradebookService;
+
+ /**
+ * Displays addStudent page.
+ */
+ @Override
+ public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ initServices();
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ Lesson lesson = lessonService.getLesson(lessonId);
+ request.setAttribute("lesson", lesson);
+ request.setAttribute("totalLearnersNumber", lesson.getAllLearners().size());
+
+ List lessonActivities = getLessonActivities(lesson);
+ setupAvailableActivityTypes(request, lessonActivities);
+ return mapping.findForward("tblmonitor");
+ }
+
+ /**
+ * Shows Teams page
+ */
+ public ActionForward teams(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ Lesson lesson = lessonService.getLesson(lessonId);
+
+ List lessonActivities = getLessonActivities(lesson);
+ setupAvailableActivityTypes(request, lessonActivities);
+ boolean isScratchieAvailable = (request.getAttribute("isScratchieAvailable") != null)
+ && ((Boolean) request.getAttribute("isScratchieAvailable"));
+ boolean isIraMcqAvailable = (request.getAttribute("isIraMcqAvailable") != null)
+ && ((Boolean) request.getAttribute("isIraMcqAvailable"));
+ boolean isIraAssessmentAvailable = (request.getAttribute("isIraAssessmentAvailable") != null)
+ && ((Boolean) request.getAttribute("isIraAssessmentAvailable"));
+ Long iraToolActivityId = request.getAttribute("iraToolActivityId") == null ? null
+ : (Long) request.getAttribute("iraToolActivityId");
+ Long traToolActivityId = request.getAttribute("traToolActivityId") == null ? null
+ : (Long) request.getAttribute("traToolActivityId");
+ Long leaderselectionToolActivityId = request.getAttribute("leaderselectionToolActivityId") == null ? null
+ : (Long) request.getAttribute("leaderselectionToolActivityId");
+
+ //get all mcq and assessment scores
+ List iraGradebookUserActivities = new LinkedList<>();
+ List traGradebookUserActivities = new LinkedList<>();
+ if (isIraMcqAvailable || isIraAssessmentAvailable) {
+ iraGradebookUserActivities = gradebookService.getGradebookUserActivities(iraToolActivityId);
+ }
+ if (isScratchieAvailable) {
+ traGradebookUserActivities = gradebookService.getGradebookUserActivities(traToolActivityId);
+ }
+
+ Set leaderUserIds = leaderselectionToolActivityId == null ? new HashSet()
+ : toolService.getLeaderUserId(leaderselectionToolActivityId);
+
+ GroupingActivity groupingActivity = getGroupingActivity(lesson);
+ Grouping grouping = groupingActivity == null ? null : groupingActivity.getCreateGrouping();
+ Set groups = grouping == null ? null : grouping.getGroups();
+
+ Set groupDtos = new TreeSet();
+ if (groups != null) {
+ for (Group group : groups) {
+ TblGroupDTO groupDto = new TblGroupDTO(group);
+ groupDtos.add(groupDto);
+
+ if (group.getUsers() != null) {
+ for (User user : group.getUsers()) {
+ TblUserDTO userDto = new TblUserDTO(user.getUserDTO());
+ groupDto.getUserList().add(userDto);
+
+ //set up all user leaders
+ if (leaderUserIds.contains(new Long(user.getUserId()))) {
+ userDto.setGroupLeader(true);
+ groupDto.setGroupLeader(userDto);
+ }
+
+ if (isIraMcqAvailable || isIraAssessmentAvailable) {
+ //find according iraGradebookUserActivity
+ for (GradebookUserActivity iraGradebookUserActivity : iraGradebookUserActivities) {
+ if (iraGradebookUserActivity.getLearner().getUserId().equals(user.getUserId())) {
+ userDto.setIraScore(iraGradebookUserActivity.getMark());
+ break;
+ }
+ }
+ }
+
+ if (isScratchieAvailable) {
+ //find according traGradebookUserActivity
+ for (GradebookUserActivity traGradebookUserActivity : traGradebookUserActivities) {
+ if (traGradebookUserActivity.getLearner().getUserId().equals(user.getUserId())) {
+ //we set traScore multiple times, but it's doesn't matter
+ groupDto.setTraScore(traGradebookUserActivity.getMark());
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ request.setAttribute("groupDtos", groupDtos);
+
+ return mapping.findForward("teams");
+ }
+
+ /**
+ * Shows Gates page
+ */
+ public ActionForward gates(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+
+ List permissionGates = new ArrayList();
+
+ List contributeActivities = monitoringService.getAllContributeActivityDTO(lessonId);
+ if (contributeActivities != null) {
+ for (ContributeActivityDTO contributeActivity : contributeActivities) {
+
+ if (contributeActivity.getContributeEntries() != null) {
+
+ //check if there is any persmission gates entries
+ for (ContributeActivityDTO.ContributeEntry contributeEntry : contributeActivity
+ .getContributeEntries()) {
+ if (ContributionTypes.PERMISSION_GATE.equals(contributeEntry.getContributionType())) {
+
+ Long activityId = contributeActivity.getActivityID();
+ Activity activity = monitoringService.getActivityById(activityId);
+ PermissionGateDTO gateDto = new PermissionGateDTO((PermissionGateActivity) activity);
+
+ gateDto.setUrl(contributeEntry.getURL());
+ gateDto.setComplete(contributeEntry.getIsComplete());
+
+ int waitingLearnersCount = lessonService.getCountLearnersHaveAttemptedActivity(activity);
+ gateDto.setWaitingLearnersCount(waitingLearnersCount);
+
+ permissionGates.add(gateDto);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ request.setAttribute("permissionGates", permissionGates);
+ return mapping.findForward("gates");
+ }
+
+ /**
+ * Shows forum page
+ */
+ public ActionForward forum(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ long forumActivityId = WebUtil.readLongParam(request, "activityId");
+ ToolActivity forumActivity = (ToolActivity) monitoringService.getActivityById(forumActivityId);
+
+ int attemptedLearnersNumber = lessonService.getCountLearnersHaveAttemptedActivity(forumActivity);
+ request.setAttribute("attemptedLearnersNumber", attemptedLearnersNumber);
+
+ Set toolSessions = forumActivity.getToolSessions();
+ request.setAttribute("toolSessions", toolSessions);
+
+ return mapping.findForward("forum");
+ }
+
+ /**
+ * Shows peerreview page
+ */
+ public ActionForward peerreview(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ long peerreviewActivityId = WebUtil.readLongParam(request, "activityId");
+ ToolActivity peerreviewActivity = (ToolActivity) monitoringService.getActivityById(peerreviewActivityId);
+
+ int attemptedLearnersNumber = lessonService.getCountLearnersHaveAttemptedActivity(peerreviewActivity);
+ request.setAttribute("attemptedLearnersNumber", attemptedLearnersNumber);
+
+ Set toolSessions = peerreviewActivity.getToolSessions();
+ request.setAttribute("toolSessions", toolSessions);
+
+ return mapping.findForward("peerreview");
+ }
+
+ /**
+ * Shows sequence diagram page
+ */
+ public ActionForward sequence(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID);
+ Lesson lesson = lessonService.getLesson(lessonId);
+ request.setAttribute("lesson", lesson);
+ return mapping.findForward("sequence");
+ }
+
+ /**
+ * Returns lesson activities sorted by the learning design order.
+ */
+ @SuppressWarnings("unchecked")
+ private List getLessonActivities(Lesson lesson) {
+
+ /*
+ * Hibernate CGLIB is failing to load the first activity in the sequence as a ToolActivity for some mysterious
+ * reason Causes a ClassCastException when you try to cast it, even if it is a ToolActivity.
+ *
+ * THIS IS A HACK to retrieve the first tool activity manually so it can be cast as a ToolActivity - if it is
+ * one
+ */
+ Activity firstActivity = activityDAO
+ .getActivityByActivityId(lesson.getLearningDesign().getFirstActivity().getActivityId());
+ List activities = new ArrayList();
+ sortActivitiesByLearningDesignOrder(firstActivity, activities);
+
+ return activities;
+ }
+
+ /**
+ * Sort all activities by the learning design order.
+ *
+ * @param activity
+ * @param sortedActivities
+ */
+ private void sortActivitiesByLearningDesignOrder(Activity activity, List sortedActivities) {
+ sortedActivities.add(activity);
+
+ //in case of branching activity - add all activities based on their orderId
+ if (activity.isBranchingActivity()) {
+ BranchingActivity branchingActivity = (BranchingActivity) activity;
+ Set sequenceActivities = new TreeSet(new ActivityOrderComparator());
+ sequenceActivities.addAll(branchingActivity.getActivities());
+ for (Activity sequenceActivityNotInitialized : sequenceActivities) {
+ SequenceActivity sequenceActivity = (SequenceActivity) monitoringService
+ .getActivityById(sequenceActivityNotInitialized.getActivityId());
+ Set childActivities = new TreeSet(new ActivityOrderComparator());
+ childActivities.addAll(sequenceActivity.getActivities());
+
+ //add one by one in order to initialize all activities
+ for (Activity childActivity : childActivities) {
+ Activity activityInit = monitoringService.getActivityById(childActivity.getActivityId());
+ sortedActivities.add(activityInit);
+ }
+ }
+
+ // In case of complex activity (parallel, help or optional activity) add all its children activities.
+ // They will be sorted by orderId
+ } else if (activity.isComplexActivity()) {
+ ComplexActivity complexActivity = (ComplexActivity) activity;
+ Set childActivities = new TreeSet(new ActivityOrderComparator());
+ childActivities.addAll(complexActivity.getActivities());
+
+ // add one by one in order to initialize all activities
+ for (Activity childActivity : childActivities) {
+ Activity activityInit = monitoringService.getActivityById(childActivity.getActivityId());
+ sortedActivities.add(activityInit);
+ }
+ }
+
+ Transition transitionFrom = activity.getTransitionFrom();
+ if (transitionFrom != null) {
+ // query activity from DB as transition holds only proxied activity object
+ Long nextActivityId = transitionFrom.getToActivity().getActivityId();
+ Activity nextActivity = monitoringService.getActivityById(nextActivityId);
+
+ sortActivitiesByLearningDesignOrder(nextActivity, sortedActivities);
+ }
+ }
+
+ private GroupingActivity getGroupingActivity(Lesson lesson) {
+ Set activities = new TreeSet();
+
+ /*
+ * Hibernate CGLIB is failing to load the first activity in the sequence as a ToolActivity for some mysterious
+ * reason Causes a ClassCastException when you try to cast it, even if it is a ToolActivity.
+ *
+ * THIS IS A HACK to retrieve the first tool activity manually so it can be cast as a ToolActivity - if it is
+ * one
+ */
+ Activity firstActivity = monitoringService
+ .getActivityById(lesson.getLearningDesign().getFirstActivity().getActivityId());
+ activities.add(firstActivity);
+ activities.addAll(lesson.getLearningDesign().getActivities());
+
+ for (Activity activity : activities) {
+ if (activity instanceof GroupingActivity) {
+ return (GroupingActivity) activity;
+ }
+ }
+
+ return null;
+ }
+
+ private void setupAvailableActivityTypes(HttpServletRequest request, List activities) {
+
+ //check if there is Scratchie activity. It's used only in case of LKC TBL monitoring, when all assessment are treated as AEs
+ boolean isScratchieAvailable = false;
+ for (Activity activity : activities) {
+ if (activity instanceof ToolActivity) {
+ ToolActivity toolActivity = (ToolActivity) activity;
+ String toolSignature = toolActivity.getTool().getToolSignature();
+ if (CentralConstants.TOOL_SIGNATURE_SCRATCHIE.equals(toolSignature)) {
+ isScratchieAvailable = true;
+ break;
+ }
+ }
+ }
+
+ boolean scratchiePassed = false;
+ boolean iraPassed = false;
+ String assessmentToolContentIds = "";
+ String assessmentActivityTitles = "";
+ for (Activity activity : activities) {
+ if (activity instanceof ToolActivity) {
+ ToolActivity toolActivity = (ToolActivity) activity;
+ String toolSignature = toolActivity.getTool().getToolSignature();
+ Long toolContentId = toolActivity.getToolContentId();
+ Long toolActivityId = toolActivity.getActivityId();
+ String toolTitle = toolActivity.getTitle();
+
+ //count only the first MCQ or Assessmnet as iRA
+ if (!iraPassed && (CentralConstants.TOOL_SIGNATURE_MCQ.equals(toolSignature)
+ || isScratchieAvailable && CentralConstants.TOOL_SIGNATURE_ASSESSMENT.equals(toolSignature))) {
+ iraPassed = true;
+ if (CentralConstants.TOOL_SIGNATURE_MCQ.equals(toolSignature)) {
+ request.setAttribute("isIraMcqAvailable", true);
+
+ } else {
+ request.setAttribute("isIraAssessmentAvailable", true);
+ }
+ request.setAttribute("iraToolContentId", toolContentId);
+ request.setAttribute("iraToolActivityId", toolActivityId);
+
+ continue;
+ }
+
+ //aes are counted only after Scratchie activity, or for LKC TBL monitoring
+ if ((scratchiePassed || !isScratchieAvailable) && CentralConstants.TOOL_SIGNATURE_ASSESSMENT.equals(toolSignature)) {
+ request.setAttribute("isAeAvailable", true);
+ //prepare assessment details to be passed to Assessment tool
+ assessmentToolContentIds += toolContentId + ",";
+ assessmentActivityTitles += toolTitle + "\\,";
+
+ } else if (CentralConstants.TOOL_SIGNATURE_FORUM.equals(toolSignature)) {
+ request.setAttribute("isForumAvailable", true);
+ request.setAttribute("forumActivityId", toolActivityId);
+
+ } else if (CentralConstants.TOOL_SIGNATURE_PEER_REVIEW.equals(toolSignature)) {
+ request.setAttribute("isPeerreviewAvailable", true);
+ request.setAttribute("peerreviewToolContentId", toolContentId);
+
+ //tRA is the first scratchie activity
+ } else if (!scratchiePassed && CentralConstants.TOOL_SIGNATURE_SCRATCHIE.equals(toolSignature)) {
+ scratchiePassed = true;
+
+ request.setAttribute("isScratchieAvailable", true);
+ request.setAttribute("traToolContentId", toolContentId);
+ request.setAttribute("traToolActivityId", toolActivityId);
+ }
+
+ if (CentralConstants.TOOL_SIGNATURE_LEADERSELECTION.equals(toolSignature)) {
+ request.setAttribute("leaderselectionToolActivityId", toolActivityId);
+ request.setAttribute("leaderselectionToolContentId", toolContentId);
+ }
+
+ } else if (activity instanceof GateActivity) {
+ request.setAttribute("isGatesAvailable", true);
+ }
+ }
+
+ request.setAttribute("assessmentToolContentIds", assessmentToolContentIds);
+ request.setAttribute("assessmentActivityTitles", assessmentActivityTitles);
+ }
+
+ private void initServices() {
+ ServletContext servletContext = this.getServlet().getServletContext();
+ WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
+ if (lessonService == null) {
+ lessonService = (ILessonService) ctx.getBean("lessonService");
+ }
+
+ if (monitoringService == null) {
+ monitoringService = (IMonitoringService) ctx.getBean("monitoringService");
+ }
+
+ if (coreToolService == null) {
+ coreToolService = (ILamsCoreToolService) ctx.getBean("lamsCoreToolService");
+ }
+
+ if (toolService == null) {
+ toolService = (ILamsToolService) ctx.getBean("lamsToolService");
+ }
+
+ if (activityDAO == null) {
+ activityDAO = (IActivityDAO) ctx.getBean("activityDAO");
+ }
+
+ if (gradebookService == null) {
+ gradebookService = (IGradebookService) ctx.getBean("gradebookService");
+ }
+
+ }
+
+}
Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/ToolOutputBranchingAction.java
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r6e7ab7890111cde84e6557c3507a1e9d3682e318
--- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/ToolOutputBranchingAction.java (.../ToolOutputBranchingAction.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/ToolOutputBranchingAction.java (.../ToolOutputBranchingAction.java) (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -36,6 +36,6 @@
*
*
*/
-public class ToolOutputBranchingAction extends BranchingAction {
+public class ToolOutputBranchingAction extends BranchingController {
}
Index: lams_monitoring/web/WEB-INF/spring-servlet.xml
===================================================================
diff -u
--- lams_monitoring/web/WEB-INF/spring-servlet.xml (revision 0)
+++ lams_monitoring/web/WEB-INF/spring-servlet.xml (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: lams_monitoring/web/WEB-INF/web.xml
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r6e7ab7890111cde84e6557c3507a1e9d3682e318
--- lams_monitoring/web/WEB-INF/web.xml (.../web.xml) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_monitoring/web/WEB-INF/web.xml (.../web.xml) (revision 6e7ab7890111cde84e6557c3507a1e9d3682e318)
@@ -73,6 +73,14 @@
org.springframework.web.context.ContextLoaderListener
+
+
+ spring
+
+ org.springframework.web.servlet.DispatcherServlet
+
+ 1
+
action
@@ -97,7 +105,7 @@
- action
+ spring
*.do