Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/learningdesign/Activity.hbm.xml =================================================================== diff -u -rac99dd8a79daaa42b1e6cdbe9b1a5fd197107b83 -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/learningdesign/Activity.hbm.xml (.../Activity.hbm.xml) (revision ac99dd8a79daaa42b1e6cdbe9b1a5fd197107b83) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/learningdesign/Activity.hbm.xml (.../Activity.hbm.xml) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -186,10 +186,21 @@ - + + + + + + + + + + + + Index: lams_common/db/model/lams_11.clay =================================================================== diff -u -r6e4ed3724bd76354c2ee43c88979385c4a162a0e -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_common/db/model/lams_11.clay (.../lams_11.clay) (revision 6e4ed3724bd76354c2ee43c88979385c4a162a0e) +++ lams_common/db/model/lams_11.clay (.../lams_11.clay) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -3771,6 +3771,19 @@ + + + + + + + + + + + + + Index: lams_common/db/sql/create_lams_11_tables.sql =================================================================== diff -u -rc209be8131f22f6fe37bd8d6c14b56425a78b766 -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_common/db/sql/create_lams_11_tables.sql (.../create_lams_11_tables.sql) (revision c209be8131f22f6fe37bd8d6c14b56425a78b766) +++ lams_common/db/sql/create_lams_11_tables.sql (.../create_lams_11_tables.sql) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -823,6 +823,7 @@ CREATE TABLE lams_activity_learners ( user_id BIGINT(20) NOT NULL DEFAULT 0 , activity_id BIGINT(20) NOT NULL DEFAULT 0 + , allowed_to_pass TINYINT NOT NULL DEFAULT 0 , INDEX (user_id) , CONSTRAINT FK_TABLE_32_1 FOREIGN KEY (user_id) REFERENCES lams_user (user_id) Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/GateActivity.java =================================================================== diff -u -r309a597eada52a4079f2985e0d97beedf9adda42 -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/GateActivity.java (.../GateActivity.java) (revision 309a597eada52a4079f2985e0d97beedf9adda42) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/GateActivity.java (.../GateActivity.java) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -33,214 +33,182 @@ import org.lamsfoundation.lams.tool.SystemTool; import org.lamsfoundation.lams.usermanagement.User; - /** * @hibernate.class */ -public abstract class GateActivity extends SimpleActivity implements Serializable, ISystemToolActivity -{ - - public static final int LEARNER_GATE_LEVEL = 1; - - public static final int GROUP_GATE_LEVEL = 2; - - public static final int CLASS_GATE_LEVEL = 3; - - /** persistent field */ - private SystemTool systemTool; +public abstract class GateActivity extends SimpleActivity implements Serializable, ISystemToolActivity { - /** persistent field */ - private Integer gateActivityLevelId; - - /** persistent field */ - private Boolean gateOpen; - - /** - * The learners who are waiting at the gate. - */ - private Set waitingLearners; - - /** full constructor */ - public GateActivity(Long activityId, - Integer id, - String description, - String title, - Integer xcoord, - Integer ycoord, - Integer orderId, - Boolean defineLater, - java.util.Date createDateTime, - LearningLibrary learningLibrary, - Activity parentActivity, - Activity libraryActivity, - Integer parentUIID, - LearningDesign learningDesign, - Grouping grouping, - Integer activityTypeId, - Transition transitionTo, - Transition transitionFrom, - String languageFile, - Boolean stopAfterActivity, - Set inputActivities, - Integer gateActivityLevelId, - Set waitingLearners, - SystemTool sysTool) - { - super(activityId, - id, - description, - title, - xcoord, - ycoord, - orderId, - defineLater, - createDateTime, - learningLibrary, - parentActivity, - libraryActivity, - parentUIID, - learningDesign, - grouping, - activityTypeId, - transitionTo, - transitionFrom, - languageFile, - stopAfterActivity, - inputActivities); - this.gateActivityLevelId = gateActivityLevelId; - this.waitingLearners = waitingLearners; - this.systemTool = sysTool; - } - - /** default constructor */ - public GateActivity() - { - } - - /** minimal constructor */ - public GateActivity(Long activityId, - Boolean defineLater, - java.util.Date createDateTime, - org.lamsfoundation.lams.learningdesign.LearningLibrary learningLibrary, - org.lamsfoundation.lams.learningdesign.Activity parentActivity, - org.lamsfoundation.lams.learningdesign.LearningDesign learningDesign, - org.lamsfoundation.lams.learningdesign.Grouping grouping, - Integer activityTypeId, - Transition transitionTo, - Transition transitionFrom, - Integer gateActivityLevelId, - Set waitingLearners) - { - super(activityId, - defineLater, - createDateTime, - learningLibrary, - parentActivity, - learningDesign, - grouping, - activityTypeId, - transitionTo, - transitionFrom); - this.gateActivityLevelId = gateActivityLevelId; - this.waitingLearners = waitingLearners; - } - - /** - * @hibernate.many-to-one not-null="true" - * @hibernate.column name="gate_activity_level_id" - * - */ - public Integer getGateActivityLevelId() - { - return this.gateActivityLevelId; - } - - public void setGateActivityLevelId(Integer gateActivityLevelId) - { - this.gateActivityLevelId = gateActivityLevelId; - } - - /** - * @return Returns the waitingLearners. - */ - public Set getWaitingLearners() - { - if(this.waitingLearners == null) - this.setWaitingLearners(new HashSet()); - return waitingLearners; - } - /** - * @param waitingLearners The waitingLearners to set. - */ - public void setWaitingLearners(Set waitingLearners) - { - this.waitingLearners = waitingLearners; - } - - public Boolean getGateOpen() - { + public static final int LEARNER_GATE_LEVEL = 1; + + public static final int GROUP_GATE_LEVEL = 2; + + public static final int CLASS_GATE_LEVEL = 3; + + /** persistent field */ + private SystemTool systemTool; + + /** persistent field */ + private Integer gateActivityLevelId; + + /** persistent field */ + private Boolean gateOpen; + + /** + * The learners who are waiting at the gate. + */ + private Set waitingLearners; + /** + * The learners who passed the gate. + */ + private Set allowedToPassLearners; + /** + * The learners who reached the gate. + */ + private Set allGateUsers; + + /** full constructor */ + public GateActivity(Long activityId, Integer id, String description, String title, Integer xcoord, Integer ycoord, + Integer orderId, Boolean defineLater, java.util.Date createDateTime, LearningLibrary learningLibrary, + Activity parentActivity, Activity libraryActivity, Integer parentUIID, LearningDesign learningDesign, + Grouping grouping, Integer activityTypeId, Transition transitionTo, Transition transitionFrom, String languageFile, + Boolean stopAfterActivity, Set inputActivities, Integer gateActivityLevelId, Set waitingLearners, SystemTool sysTool) { + super(activityId, id, description, title, xcoord, ycoord, orderId, defineLater, createDateTime, learningLibrary, + parentActivity, libraryActivity, parentUIID, learningDesign, grouping, activityTypeId, transitionTo, + transitionFrom, languageFile, stopAfterActivity, inputActivities); + this.gateActivityLevelId = gateActivityLevelId; + this.waitingLearners = waitingLearners; + systemTool = sysTool; + } + + /** default constructor */ + public GateActivity() { + } + + /** minimal constructor */ + public GateActivity(Long activityId, Boolean defineLater, java.util.Date createDateTime, + org.lamsfoundation.lams.learningdesign.LearningLibrary learningLibrary, + org.lamsfoundation.lams.learningdesign.Activity parentActivity, + org.lamsfoundation.lams.learningdesign.LearningDesign learningDesign, + org.lamsfoundation.lams.learningdesign.Grouping grouping, Integer activityTypeId, Transition transitionTo, + Transition transitionFrom, Integer gateActivityLevelId, Set waitingLearners) { + super(activityId, defineLater, createDateTime, learningLibrary, parentActivity, learningDesign, grouping, activityTypeId, + transitionTo, transitionFrom); + this.gateActivityLevelId = gateActivityLevelId; + this.waitingLearners = waitingLearners; + } + + /** + * @hibernate.many-to-one not-null="true" + * @hibernate.column name="gate_activity_level_id" + * + */ + public Integer getGateActivityLevelId() { + return gateActivityLevelId; + } + + public void setGateActivityLevelId(Integer gateActivityLevelId) { + this.gateActivityLevelId = gateActivityLevelId; + } + + /** + * @return Returns the waitingLearners. + */ + public Set getWaitingLearners() { + if (waitingLearners == null) { + this.setWaitingLearners(new HashSet()); + } + return waitingLearners; + } + + /** + * @param waitingLearners The waitingLearners to set. + */ + public void setWaitingLearners(Set waitingLearners) { + this.waitingLearners = waitingLearners; + } + + public Boolean getGateOpen() { return gateOpen; } - - public void setGateOpen(Boolean gateOpen) - { + + public void setGateOpen(Boolean gateOpen) { this.gateOpen = gateOpen; } - - //--------------------------------------------------------------------- - // Domain service methods - //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + // Domain service methods + //--------------------------------------------------------------------- + /** - * Add a learner into the waiting list. - * @param learner the new waiting learner. + * Add a learner into the waiting or allowed to pass list. + * @param learner the new learner. + * @param isAllowedToPass is the learner allowed to pass the gate */ - public void addWaitingLeaner(User learner) - { - if(this.waitingLearners == null) - this.setWaitingLearners(new HashSet()); - - //only add the learner if we have never added him before. - if(!this.waitingLearners.contains(learner)) - this.waitingLearners.add(learner); + public void addLeaner(User learner, boolean isAllowedToPass) { + boolean add = true; + GateUser gateUser = new GateUser(); + gateUser.setUser(learner); + + if (getWaitingLearners().contains(learner)) { + if (isAllowedToPass) { + gateUser.setAllowedToPass(false); + getAllGateUsers().remove(gateUser); + getWaitingLearners().remove(learner); + } + else { + add = false; + } + } + else if (getAllowedToPassLearners().contains(learner)) { + // Once a learner was allowed to pass, he/she may never be forbidden back. + add = false; + } + + if (add) { + gateUser.setAllowedToPass(isAllowedToPass); + getAllGateUsers().add(gateUser); + if (isAllowedToPass) { + getAllowedToPassLearners().add(learner); + } + else { + getWaitingLearners().add(learner); + } + } } - + /** * Delegate to strategy class to calculate whether we should open the * gate for this learner. * @param learner the learner who wants to go through the gate. * @return the gate is open or closed. */ - public boolean shouldOpenGateFor(User learner, List lessonLearners) - { - //by default, we close the gate - if(getGateOpen()==null) - this.setGateOpen(Boolean.FALSE); - - return ((GateActivityStrategy)simpleActivityStrategy).shouldOpenGateFor(learner,lessonLearners); + public boolean shouldOpenGateFor(User learner, List lessonLearners) { + //by default, we close the gate + if (getGateOpen() == null) { + this.setGateOpen(Boolean.FALSE); + } + + return ((GateActivityStrategy) simpleActivityStrategy).shouldOpenGateFor(learner, lessonLearners); } /** * Force the gate to open, irrespective of the strategy. Used for preview. * Opens the gate and clears any "waiting" list. * @return the gate is open or closed. */ - public boolean forceGateOpen() - { + public boolean forceGateOpen() { setGateOpen(Boolean.TRUE); - getWaitingLearners().clear(); - return true; + getAllGateUsers().clear(); + return true; } //--------------------------------------------------------------------- - // Helper methods - //--------------------------------------------------------------------- - public String toString() - { - return new ToStringBuilder(this) - .append("activityId", getActivityId()) - .toString(); - } + // Helper methods + //--------------------------------------------------------------------- + @Override + public String toString() { + return new ToStringBuilder(this).append("activityId", getActivityId()).toString(); + } public SystemTool getSystemTool() { return systemTool; @@ -249,11 +217,33 @@ public void setSystemTool(SystemTool systemTool) { this.systemTool = systemTool; } - - protected void copyToNewActivity(GateActivity newActivity, int uiidOffset) { - super.copyToNewActivity(newActivity, uiidOffset); - newActivity.setSystemTool(this.getSystemTool()); - } + protected void copyToNewActivity(GateActivity newActivity, int uiidOffset) { + super.copyToNewActivity(newActivity, uiidOffset); + newActivity.setSystemTool(this.getSystemTool()); + } + + public Set getAllowedToPassLearners() { + if (allowedToPassLearners == null) { + this.setAllowedToPassLearners(new HashSet()); + } + return allowedToPassLearners; + } + + public void setAllowedToPassLearners(Set allowedToPassLearners) { + this.allowedToPassLearners = allowedToPassLearners; + } + + public Set getAllGateUsers() { + if (allGateUsers == null) { + this.setAllGateUsers(new HashSet()); + } + return allGateUsers; + } + + public void setAllGateUsers(Set allLearners) { + allGateUsers = allLearners; + } + } Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/GateUser.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/GateUser.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/GateUser.java (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -0,0 +1,43 @@ +package org.lamsfoundation.lams.learningdesign; + +import java.io.Serializable; + +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.lamsfoundation.lams.usermanagement.User; + +/** + * Learner that uses a gate. + * It contains information of the user and the status can he/she pass the gate. + * @author Marcin Cieslak + * + */ +public class GateUser implements Serializable { + private User user; + private Boolean allowedToPass; + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Boolean getAllowedToPass() { + return allowedToPass; + } + + public void setAllowedToPass(Boolean passed) { + allowedToPass = passed; + } + + @Override + public boolean equals(Object o) { + return o instanceof GateUser && ((GateUser) o).user.equals(user) && ((GateUser) o).allowedToPass.equals(allowedToPass); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(user).append(allowedToPass).toHashCode(); + } +} Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/strategy/GateActivityStrategy.java =================================================================== diff -u -r08950e1090443c3423a3d1c587416a2fccd8bbdf -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/strategy/GateActivityStrategy.java (.../GateActivityStrategy.java) (revision 08950e1090443c3423a3d1c587416a2fccd8bbdf) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/strategy/GateActivityStrategy.java (.../GateActivityStrategy.java) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -29,7 +29,6 @@ import org.lamsfoundation.lams.learningdesign.GateActivity; import org.lamsfoundation.lams.usermanagement.User; - /** * Activity strategy that deal with the calculation of all sub gate activities. * It is abstract calculation layer for all concrete gate activities. @@ -39,74 +38,77 @@ * @version 1.1 * */ -public abstract class GateActivityStrategy extends SimpleActivityStrategy -{ +public abstract class GateActivityStrategy extends SimpleActivityStrategy { protected GateActivity gateActivity = null; - + public GateActivityStrategy(GateActivity gateActivity) { this.gateActivity = gateActivity; } + //--------------------------------------------------------------------- + // Template methods + //--------------------------------------------------------------------- + /** + * Returns wether we should open the gate or close the gate. It's + * implementation depends on the type of the gate we are dealing with. + * Generally, it needs the check up the current gate status. If the gate + * is already opened, we will keep it open for current learner. Otherwise, + * we need to validate the open condition for this learner. + * + * @param learner the learner who just arrived at the gate. + * @param activity the gate activity. + * @param lessonLearners all learners who have started the lesson + * @return whether we should open it or not. + */ + public boolean shouldOpenGateFor(User learner, List lessonLearners) { + gateActivity.addLeaner(learner, false); - //--------------------------------------------------------------------- - // Template methods - //--------------------------------------------------------------------- - /** - * Returns wether we should open the gate or close the gate. It's - * implementation depends on the type of the gate we are dealing with. - * Generally, it needs the check up the current gate status. If the gate - * is already opened, we will keep it open for current learner. Otherwise, - * we need to validate the open condition for this learner. - * - * @param learner the learner who just arrived at the gate. - * @param activity the gate activity. - * @param lessonLearners all learners who have started the lesson - * @return whether we should open it or not. - */ - public boolean shouldOpenGateFor(User learner,List lessonLearners) - { - gateActivity.addWaitingLeaner(learner); - - if(!gateActivity.getGateOpen().booleanValue()) - { - if( isOpenConditionMet(lessonLearners)) - { - gateActivity.setGateOpen(new Boolean(true)); - gateActivity.getWaitingLearners().clear(); - } + if (!gateActivity.getGateOpen().booleanValue()) { + if (isOpenConditionMet(lessonLearners)) { + gateActivity.setGateOpen(new Boolean(true)); + //always clear all lists if the gate is opened + gateActivity.getAllGateUsers().clear(); + gateActivity.getWaitingLearners().clear(); + gateActivity.getAllowedToPassLearners().clear(); + } + else if (gateActivity.getAllowedToPassLearners().contains(learner)) { + return true; + } + } + //always clear all lists if the gate is already opened. + else { + gateActivity.getAllGateUsers().clear(); + gateActivity.getWaitingLearners().clear(); + gateActivity.getAllowedToPassLearners().clear(); + } + return gateActivity.getGateOpen().booleanValue(); + } - } - //always clear waiting list if the gate is already opened. - else { - gateActivity.getWaitingLearners().clear(); - } - return gateActivity.getGateOpen().booleanValue(); - } - - //--------------------------------------------------------------------- - // Abstract methods - //--------------------------------------------------------------------- - /** - * Check up the open condition according the gate type. - * @return return true if the condition is met. - */ - protected abstract boolean isOpenConditionMet(List lessonLearners); - //--------------------------------------------------------------------- - // Overidden methods - //--------------------------------------------------------------------- - /** - * @see org.lamsfoundation.lams.learningdesign.strategy.SimpleActivityStrategy#setUpContributionType(org.lamsfoundation.lams.learningdesign.Activity, java.util.ArrayList) - */ - protected abstract void setUpContributionType(ArrayList contributionTypes); + //--------------------------------------------------------------------- + // Abstract methods + //--------------------------------------------------------------------- + /** + * Check up the open condition according the gate type. + * @return return true if the condition is met. + */ + protected abstract boolean isOpenConditionMet(List lessonLearners); - /** - * Get the activity for this strategy. The activity should be set - * when the strategy is created. - */ - protected Activity getActivity() { - return gateActivity; - } + //--------------------------------------------------------------------- + // Overidden methods + //--------------------------------------------------------------------- + /** + * @see org.lamsfoundation.lams.learningdesign.strategy.SimpleActivityStrategy#setUpContributionType(org.lamsfoundation.lams.learningdesign.Activity, java.util.ArrayList) + */ + @Override + protected abstract void setUpContributionType(ArrayList contributionTypes); + /** + * Get the activity for this strategy. The activity should be set + * when the strategy is created. + */ + @Override + protected Activity getActivity() { + return gateActivity; + } - } Index: lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java =================================================================== diff -u -rc209be8131f22f6fe37bd8d6c14b56425a78b766 -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java (.../LearnerService.java) (revision c209be8131f22f6fe37bd8d6c14b56425a78b766) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java (.../LearnerService.java) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -709,7 +709,7 @@ //update gate including updating the waiting list and gate status in //the database. activityDAO.update(gate); - return new GateActivityDTO(gate, lessonLearners); + return new GateActivityDTO(gate, lessonLearners, gateOpen); } Index: lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/GateAction.java =================================================================== diff -u -rfdca3605f0b782b19e214abbe94df6f4a457b88e -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/GateAction.java (.../GateAction.java) (revision fdca3605f0b782b19e214abbe94df6f4a457b88e) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/GateAction.java (.../GateAction.java) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -21,7 +21,7 @@ * **************************************************************** */ -/* $$Id$$ */ +/* $$Id$$ */ package org.lamsfoundation.lams.learning.web.action; import java.io.IOException; @@ -41,17 +41,14 @@ import org.lamsfoundation.lams.learning.web.util.ActivityMapping; import org.lamsfoundation.lams.learning.web.util.LearningWebUtil; import org.lamsfoundation.lams.learningdesign.Activity; -import org.lamsfoundation.lams.learningdesign.PermissionGateActivity; import org.lamsfoundation.lams.learningdesign.ScheduleGateActivity; -import org.lamsfoundation.lams.learningdesign.SynchGateActivity; import org.lamsfoundation.lams.lesson.LearnerProgress; import org.lamsfoundation.lams.lesson.Lesson; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.util.WebUtil; import org.lamsfoundation.lams.web.action.LamsDispatchAction; import org.lamsfoundation.lams.web.util.AttributeNames; - /** *

The action servlet that deals with gate activity. This class allows the * learner to knock gate when they reach the gate. The knocking process will @@ -83,27 +80,26 @@ * @struts:action-forward name="synchGate" path=".synchGate" * ----------------XDoclet Tags-------------------- */ -public class GateAction extends LamsDispatchAction -{ +public class GateAction extends LamsDispatchAction { - //--------------------------------------------------------------------- - // Instance variables - //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + // Instance variables + //--------------------------------------------------------------------- // private static Logger log = Logger.getLogger(GateAction.class); - //--------------------------------------------------------------------- - // Class level constants - Struts forward - //--------------------------------------------------------------------- - private static final String VIEW_PERMISSION_GATE = "permissionGate"; - private static final String VIEW_SCHEDULE_GATE = "scheduleGate"; - private static final String VIEW_SYNCH_GATE = "synchGate"; - - /** Input parameter. Boolean value */ - public static final String PARAM_FORCE_GATE_OPEN = "force"; + //--------------------------------------------------------------------- + // Class level constants - Struts forward + //--------------------------------------------------------------------- + private static final String VIEW_PERMISSION_GATE = "permissionGate"; + private static final String VIEW_SCHEDULE_GATE = "scheduleGate"; + private static final String VIEW_SYNCH_GATE = "synchGate"; + /** Input parameter. Boolean value */ + public static final String PARAM_FORCE_GATE_OPEN = "force"; + //--------------------------------------------------------------------- - // Struts Dispatch Method - //--------------------------------------------------------------------- + // Struts Dispatch Method + //--------------------------------------------------------------------- /** * * @param mapping @@ -114,94 +110,88 @@ * @throws IOException * @throws ServletException */ - public ActionForward knockGate(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) throws IOException, - ServletException - { - boolean forceGate = WebUtil.readBooleanParam(request,PARAM_FORCE_GATE_OPEN,false); - Long activityId = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); - - //initialize service object - ICoreLearnerService learnerService = LearnerServiceProxy.getLearnerService(getServlet().getServletContext()); - Activity activity = learnerService.getActivity(activityId); + public ActionForward knockGate(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + boolean forceGate = WebUtil.readBooleanParam(request, GateAction.PARAM_FORCE_GATE_OPEN, false); + Long activityId = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + + //initialize service object + ICoreLearnerService learnerService = LearnerServiceProxy.getLearnerService(getServlet().getServletContext()); + Activity activity = learnerService.getActivity(activityId); ActivityMapping actionMappings = LearningWebUtil.getActivityMapping(this.getServlet().getServletContext()); User learner = LearningWebUtil.getUser(learnerService); - Lesson lesson = learnerService.getLesson(lessonId); + Lesson lesson = learnerService.getLesson(lessonId); - // don't use LearningWebUtil.getLearnerProgress(request, learnerService) as it may try to get the lesson - // from the activity and the activity may be null (if this was a system stop gate). - LearnerProgress learnerProgress = (LearnerProgress)request.getAttribute(ActivityAction.LEARNER_PROGRESS_REQUEST_ATTRIBUTE); - if ( learnerProgress == null ) { - learnerProgress = learnerService.getProgress(learner.getUserId(), lessonId); - } + // don't use LearningWebUtil.getLearnerProgress(request, learnerService) as it may try to get the lesson + // from the activity and the activity may be null (if this was a system stop gate). + LearnerProgress learnerProgress = (LearnerProgress) request + .getAttribute(ActivityAction.LEARNER_PROGRESS_REQUEST_ATTRIBUTE); + if (learnerProgress == null) { + learnerProgress = learnerService.getProgress(learner.getUserId(), lessonId); + } - if ( activity != null ) { - //knock the gate - GateActivityDTO gate = learnerService.knockGate(activityId,learner,forceGate); + if (activity != null) { + //knock the gate + GateActivityDTO gate = learnerService.knockGate(activityId, learner, forceGate); - if ( gate == null ) { - throw new LearnerServiceException("Gate missing. gate id ["+activityId+"]"); - } + if (gate == null) { + throw new LearnerServiceException("Gate missing. gate id [" + activityId + "]"); + } - //if the gate is closed, ask the learner to wait ( updating the cached learner progress on the way ) - if ( ! gate.getGateOpen() ) { - ActionForward forward = findViewByGateType(mapping, (DynaActionForm)form, gate, lesson); - LearningWebUtil.setupProgressInRequest((DynaActionForm)form, request, learnerProgress); - return forward; - } - } - - // gate is open, so let the learner go to the next activity ( updating the cached learner progress on the way ) - return LearningWebUtil.completeActivity(request, response, - actionMappings, learnerProgress, activity, - learner.getUserId(), learnerService, true); + //if the gate is closed, ask the learner to wait ( updating the cached learner progress on the way ) + if (!gate.getAllowToPass()) { + ActionForward forward = findViewByGateType(mapping, (DynaActionForm) form, gate, lesson); + LearningWebUtil.setupProgressInRequest((DynaActionForm) form, request, learnerProgress); + return forward; + } + } - } - - //--------------------------------------------------------------------- - // Helper methods - //--------------------------------------------------------------------- - /** - * Dispatch view the according to the gate type. - * - * @param mapping An ActionMapping class that will be used by the Action - * class to tell the ActionServlet where to send the end-user. - * @param gateForm The ActionForm class that will contain any data submitted - * by the end-user via a form. - * @param permissionGate the gate activity object - * @param totalNumActiveLearners total number of active learners in the lesson (may not all be logged in) - * @return An ActionForward class that will be returned to the ActionServlet - * indicating where the user is to go next. - */ - private ActionForward findViewByGateType(ActionMapping mapping, - DynaActionForm gateForm, - GateActivityDTO gate, - Lesson lesson) - { - gateForm.set("totalLearners",new Integer(gate.getExpectedLearners().size())); - gateForm.set("waitingLearners",new Integer(gate.getWaitingLearners().size())); - gateForm.set("previewLesson",lesson.isPreviewLesson()); - gateForm.set(AttributeNames.PARAM_ACTIVITY_ID,gate.getActivityId()); - gateForm.set(AttributeNames.PARAM_LESSON_ID, lesson.getLessonId()); - gateForm.set("gate",gate); - if(gate.isSynchGate()) { - return mapping.findForward(VIEW_SYNCH_GATE); - } else if(gate.isScheduleGate()) { - ScheduleGateActivity scheduleGate = (ScheduleGateActivity)gate.getGateActivity(); - gateForm.set("startingTime",scheduleGate.getGateStartDateTime()); - gateForm.set("endingTime",scheduleGate.getGateEndDateTime()); - return mapping.findForward(VIEW_SCHEDULE_GATE); - } else if(gate.isPermissionGate() || gate.isSystemGate()) { - return mapping.findForward(VIEW_PERMISSION_GATE); - } else { - throw new LearnerServiceException("Invalid gate activity. " + - "gate id ["+gate.getActivityId()+"] - the type ["+ - gate.getActivityTypeId()+"] is not a gate type"); - } - } + // gate is open, so let the learner go to the next activity ( updating the cached learner progress on the way ) + return LearningWebUtil.completeActivity(request, response, actionMappings, learnerProgress, activity, + learner.getUserId(), learnerService, true); + } + + //--------------------------------------------------------------------- + // Helper methods + //--------------------------------------------------------------------- + /** + * Dispatch view the according to the gate type. + * + * @param mapping An ActionMapping class that will be used by the Action + * class to tell the ActionServlet where to send the end-user. + * @param gateForm The ActionForm class that will contain any data submitted + * by the end-user via a form. + * @param permissionGate the gate activity object + * @param totalNumActiveLearners total number of active learners in the lesson (may not all be logged in) + * @return An ActionForward class that will be returned to the ActionServlet + * indicating where the user is to go next. + */ + private ActionForward findViewByGateType(ActionMapping mapping, DynaActionForm gateForm, GateActivityDTO gate, Lesson lesson) { + gateForm.set("totalLearners", new Integer(gate.getExpectedLearners().size())); + gateForm.set("waitingLearners", new Integer(gate.getWaitingLearners().size())); + gateForm.set("previewLesson", lesson.isPreviewLesson()); + gateForm.set(AttributeNames.PARAM_ACTIVITY_ID, gate.getActivityId()); + gateForm.set(AttributeNames.PARAM_LESSON_ID, lesson.getLessonId()); + gateForm.set("gate", gate); + if (gate.isSynchGate()) { + return mapping.findForward(GateAction.VIEW_SYNCH_GATE); + } + else if (gate.isScheduleGate()) { + ScheduleGateActivity scheduleGate = (ScheduleGateActivity) gate.getGateActivity(); + gateForm.set("startingTime", scheduleGate.getGateStartDateTime()); + gateForm.set("endingTime", scheduleGate.getGateEndDateTime()); + return mapping.findForward(GateAction.VIEW_SCHEDULE_GATE); + } + else if (gate.isPermissionGate() || gate.isSystemGate()) { + return mapping.findForward(GateAction.VIEW_PERMISSION_GATE); + } + else { + throw new LearnerServiceException("Invalid gate activity. " + "gate id [" + gate.getActivityId() + "] - the type [" + + gate.getActivityTypeId() + "] is not a gate type"); + } + } + } Index: lams_learning/src/java/org/lamsfoundation/lams/learning/web/bean/GateActivityDTO.java =================================================================== diff -u -rfdca3605f0b782b19e214abbe94df6f4a457b88e -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_learning/src/java/org/lamsfoundation/lams/learning/web/bean/GateActivityDTO.java (.../GateActivityDTO.java) (revision fdca3605f0b782b19e214abbe94df6f4a457b88e) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/web/bean/GateActivityDTO.java (.../GateActivityDTO.java) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -47,28 +47,32 @@ * DTO wrapping a normal Gate Activity class, with an extra "calculated" field added for the learning module's gate screen. * */ -public class GateActivityDTO { +public class GateActivityDTO { private List expectedLearners; private GateActivity gateActivity; - - public GateActivityDTO(GateActivity gateActivity, List lessonLearners) { + private boolean allowToPass; + + public GateActivityDTO(GateActivity gateActivity, List lessonLearners, boolean allowToPass) { this.gateActivity = gateActivity; - this.expectedLearners = lessonLearners; + expectedLearners = lessonLearners; + this.allowToPass = allowToPass; } - /** - * Temporary value of the expected number of learners. This may change every time - * the gate is knocked, and is NOT persisted to the database. It is calculated when this - * DTO is created by the knockGate() method in LearnerService. - */ + + /** + * Temporary value of the expected number of learners. This may change every time + * the gate is knocked, and is NOT persisted to the database. It is calculated when this + * DTO is created by the knockGate() method in LearnerService. + */ public List getExpectedLearners() { return expectedLearners; } public void setExpectedLearners(List tempExpectedLearnerCount) { - this.expectedLearners = tempExpectedLearnerCount; + expectedLearners = tempExpectedLearnerCount; } + @Override public boolean equals(Object other) { return gateActivity.equals(other); } @@ -97,11 +101,9 @@ return gateActivity.getApplyGrouping(); } - public Set getAuthoringActivityDTOSet( - ArrayList branchMappings, + public Set getAuthoringActivityDTOSet(ArrayList branchMappings, String languageCode) { - return gateActivity.getAuthoringActivityDTOSet(branchMappings, - languageCode); + return gateActivity.getAuthoringActivityDTOSet(branchMappings, languageCode); } public Date getCreateDateTime() { @@ -240,6 +242,7 @@ return gateActivity.getYcoord(); } + @Override public int hashCode() { return gateActivity.hashCode(); } @@ -256,6 +259,7 @@ return gateActivity.isSystemToolActivity(); } + @Override public String toString() { return gateActivity.toString(); } @@ -270,18 +274,28 @@ public void setGateActivity(GateActivity gateActivity) { this.gateActivity = gateActivity; } + public boolean isPermissionGate() { return gateActivity.isPermissionGate(); } + public boolean isScheduleGate() { return gateActivity.isScheduleGate(); } + public boolean isSynchGate() { return gateActivity.isSynchGate(); } + public boolean isSystemGate() { return gateActivity.isSystemGate(); } + public boolean getAllowToPass() { + return getGateOpen() || allowToPass; + } -} + public void setAllowToPass(boolean allowToPass) { + this.allowToPass = allowToPass; + } +} \ No newline at end of file Index: lams_monitoring/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r4817e3da1eb7dc61568fc813da2eb0cf383aec46 -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_monitoring/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 4817e3da1eb7dc61568fc813da2eb0cf383aec46) +++ lams_monitoring/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -17,11 +17,16 @@ label.schedule.gate.title =Schedule Gate label.schedule.gate.open.message =Schedule Gate will be opened at: label.schedule.gate.close.message =Schedule Gate will be closed at: -label.gate.you.open.message =Class cannot continue until the gate is opened by you +label.gate.you.open.message =You may open the gate for the whole class label.gate.gate.open =Gate has been opened label.gate.open =Open label.gate.closed =Closed +label.gate.allow=Allow to pass label.gate.waiting.learners ={0} out of {1} are waiting in front of the gate. +label.gate.open.single.learner =You may allow a single learner to pass the gate +label.gate.list.all.learners=Forbidden to pass +label.gate.list.waiting.learners=Waiting in front of the gate +label.gate.list.allowed.learners=Allowed to pass label.grouping.max.num.in.group.heading =Maximum number of groups: label.grouping.general.instructions.line1 =Place the lesson participants in their groups. Initially you can add and remove users, but once the grouping is used (that is, a participant starts an activity that uses the grouping) you will not be able to remove users from groups. If you try to remove someone from a group and they will not remove check their progress - if they start using the group while you are on this screen you will not get any errors but you will not be able to remove them from their group. You will still be able to add users to groups. label.grouping.group.heading =Groups Index: lams_monitoring/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -r4817e3da1eb7dc61568fc813da2eb0cf383aec46 -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_monitoring/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 4817e3da1eb7dc61568fc813da2eb0cf383aec46) +++ lams_monitoring/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -17,11 +17,16 @@ label.schedule.gate.title =Schedule Gate label.schedule.gate.open.message =Schedule Gate will be opened at: label.schedule.gate.close.message =Schedule Gate will be closed at: -label.gate.you.open.message =Class cannot continue until the gate is opened by you +label.gate.you.open.message =You may open the gate for the whole class label.gate.gate.open =Gate has been opened label.gate.open =Open label.gate.closed =Closed +label.gate.allow=Allow to pass label.gate.waiting.learners ={0} out of {1} are waiting in front of the gate. +label.gate.open.single.learner =You may allow a single learner to pass the gate +label.gate.list.all.learners=Forbidden to pass +label.gate.list.waiting.learners=Waiting in front of the gate +label.gate.list.allowed.learners=Allowed to pass label.grouping.max.num.in.group.heading =Maximum number of groups: label.grouping.general.instructions.line1 =Place the lesson participants in their groups. Initially you can add and remove users, but once the grouping is used (that is, a participant starts an activity that uses the grouping) you will not be able to remove users from groups. If you try to remove someone from a group and they will not remove check their progress - if they start using the group while you are on this screen you will not get any errors but you will not be able to remove them from their group. You will still be able to add users to groups. label.grouping.group.heading =Groups Index: lams_monitoring/conf/xdoclet/struts-forms.xml =================================================================== diff -u -r843a5e9055e55f5069e0a26eb76ae76c1665814f -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_monitoring/conf/xdoclet/struts-forms.xml (.../struts-forms.xml) (revision 843a5e9055e55f5069e0a26eb76ae76c1665814f) +++ lams_monitoring/conf/xdoclet/struts-forms.xml (.../struts-forms.xml) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -7,5 +7,9 @@ + + + + Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java =================================================================== diff -u -r6f7eb4826ae72ada881fdba6af1cac7288d03804 -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision 6f7eb4826ae72ada881fdba6af1cac7288d03804) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -44,457 +44,471 @@ import org.lamsfoundation.lams.usermanagement.exception.UserAccessDeniedException; import org.lamsfoundation.lams.util.MessageService; - /** * Interface defines all monitoring services needed by presentation tier. * @author Jacky Fang 2/02/2005 * @author Manpreet Minhas */ -public interface IMonitoringService -{ +public interface IMonitoringService { /** Get the message service, which gives access to the I18N text */ public MessageService getMessageService(); - /** - * Initialize a new lesson so as to start the learning process. It needs to - * notify lams which learning design it belongs to. The initialize process - * doesn't involve the setup of lesson class and organization. - * - * @param lessonName the name of the lesson - * @param lessonDescription the description of the lesson. - * @param learnerExportAvailable should the export portfolio option be made available to the learner? - * @param learningDesignId the selected learning design - * @param organisationId the copied sequence will be put in the default runtime sequence folder for this org, if such a folder exists. - * @param userId the user who want to create this lesson. - * @param customCSV the custom comma separated values to be used by toolAdapters - * @return the lesson initialized. - */ - public Lesson initializeLesson(String lessonName, String lessonDescription,Boolean learnerExportAvailable,long learningDesignId,Integer organisationId,Integer userID, String customCSV); - - /** - * Initialize a new lesson so as to start the learning process for a normal or preview learning session. - * It needs to notify lams which learning design it belongs to. The initialize process - * doesn't involve the setup of lesson class and organization. - * - * @param creatorUserId the user who want to create this lesson. - * @param lessonPacket The WDDX packet containing the required initialisation paramaters - * @return WDDX message packet containing the Lesson ID - * @throws Exception - */ - public String initializeLesson(Integer creatorUserId, String lessonPacket) throws Exception; - - /** - * Create new lesson according to the learning design specified by the - * user, but for a preview session rather than a normal learning session. - * The design is not assigned to any workspace folder. - */ - public Lesson initializeLessonForPreview(String lessonName,String lessonDescription,long learningDesignId,Integer userID, String customCSV); - - /** - * Create a lession according to the input lession WDDX package. The sample package is following: - * - *

-     * 
+ /** + * Initialize a new lesson so as to start the learning process. It needs to + * notify lams which learning design it belongs to. The initialize process + * doesn't involve the setup of lesson class and organization. + * + * @param lessonName the name of the lesson + * @param lessonDescription the description of the lesson. + * @param learnerExportAvailable should the export portfolio option be made available to the learner? + * @param learningDesignId the selected learning design + * @param organisationId the copied sequence will be put in the default runtime sequence folder for this org, if such a folder exists. + * @param userId the user who want to create this lesson. + * @param customCSV the custom comma separated values to be used by toolAdapters + * @return the lesson initialized. + */ + public Lesson initializeLesson(String lessonName, String lessonDescription, Boolean learnerExportAvailable, + long learningDesignId, Integer organisationId, Integer userID, String customCSV); + + /** + * Initialize a new lesson so as to start the learning process for a normal or preview learning session. + * It needs to notify lams which learning design it belongs to. The initialize process + * doesn't involve the setup of lesson class and organization. + * + * @param creatorUserId the user who want to create this lesson. + * @param lessonPacket The WDDX packet containing the required initialisation paramaters + * @return WDDX message packet containing the Lesson ID + * @throws Exception + */ + public String initializeLesson(Integer creatorUserId, String lessonPacket) throws Exception; + + /** + * Create new lesson according to the learning design specified by the + * user, but for a preview session rather than a normal learning session. + * The design is not assigned to any workspace folder. + */ + public Lesson initializeLessonForPreview(String lessonName, String lessonDescription, long learningDesignId, Integer userID, + String customCSV); + + /** + * Create a lession according to the input lession WDDX package. The sample package is following: + * + *
+	 * 
* 135 * 10 - * 78 + * 78 * 12 * 3456 * - *
- *
- *

The lesson will includes creator who create this lesson as staff and learner. - * - * @param creatorUserId The lesson creator, who will be add to lesson as staff and learner. - * @param lessionPackage - * @return - */ - public String createLessonClassForLessonWDDX(Integer creatorUserId,String lessionPackage) throws UserAccessDeniedException; - - /** - * Setup the lesson class and organization for a lesson according to the - * input from monitoring GUI interface. - * - * @param lessonId the lesson without lesson class and organization - * @param organisation the organization this lesson belongs to. - * @param name of learner group - * @param organizationUsers a list of learner will be in this new lessons. - * @param name of staff group - * @param staffs a list of staffs who will be in charge of this lesson. - * @return the lesson with lesson class and organization - */ - public Lesson createLessonClassForLesson(long lessonId, Organisation organisation,String leanerGroupName, List organizationUsers,String staffGroupName, List staffs, Integer userID) throws UserAccessDeniedException; - - /** - * Start the specified the lesson. It must be created before calling this - * service. - * @param lessonId the specified the lesson id. - * @param userId checks that the user is a staff member for this lesson - * @throws LamsToolServiceException the exception occurred during the - * lams and tool interaction to start a - * lesson. - */ - public void startLesson(long lessonId, Integer userId) throws UserAccessDeniedException; - - - /** Do any normal initialisation needed for gates and branching. Done both when a lesson is started, or for new activities - * added during a Live Edit. Returns a new MaxID for the design if needed. If MaxID is returned, update the design with this - * new value and save the whole design (as initialiseSystemActivities has changed the design). - */ - public Integer startSystemActivity( Activity activity, Integer currentMaxId, Date lessonStartTime, String lessonName ); - - - /** - *

Runs the system scheduler to start the scheduling for opening gate and - * closing gate. It invlovs a couple of steps to start the scheduler:

- *
  • 1. Initialize the resource needed by scheduling job by setting - * them into the job data map. - *
  • - *
  • 2. Create customized triggers for the scheduling.
  • - *
  • 3. start the scheduling job
  • - * - * @param scheduleGate the gate that needs to be scheduled. - * @param schedulingStartTime the time on which the gate open should be based if an offset is used. For starting - * a lesson, this is the lessonStartTime. For live edit, it is now. - * @param lessonName the name lesson incorporating this gate - used for the description of the Quartz job. Optional. - * @returns An updated gate, that should be saved by the calling code. - */ - public ScheduleGateActivity runGateScheduler(ScheduleGateActivity scheduleGate, - Date schedulingStartTime, String lessonName); + *
    + * + *

    The lesson will includes creator who create this lesson as staff and learner. + * + * @param creatorUserId The lesson creator, who will be add to lesson as staff and learner. + * @param lessionPackage + * @return + */ + public String createLessonClassForLessonWDDX(Integer creatorUserId, String lessionPackage) throws UserAccessDeniedException; - /** - * Start a lesson on schedule datetime. - * @param lessonId - * @param startDate the lesson start date and time. - * @param userId checks that the user is a staff member for this lesson - * @see org.lamsfoundation.lams.monitoring.service#startLesson(long) - */ - public void startLessonOnSchedule(long lessonId, Date startDate, Integer userId) throws UserAccessDeniedException; - /** - * Finish a lesson on schedule datetime. - * @param lessonId - * @param endDate the lesson end date and time. - * @param userId checks that the user is a staff member for this lesson - */ - public void finishLessonOnSchedule(long lessonId, Date endDate, Integer userId) throws UserAccessDeniedException; - /** - * Finish a lesson.A Finished lesson can be viewed on the monitoring interface. - * It should be an "inactive" lesson. A Finished lesson is listed on the learner - * interface but all the learner can do is view the progress bar and do an - * export portfolio - they cannot access any of the tool screens. - * - * @param lessonId - * @param userId checks that the user is a staff member for this lesson - * @param endDate teh lesson end date and time. - */ - public void finishLesson(long lessonId, Integer userId) throws UserAccessDeniedException; - - /** - * Set whether or not the export portfolio button is available in learner. Sets it to FALSE if - * learnerExportAvailable is null. Checks that the user is a staff member of this lesson before - * updating. - * - * @param lessonId - * @param userId - * @param learnerExportAvailable - * @return new value for learnerExportAvailable. Normally will be same as input parameter, will only - * be different if the value cannot be updated for some reason. - */ - public Boolean setLearnerPortfolioAvailable(long lessonId, Integer userId, Boolean learnerExportAvailable); - - /** - * Force Complete works on an individual user. The teacher may complete it up to a particular activity, - * or till the end of the sequence which activity id is null indicating complete to end. Note, the give - * activity will be complete as well. - * - * @param learnerId - * @param requesterId the user id of the person requesting the force complete. For security check - * @param lessonId - * @param activityId force complete to this activity(this activity will be force complete as well). - * If null value, force will complete all activities in this lesson. - * @return success message. - */ - public String forceCompleteLessonByUser(Integer learnerId, Integer requesterId, long lessonId,Long activityId); - - /** - * Archive the specified lesson. When archived, the data is retained - * but the learners cannot access the details. - * @param lessonId the specified the lesson id. - * @param userId checks that the user is a staff member for this lesson - */ - public void archiveLesson(long lessonId, Integer userId) throws UserAccessDeniedException; - - /** - * Unarchive the specified the lesson. Reverts back to its previous state. - * @param lessonId the specified the lesson id. - */ - public void unarchiveLesson(long lessonId, Integer userId); - - /** - * A lesson can only be suspended if it is started. The purpose of suspending is - * to hide the lesson from learners temporarily. - * @param lessonId the lesson ID which will be suspended. - * @param userId checks that the user is a staff member for this lesson - */ - public void suspendLesson(long lessonId, Integer userId) throws UserAccessDeniedException; - /** - * Unsuspend a lesson, which state must be Lesson.SUSPEND_STATE. Returns the lesson - * back to its previous state. Otherwise an exception will be thrown. - * @param lessonId - * @param userId checks that the user is a staff member for this lesson - */ - public void unsuspendLesson(long lessonId, Integer userId) throws UserAccessDeniedException; + /** + * Setup the lesson class and organization for a lesson according to the + * input from monitoring GUI interface. + * + * @param lessonId the lesson without lesson class and organization + * @param organisation the organization this lesson belongs to. + * @param name of learner group + * @param organizationUsers a list of learner will be in this new lessons. + * @param name of staff group + * @param staffs a list of staffs who will be in charge of this lesson. + * @return the lesson with lesson class and organization + */ + public Lesson createLessonClassForLesson(long lessonId, Organisation organisation, String leanerGroupName, + List organizationUsers, String staffGroupName, List staffs, Integer userID) + throws UserAccessDeniedException; - /** - *

    - * Teachers sometimes find that there are just too many "old" designs and - * wish to remove them and never access them again. This function disables - * the lesson - it does not remove the contents from the database - *

    - * @param lessonId the specified the lesson id. - * @param userId checks that the user is a staff member for this lesson - */ + /** + * Start the specified the lesson. It must be created before calling this + * service. + * @param lessonId the specified the lesson id. + * @param userId checks that the user is a staff member for this lesson + * @throws LamsToolServiceException the exception occurred during the + * lams and tool interaction to start a + * lesson. + */ + public void startLesson(long lessonId, Integer userId) throws UserAccessDeniedException; + + /** Do any normal initialisation needed for gates and branching. Done both when a lesson is started, or for new activities + * added during a Live Edit. Returns a new MaxID for the design if needed. If MaxID is returned, update the design with this + * new value and save the whole design (as initialiseSystemActivities has changed the design). + */ + public Integer startSystemActivity(Activity activity, Integer currentMaxId, Date lessonStartTime, String lessonName); + + /** + *

    Runs the system scheduler to start the scheduling for opening gate and + * closing gate. It invlovs a couple of steps to start the scheduler:

    + *
  • 1. Initialize the resource needed by scheduling job by setting + * them into the job data map. + *
  • + *
  • 2. Create customized triggers for the scheduling.
  • + *
  • 3. start the scheduling job
  • + * + * @param scheduleGate the gate that needs to be scheduled. + * @param schedulingStartTime the time on which the gate open should be based if an offset is used. For starting + * a lesson, this is the lessonStartTime. For live edit, it is now. + * @param lessonName the name lesson incorporating this gate - used for the description of the Quartz job. Optional. + * @returns An updated gate, that should be saved by the calling code. + */ + public ScheduleGateActivity runGateScheduler(ScheduleGateActivity scheduleGate, Date schedulingStartTime, String lessonName); + + /** + * Start a lesson on schedule datetime. + * @param lessonId + * @param startDate the lesson start date and time. + * @param userId checks that the user is a staff member for this lesson + * @see org.lamsfoundation.lams.monitoring.service#startLesson(long) + */ + public void startLessonOnSchedule(long lessonId, Date startDate, Integer userId) throws UserAccessDeniedException; + + /** + * Finish a lesson on schedule datetime. + * @param lessonId + * @param endDate the lesson end date and time. + * @param userId checks that the user is a staff member for this lesson + */ + public void finishLessonOnSchedule(long lessonId, Date endDate, Integer userId) throws UserAccessDeniedException; + + /** + * Finish a lesson.A Finished lesson can be viewed on the monitoring interface. + * It should be an "inactive" lesson. A Finished lesson is listed on the learner + * interface but all the learner can do is view the progress bar and do an + * export portfolio - they cannot access any of the tool screens. + * + * @param lessonId + * @param userId checks that the user is a staff member for this lesson + * @param endDate teh lesson end date and time. + */ + public void finishLesson(long lessonId, Integer userId) throws UserAccessDeniedException; + + /** + * Set whether or not the export portfolio button is available in learner. Sets it to FALSE if + * learnerExportAvailable is null. Checks that the user is a staff member of this lesson before + * updating. + * + * @param lessonId + * @param userId + * @param learnerExportAvailable + * @return new value for learnerExportAvailable. Normally will be same as input parameter, will only + * be different if the value cannot be updated for some reason. + */ + public Boolean setLearnerPortfolioAvailable(long lessonId, Integer userId, Boolean learnerExportAvailable); + + /** + * Force Complete works on an individual user. The teacher may complete it up to a particular activity, + * or till the end of the sequence which activity id is null indicating complete to end. Note, the give + * activity will be complete as well. + * + * @param learnerId + * @param requesterId the user id of the person requesting the force complete. For security check + * @param lessonId + * @param activityId force complete to this activity(this activity will be force complete as well). + * If null value, force will complete all activities in this lesson. + * @return success message. + */ + public String forceCompleteLessonByUser(Integer learnerId, Integer requesterId, long lessonId, Long activityId); + + /** + * Archive the specified lesson. When archived, the data is retained + * but the learners cannot access the details. + * @param lessonId the specified the lesson id. + * @param userId checks that the user is a staff member for this lesson + */ + public void archiveLesson(long lessonId, Integer userId) throws UserAccessDeniedException; + + /** + * Unarchive the specified the lesson. Reverts back to its previous state. + * @param lessonId the specified the lesson id. + */ + public void unarchiveLesson(long lessonId, Integer userId); + + /** + * A lesson can only be suspended if it is started. The purpose of suspending is + * to hide the lesson from learners temporarily. + * @param lessonId the lesson ID which will be suspended. + * @param userId checks that the user is a staff member for this lesson + */ + public void suspendLesson(long lessonId, Integer userId) throws UserAccessDeniedException; + + /** + * Unsuspend a lesson, which state must be Lesson.SUSPEND_STATE. Returns the lesson + * back to its previous state. Otherwise an exception will be thrown. + * @param lessonId + * @param userId checks that the user is a staff member for this lesson + */ + public void unsuspendLesson(long lessonId, Integer userId) throws UserAccessDeniedException; + + /** + *

    + * Teachers sometimes find that there are just too many "old" designs and + * wish to remove them and never access them again. This function disables + * the lesson - it does not remove the contents from the database + *

    + * @param lessonId the specified the lesson id. + * @param userId checks that the user is a staff member for this lesson + */ public void removeLesson(long lessonId, Integer userId) throws UserAccessDeniedException; - /** - * Set the gate to open to let all the learners through. This learning service - * is triggerred by the system scheduler. Will return true GateActivity (or subclass) - * object, rather than a hibernate proxy. This is needed so that the class can - * be returned to the web layer for proper handling. - * - * @param gate the id of the gate we need to open. - */ - public GateActivity openGate(Long gateId); - - /** - * Set the gate to closed. - * @param gate the id of the gate we need to close. - */ - public GateActivity closeGate(Long gateId); - - /** - * This method returns the details for the given Lesson in - * WDDX format. Object inside the packet is a LessonDetailsDTO. - * - * @param lessonID The lesson_id of the Lesson for which the details have - * to be fetched - * @param userID The user who is fetching the Lesson details - * @return String The requested details in wddx format - * @throws IOException - */ - public String getLessonDetails(Long lessonID, Integer userID)throws IOException; - - /** - * Returns a list of learners participating in the given Lesson - * - * @param lessonID The lesson_id of the Lesson - * @param userID The user id of the user requesting the lesson learners - * @return String The requested list in wddx format - * - * @throws IOException - */ - public String getLessonLearners(Long lessonID, Integer userID)throws IOException; - - /** - * Returns a list of staff participating in the given Lesson - * - * @param lessonID The lesson_id of the Lesson - * @param userID The user id of the user requesting the lesson staff members - * @return String The requested list in wddx format - * - * @throws IOException - */ - public String getLessonStaff(Long lessonID, Integer userID)throws IOException; - - - /** - * This method returns the LearningDesign details for a given Lesson - * - * @param lessonID The lesson_id of the Lesson whose LearningDesign details are required - * @return String The requested details in wddx format - * @throws IOException - */ - public String getLearningDesignDetails(Long lessonID)throws IOException; - - /** - * This method returns the progress information of all learners - * in a given Lesson. - * - * @param lessonID The lesson_id of the Lesson whose progress information is required - * @param userID The user id of the user requesting the progress details - * @return String The requested information in wddx format - * @throws IOException - */ - public String getAllLearnersProgress(Long lessonID, Integer userID)throws IOException; - - /** - * This method returns a batch of progress information of learners - * in a given Lesson. It returns the first batch of users, using the learner's name as the - * sorting order. The batch size is determined by the LEARNER_PROGRESS_BATCH size - * in the database. - * - * @param lessonID The lesson_id of the Lesson whose progress information is required - * @param userID The user id of the user requesting the progress details - * @return String The requested information in wddx format - * @throws IOException - */ - public String getInitialLearnersProgress(Long lessonID, Integer userID)throws IOException; - /** - * This method returns the next batch of progress information of learners - * in a given Lesson. The batch size is determined by the LEARNER_PROGRESS_BATCH size - * in the database, and the next batch starts at after the supplied user_id - * - * @param lessonID The lesson_id of the Lesson whose progress information is required - * @param lastUserID The user id of the last user from the previous batch - * @param userID The user id of the user requesting the progress details - * @return String The requested information in wddx format - * @throws IOException - */ - public String getAdditionalLearnersProgress(Long lessonID, Integer lastUserID, Integer userID)throws IOException; + /** + * Set the gate to open to let all the learners through. This learning service + * is triggerred by the system scheduler. Will return true GateActivity (or subclass) + * object, rather than a hibernate proxy. This is needed so that the class can + * be returned to the web layer for proper handling. + * + * @param gate the id of the gate we need to open. + */ + public GateActivity openGate(Long gateId); - /** - * This method is called when the user clicks the 'Contribute' tab in the - * monitoring enviornment. It returns a list of activities "in the - * order" they have to be performed and with additional information as to - * what kind of contribution (Define later content, Moderation, Contribution, - * Permission for gate activity, Chosen Grouing etc.) is reuired from the - * user(teacher/staff). - * - * @param lessonID The lesson_id of the Lesson for which the information has - * to be fetched. - * @return String The required information in WDDX format - * @throws IOException - */ - public String getAllContributeActivities(Long lessonID)throws IOException, LearningDesignProcessorException; - - /** - * This method returns the url associated with the activity in the monitoring - * enviornment. This is the URL that opens up when the user/teacher clicks on - * the activity in the monitoring enviornment and then selects a learner OR - * in the LEARNER tab when a learner's activity is clicked. - * - * This is also known as the learner progress url. - * - * @param lessonID The lesson_id of the Lesson for which the information has - * to be fetched. - * @param activityID The activity_id of the activity for which the URL is required - * @param learnerID The user_id of the Learner for whom the URL is being fetched - * @param requesterID The user_id of the user who is requesting the url - * @return String The required information in WDDX format - * @throws IOException - * @throws LamsToolServiceException - */ - public String getLearnerActivityURL(Long lessonID, Long activityID,Integer learnerUserID, Integer requestingUserId)throws IOException,LamsToolServiceException; - - /** - * This method returns the define later url for the given activity. - * - * @param lessonID The lesson_id of the Lesson for which the information has - * to be fetched. - * @param activityID The activity_id of the Activity whose URL will be returned - * @param userID The user id of the user requesting the url. - * @return String the url - * @throws IOException - */ - public String getActivityDefineLaterURL(Long lessonID, Long activityID, Integer userID)throws IOException, LamsToolServiceException; + /** + * Allows a single learner to pass the gate. + * @param gateId + * @param userId + * @return + */ + public GateActivity openGateForSingleUser(Long gateId, Integer userId); - /** - * This method returns the monitor url for the given activity - * - * @param lessonID The lesson_id of the Lesson for which the information has - * to be fetched. - * @param activityID The activity_id of the Activity whose URL will be returned - * @param userID The user id of the user requesting the url. - * @return String The required information in WDDX format - * @throws IOException - */ - public String getActivityMonitorURL(Long lessonID, Long activityID, String contentFolderID, Integer userID)throws IOException, LamsToolServiceException; - - /** - * This method moves the learning design corresponding to the given - * Lesson into the specified workspaceFolder. But before this action - * is performed it checks whether the user is authorized to do so. - * If not, Flash is notified of the same. As of now it is assumed that - * only the owner of lesson/learning design can move it - * - * @param lessonID The lesson_id of the Lesson which has to be moved - * @param targetWorkspaceFolderID The workspace_folder_id of the WorkspaceFolder - * to which the lesson has to be moved - * @param userID The user_id of the User who has requested this operation - * @return String The acknowledgement message/error in WDDX format - * @throws IOException - */ - public String moveLesson(Long lessonID, Integer targetWorkspaceFolderID,Integer userID)throws IOException; - /** - * This method changes the name of an existing Lesson to the - * one specified. - * - * @param lessonID The lesson_id of the Lesson whose name has to be changed - * @param newName The new name of the Lesson - * @param userID The user_id of the User who has requested this operation - * @return String The acknowledgement message/error in WDDX format - * @throws IOException - */ - public String renameLesson(Long lessonID, String newName, Integer userID)throws IOException; - - /** - * Return an activity object based on the requested id. - * @param activityId id of the activity. - * @return the requested activity object. - */ - public Activity getActivityById(Long activityId); - - /** - * Return an activity object based on the requested id. Where possible, give it the type we want - * so that it can be cast properly. - * - * @param activityId id of the activity. - * @return the requested activity object. - */ - public Activity getActivityById(Long activityId, Class clasz); - - /** - * Return an activity object based on the requested id. - * @param activityId id of the activity. - * @return the requested activity object. - */ - public GroupingActivity getGroupingActivityById(Long activityID); + /** + * Set the gate to closed. + * @param gate the id of the gate we need to close. + */ + public GateActivity closeGate(Long gateId); - /** - * Returns the status of the gate in WDDX format. - * - * @param activityID The activity_id of the Activity whose gate must be checked - * @param lessonID The lesson_id of the Lesson - * @return - */ - public String checkGateStatus(Long activityID, Long lessonID) throws IOException; - - /** - * Returns an acknowledgement that the gate has been released. - * Returns true if the gate has been released and false otherwise. - * This information is packaged in WDDX format - * - * @param activityID The activity_id of the Activity whose gate must be checked - * @param lessonID The lesson_id of the Lesson - * @return - */ - public String releaseGate(Long activityID) throws IOException; - - /** - * Perform chosen grouping. The groups contains a list of Hashtable which contain - * following information for each group:
    - *
      - *
    1. List of learners in this group
    2. - *
    3. Order ID
    4. - *
    5. Group name
    6. - *
    - * - * @param groupingActivity - * @param groups list of group information. - */ - public void performChosenGrouping(GroupingActivity groupingActivity, List groups) throws LessonServiceException; - + /** + * This method returns the details for the given Lesson in + * WDDX format. Object inside the packet is a LessonDetailsDTO. + * + * @param lessonID The lesson_id of the Lesson for which the details have + * to be fetched + * @param userID The user who is fetching the Lesson details + * @return String The requested details in wddx format + * @throws IOException + */ + public String getLessonDetails(Long lessonID, Integer userID) throws IOException; + + /** + * Returns a list of learners participating in the given Lesson + * + * @param lessonID The lesson_id of the Lesson + * @param userID The user id of the user requesting the lesson learners + * @return String The requested list in wddx format + * + * @throws IOException + */ + public String getLessonLearners(Long lessonID, Integer userID) throws IOException; + + /** + * Returns a list of staff participating in the given Lesson + * + * @param lessonID The lesson_id of the Lesson + * @param userID The user id of the user requesting the lesson staff members + * @return String The requested list in wddx format + * + * @throws IOException + */ + public String getLessonStaff(Long lessonID, Integer userID) throws IOException; + + /** + * This method returns the LearningDesign details for a given Lesson + * + * @param lessonID The lesson_id of the Lesson whose LearningDesign details are required + * @return String The requested details in wddx format + * @throws IOException + */ + public String getLearningDesignDetails(Long lessonID) throws IOException; + + /** + * This method returns the progress information of all learners + * in a given Lesson. + * + * @param lessonID The lesson_id of the Lesson whose progress information is required + * @param userID The user id of the user requesting the progress details + * @return String The requested information in wddx format + * @throws IOException + */ + public String getAllLearnersProgress(Long lessonID, Integer userID) throws IOException; + + /** + * This method returns a batch of progress information of learners + * in a given Lesson. It returns the first batch of users, using the learner's name as the + * sorting order. The batch size is determined by the LEARNER_PROGRESS_BATCH size + * in the database. + * + * @param lessonID The lesson_id of the Lesson whose progress information is required + * @param userID The user id of the user requesting the progress details + * @return String The requested information in wddx format + * @throws IOException + */ + public String getInitialLearnersProgress(Long lessonID, Integer userID) throws IOException; + + /** + * This method returns the next batch of progress information of learners + * in a given Lesson. The batch size is determined by the LEARNER_PROGRESS_BATCH size + * in the database, and the next batch starts at after the supplied user_id + * + * @param lessonID The lesson_id of the Lesson whose progress information is required + * @param lastUserID The user id of the last user from the previous batch + * @param userID The user id of the user requesting the progress details + * @return String The requested information in wddx format + * @throws IOException + */ + public String getAdditionalLearnersProgress(Long lessonID, Integer lastUserID, Integer userID) throws IOException; + + /** + * This method is called when the user clicks the 'Contribute' tab in the + * monitoring enviornment. It returns a list of activities "in the + * order" they have to be performed and with additional information as to + * what kind of contribution (Define later content, Moderation, Contribution, + * Permission for gate activity, Chosen Grouing etc.) is reuired from the + * user(teacher/staff). + * + * @param lessonID The lesson_id of the Lesson for which the information has + * to be fetched. + * @return String The required information in WDDX format + * @throws IOException + */ + public String getAllContributeActivities(Long lessonID) throws IOException, LearningDesignProcessorException; + + /** + * This method returns the url associated with the activity in the monitoring + * enviornment. This is the URL that opens up when the user/teacher clicks on + * the activity in the monitoring enviornment and then selects a learner OR + * in the LEARNER tab when a learner's activity is clicked. + * + * This is also known as the learner progress url. + * + * @param lessonID The lesson_id of the Lesson for which the information has + * to be fetched. + * @param activityID The activity_id of the activity for which the URL is required + * @param learnerID The user_id of the Learner for whom the URL is being fetched + * @param requesterID The user_id of the user who is requesting the url + * @return String The required information in WDDX format + * @throws IOException + * @throws LamsToolServiceException + */ + public String getLearnerActivityURL(Long lessonID, Long activityID, Integer learnerUserID, Integer requestingUserId) + throws IOException, LamsToolServiceException; + + /** + * This method returns the define later url for the given activity. + * + * @param lessonID The lesson_id of the Lesson for which the information has + * to be fetched. + * @param activityID The activity_id of the Activity whose URL will be returned + * @param userID The user id of the user requesting the url. + * @return String the url + * @throws IOException + */ + public String getActivityDefineLaterURL(Long lessonID, Long activityID, Integer userID) throws IOException, + LamsToolServiceException; + + /** + * This method returns the monitor url for the given activity + * + * @param lessonID The lesson_id of the Lesson for which the information has + * to be fetched. + * @param activityID The activity_id of the Activity whose URL will be returned + * @param userID The user id of the user requesting the url. + * @return String The required information in WDDX format + * @throws IOException + */ + public String getActivityMonitorURL(Long lessonID, Long activityID, String contentFolderID, Integer userID) + throws IOException, LamsToolServiceException; + + /** + * This method moves the learning design corresponding to the given + * Lesson into the specified workspaceFolder. But before this action + * is performed it checks whether the user is authorized to do so. + * If not, Flash is notified of the same. As of now it is assumed that + * only the owner of lesson/learning design can move it + * + * @param lessonID The lesson_id of the Lesson which has to be moved + * @param targetWorkspaceFolderID The workspace_folder_id of the WorkspaceFolder + * to which the lesson has to be moved + * @param userID The user_id of the User who has requested this operation + * @return String The acknowledgement message/error in WDDX format + * @throws IOException + */ + public String moveLesson(Long lessonID, Integer targetWorkspaceFolderID, Integer userID) throws IOException; + + /** + * This method changes the name of an existing Lesson to the + * one specified. + * + * @param lessonID The lesson_id of the Lesson whose name has to be changed + * @param newName The new name of the Lesson + * @param userID The user_id of the User who has requested this operation + * @return String The acknowledgement message/error in WDDX format + * @throws IOException + */ + public String renameLesson(Long lessonID, String newName, Integer userID) throws IOException; + + /** + * Return an activity object based on the requested id. + * @param activityId id of the activity. + * @return the requested activity object. + */ + public Activity getActivityById(Long activityId); + + /** + * Return an activity object based on the requested id. Where possible, give it the type we want + * so that it can be cast properly. + * + * @param activityId id of the activity. + * @return the requested activity object. + */ + public Activity getActivityById(Long activityId, Class clasz); + + /** + * Return an activity object based on the requested id. + * @param activityId id of the activity. + * @return the requested activity object. + */ + public GroupingActivity getGroupingActivityById(Long activityID); + + /** + * Returns the status of the gate in WDDX format. + * + * @param activityID The activity_id of the Activity whose gate must be checked + * @param lessonID The lesson_id of the Lesson + * @return + */ + public String checkGateStatus(Long activityID, Long lessonID) throws IOException; + + /** + * Returns an acknowledgement that the gate has been released. + * Returns true if the gate has been released and false otherwise. + * This information is packaged in WDDX format + * + * @param activityID The activity_id of the Activity whose gate must be checked + * @param lessonID The lesson_id of the Lesson + * @return + */ + public String releaseGate(Long activityID) throws IOException; + + /** + * Perform chosen grouping. The groups contains a list of Hashtable which contain + * following information for each group:
    + *
      + *
    1. List of learners in this group
    2. + *
    3. Order ID
    4. + *
    5. Group name
    6. + *
    + * + * @param groupingActivity + * @param groups list of group information. + */ + public void performChosenGrouping(GroupingActivity groupingActivity, List groups) throws LessonServiceException; + //--------------------------------------------------------------------- // Preview Methods //--------------------------------------------------------------------- @@ -506,8 +520,7 @@ * @param lessonID ID of the lesson * @return Lesson */ - public abstract Lesson createPreviewClassForLesson(int userID, - long lessonID) throws UserAccessDeniedException; + public abstract Lesson createPreviewClassForLesson(int userID, long lessonID) throws UserAccessDeniedException; /** * Remove all the details for a particular preview lessons. The transaction @@ -531,38 +544,39 @@ public abstract int deleteAllOldPreviewLessons(); /* ************ Supports the Chosen Groupings and Branching **********************************/ - /** Get all the active learners in the lesson who are not in a group or in a branch. - * - * If the activity is a grouping activity, then set useCreatingGrouping = true to - * base the list on the create grouping. Otherwise leave it false and it will use the - * grouping applied to the activity - this is used for branching activities. - * - * @param activityID - * @param lessonID - * @param useCreateGrouping true/false for GroupingActivities, always false for non-GroupingActivities - * @return Sorted set of Users, sorted by surname - */ - public SortedSet getClassMembersNotGrouped(Long lessonID, Long activityID, boolean useCreateGrouping); - + /** Get all the active learners in the lesson who are not in a group or in a branch. + * + * If the activity is a grouping activity, then set useCreatingGrouping = true to + * base the list on the create grouping. Otherwise leave it false and it will use the + * grouping applied to the activity - this is used for branching activities. + * + * @param activityID + * @param lessonID + * @param useCreateGrouping true/false for GroupingActivities, always false for non-GroupingActivities + * @return Sorted set of Users, sorted by surname + */ + public SortedSet getClassMembersNotGrouped(Long lessonID, Long activityID, boolean useCreateGrouping); + /** Add a new group to a grouping activity. If name already exists or the name is blank, does not add a new group. - * - * @param activityID id of the activity + * + * @param activityID id of the activity * @param name group name * @throws LessonServiceException */ - public abstract void addGroup(Long activityID, String name, boolean overrideMaxNumberOfGroups) throws LessonServiceException, MonitoringServiceException; - + public abstract void addGroup(Long activityID, String name, boolean overrideMaxNumberOfGroups) throws LessonServiceException, + MonitoringServiceException; + /** Remove a group to from a grouping activity. If the group does not exists then nothing happens. * If the group is already used (e.g. a tool session exists) then it throws a LessonServiceException. - * - * @param activityID id of the activity + * + * @param activityID id of the activity * @param name group name * @throws LessonServiceException **/ public abstract void removeGroup(Long activityID, Long groupID) throws LessonServiceException; /** Add learners to a group. Doesn't necessarily check if the user is already in another group. */ - public abstract void addUsersToGroup(Long activityID, Long groupID, String learnerIDs[]) throws LessonServiceException ; + public abstract void addUsersToGroup(Long activityID, Long groupID, String learnerIDs[]) throws LessonServiceException; /** Remove a user to a group. If the user is not in the group, then nothing is changed. * @throws LessonServiceException */ @@ -575,7 +589,7 @@ * @param sequenceActivityID Activity id of the sequenceActivity representing this branch * @param learnerIDs the IDS of the learners to be added. */ - public void addUsersToBranch(Long sequenceActivityID, String learnerIDs[]) throws LessonServiceException; + public void addUsersToBranch(Long sequenceActivityID, String learnerIDs[]) throws LessonServiceException; /** Remove learners from a branch. * Assumes there should only be one group for this branch. Use for Teacher Chosen Branching. Don't use @@ -589,14 +603,14 @@ /** Has anyone started this branch / branching activity ? Irrespective of the groups. * Used to determine if a branch mapping can be removed. */ public boolean isActivityAttempted(Activity activity) throws LessonServiceException; - + /** Match group(s) to a branch. Doesn't necessarily check if the group is already assigned to another branch. * Use for Group Based Branching and define later. * * @param sequenceActivityID Activity id of the sequenceActivity representing this branch * @param learnerIDs the IDS of the learners to be added. */ - public void addGroupToBranch(Long sequenceActivityID, String groupIDs[]) throws LessonServiceException; + public void addGroupToBranch(Long sequenceActivityID, String groupIDs[]) throws LessonServiceException; /** Remove group / branch mapping. Cannot be done if any users in the group have started the branch. * Used for group based branching in define later. @@ -612,7 +626,7 @@ * @param branchingActivityID Activity id of the branchingActivity */ public SortedSet getGroupsNotAssignedToBranch(Long branchingActivityID) throws LessonServiceException; - + /** * Get the list of users who have attempted an activity. This is based on the progress engine records. * This will give the users in all tool sessions for an activity (if it is a tool activity) or @@ -623,5 +637,5 @@ /** Get the record of the learner's progress for a particular lesson */ public LearnerProgress getLearnerProgress(Integer learnerId, Long lessonId); - + } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r8e93b3a767193227999bb78467b78114acb079dd -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 8e93b3a767193227999bb78467b78114acb079dd) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -92,8 +92,8 @@ import org.lamsfoundation.lams.util.audit.AuditService; import org.lamsfoundation.lams.util.wddx.FlashMessage; import org.lamsfoundation.lams.util.wddx.WDDXProcessor; -import org.lamsfoundation.lams.util.wddx.WDDXTAGS; import org.lamsfoundation.lams.util.wddx.WDDXProcessorConversionException; +import org.lamsfoundation.lams.util.wddx.WDDXTAGS; import org.lamsfoundation.lams.web.util.AttributeNames; import org.quartz.JobDetail; import org.quartz.Scheduler; @@ -104,7 +104,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; - /** *

    This is the major service facade for all monitoring functionalities. It is * configured as a Spring factory bean so as to utilize the Spring's IOC and @@ -120,65 +119,65 @@ * @since 2/02/2005 * @version 1.1 */ -public class MonitoringService implements IMonitoringService,ApplicationContextAware -{ +public class MonitoringService implements IMonitoringService, ApplicationContextAware { - //--------------------------------------------------------------------- - // Instance variables - //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + // Instance variables + //--------------------------------------------------------------------- private static Logger log = Logger.getLogger(MonitoringService.class); - private static final long numMilliSecondsInADay = 24 * 60 * 60 * 1000; - private static final String AUDIT_LEARNER_PORTFOLIO_SET = "audit.learner.portfolio.set"; - private static final String AUDIT_LESSON_CREATED_KEY = "audit.lesson.created"; - private static final int DEFAULT_LEARNER_PROGRESS_BATCH_SIZE = 15; - - private ILessonDAO lessonDAO; - private ILessonClassDAO lessonClassDAO; - private ITransitionDAO transitionDAO; - private IActivityDAO activityDAO; - private IBaseDAO baseDAO; - private ILearningDesignDAO learningDesignDAO; - private IGroupingDAO groupingDAO; - private ILearnerProgressDAO learnerProgressDAO; - private IAuthoringService authoringService; - private ICoreLearnerService learnerService; - private ILessonService lessonService; - private ILamsCoreToolService lamsCoreToolService; - private IUserManagementService userManagementService; - private Scheduler scheduler; - private ApplicationContext applicationContext; - private MessageService messageService; - private AuditService auditService; - - - /** Message keys */ - private static final String FORCE_COMPLETE_STOP_MESSAGE_ACTIVITY_DONE = "force.complete.stop.message.activity.done"; - private static final String FORCE_COMPLETE_STOP_MESSAGE_GROUPING_ERROR = "force.complete.stop.message.grouping.error"; - private static final String FORCE_COMPLETE_STOP_MESSAGE_GROUPING = "force.complete.stop.message.grouping"; - private static final String FORCE_COMPLETE_STOP_MESSAGE_GATE = "force.complete.stop.message.gate"; - private static final String FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_ACTIVITY = "force.complete.stop.message.completed.to.activity"; - private static final String FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_END = "force.complete.stop.message.completed.to.end"; - private static final String FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY = "force.complete.stop.message.stopped.unexpectedly"; + private static final long numMilliSecondsInADay = 24 * 60 * 60 * 1000; + private static final String AUDIT_LEARNER_PORTFOLIO_SET = "audit.learner.portfolio.set"; + private static final String AUDIT_LESSON_CREATED_KEY = "audit.lesson.created"; + private static final int DEFAULT_LEARNER_PROGRESS_BATCH_SIZE = 15; + private ILessonDAO lessonDAO; + private ILessonClassDAO lessonClassDAO; + private ITransitionDAO transitionDAO; + private IActivityDAO activityDAO; + private IBaseDAO baseDAO; + private ILearningDesignDAO learningDesignDAO; + private IGroupingDAO groupingDAO; + private ILearnerProgressDAO learnerProgressDAO; + private IAuthoringService authoringService; + private ICoreLearnerService learnerService; + private ILessonService lessonService; + private ILamsCoreToolService lamsCoreToolService; + private IUserManagementService userManagementService; + private Scheduler scheduler; + private ApplicationContext applicationContext; + private MessageService messageService; + private AuditService auditService; + + /** Message keys */ + private static final String FORCE_COMPLETE_STOP_MESSAGE_ACTIVITY_DONE = "force.complete.stop.message.activity.done"; + private static final String FORCE_COMPLETE_STOP_MESSAGE_GROUPING_ERROR = "force.complete.stop.message.grouping.error"; + private static final String FORCE_COMPLETE_STOP_MESSAGE_GROUPING = "force.complete.stop.message.grouping"; + private static final String FORCE_COMPLETE_STOP_MESSAGE_GATE = "force.complete.stop.message.gate"; + private static final String FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_ACTIVITY = "force.complete.stop.message.completed.to.activity"; + private static final String FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_END = "force.complete.stop.message.completed.to.end"; + private static final String FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY = "force.complete.stop.message.stopped.unexpectedly"; + //--------------------------------------------------------------------- - // Inversion of Control Methods - Method injection - //--------------------------------------------------------------------- - /** - * @param messageService the i18n Service bean. - */ + // Inversion of Control Methods - Method injection + //--------------------------------------------------------------------- + /** + * @param messageService the i18n Service bean. + */ public void setMessageService(MessageService messageService) { this.messageService = messageService; } + public MessageService getMessageService() { return messageService; } + /** * @param userManagementService The userManagementService to set. */ - public void setUserManagementService( - IUserManagementService userManagementService) { + public void setUserManagementService(IUserManagementService userManagementService) { this.userManagementService = userManagementService; } + /** * @param learningDesignDAO The learningDesignDAO to set. */ @@ -192,575 +191,558 @@ public void setTransitionDAO(ITransitionDAO transitionDAO) { this.transitionDAO = transitionDAO; } + /** * * @param learnerService */ public void setLearnerService(ICoreLearnerService learnerService) { this.learnerService = learnerService; } + /** * * @param lessonService */ public void setLessonService(ILessonService lessonService) { this.lessonService = lessonService; } - /** - * @param authoringService The authoringService to set. - */ - public void setAuthoringService(IAuthoringService authoringService) - { - this.authoringService = authoringService; - } - /** - * @param lessonClassDAO The lessonClassDAO to set. - */ - public void setLessonClassDAO(ILessonClassDAO lessonClassDAO) - { - this.lessonClassDAO = lessonClassDAO; - } + /** + * @param authoringService The authoringService to set. + */ + public void setAuthoringService(IAuthoringService authoringService) { + this.authoringService = authoringService; + } - /** - * @param lessonDAO The lessonDAO to set. - */ - public void setLessonDAO(ILessonDAO lessonDAO) - { - this.lessonDAO = lessonDAO; - } + /** + * @param lessonClassDAO The lessonClassDAO to set. + */ + public void setLessonClassDAO(ILessonClassDAO lessonClassDAO) { + this.lessonClassDAO = lessonClassDAO; + } - /** - * @param learnerProgressDAO The learnerProgressDAO to set. - */ - public void setLearnerProgressDAO(ILearnerProgressDAO learnerProgressDAO) - { - this.learnerProgressDAO = learnerProgressDAO; - } + /** + * @param lessonDAO The lessonDAO to set. + */ + public void setLessonDAO(ILessonDAO lessonDAO) { + this.lessonDAO = lessonDAO; + } - /** - * @param userDAO - */ + /** + * @param learnerProgressDAO The learnerProgressDAO to set. + */ + public void setLearnerProgressDAO(ILearnerProgressDAO learnerProgressDAO) { + this.learnerProgressDAO = learnerProgressDAO; + } + + /** + * @param userDAO + */ public void setBaseDAO(IBaseDAO baseDAO) { this.baseDAO = baseDAO; } + /** * @param groupingDAO */ public void setGroupingDAO(IGroupingDAO groupingDAO) { this.groupingDAO = groupingDAO; } - /** - * @param lamsToolService The lamsToolService to set. - */ - public void setLamsCoreToolService(ILamsCoreToolService lamsToolService) - { - this.lamsCoreToolService = lamsToolService; - } + /** + * @param lamsToolService The lamsToolService to set. + */ + public void setLamsCoreToolService(ILamsCoreToolService lamsToolService) { + lamsCoreToolService = lamsToolService; + } - /** - * @param activityDAO The activityDAO to set. - */ - public void setActivityDAO(IActivityDAO activityDAO) - { - this.activityDAO = activityDAO; - } + /** + * @param activityDAO The activityDAO to set. + */ + public void setActivityDAO(IActivityDAO activityDAO) { + this.activityDAO = activityDAO; + } - /** - * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) - */ - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - this.applicationContext=applicationContext; - } - - /** - * @param scheduler The scheduler to set. - */ - public void setScheduler(Scheduler scheduler) - { - this.scheduler = scheduler; - } - - public void setAuditService(AuditService auditService) { + /** + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + /** + * @param scheduler The scheduler to set. + */ + public void setScheduler(Scheduler scheduler) { + this.scheduler = scheduler; + } + + public void setAuditService(AuditService auditService) { this.auditService = auditService; } - //--------------------------------------------------------------------- - // Service Methods - //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + // Service Methods + //--------------------------------------------------------------------- - private void auditAction(String messageKey, Object[] args) { + private void auditAction(String messageKey, Object[] args) { String message = messageService.getMessage(messageKey, args); auditService.log("Monitoring", message); } /** Checks whether the user is a staff member for the lesson or the creator of the lesson. - * If not, throws a UserAccessDeniedException exception */ - private void checkOwnerOrStaffMember(Integer userId, Lesson lesson, String actionDescription) { - User user = (User)baseDAO.find(User.class,userId); - - if ( lesson.getUser() != null && lesson.getUser().getUserId().equals(userId) ) { - return; - } - - if ( lesson == null || lesson.getLessonClass()==null || !lesson.getLessonClass().isStaffMember(user) ) { - throw new UserAccessDeniedException("User "+userId+" may not "+actionDescription+" for lesson "+lesson.getLessonId()); - } - } + * If not, throws a UserAccessDeniedException exception */ + private void checkOwnerOrStaffMember(Integer userId, Lesson lesson, String actionDescription) { + User user = (User) baseDAO.find(User.class, userId); - /** - *

    Create new lesson according to the learning design specified by the - * user. This involves following major steps:

    - * - *
  • 1. Make a runtime copy of static learning design defined in authoring
  • - *
  • 2. Go through all the tool activities defined in the learning design, - * create a runtime copy of all tool's content.
  • - * - *

    As a runtime design, it is not copied into any folder.

    - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#initializeLesson(String, String, long, Integer) - */ - public Lesson initializeLesson(String lessonName, - String lessonDescription, - Boolean learnerExportAvailable, - long learningDesignId, - Integer organisationId, - Integer userID, - String customCSV) - { - - LearningDesign originalLearningDesign = authoringService.getLearningDesign(new Long(learningDesignId)); - if ( originalLearningDesign == null) { - throw new MonitoringServiceException("Learning design for id="+learningDesignId+" is missing. Unable to initialize lesson."); - } - - // The duplicated sequence should go in the run sequences folder under the given organisation - WorkspaceFolder runSeqFolder = null; - int MAX_DEEP_LEVEL_FOLDER = 50; - if ( organisationId != null ) { - Organisation org = (Organisation)baseDAO.find(Organisation.class,organisationId); - // Don't use unlimited loop to avoid dead lock. For instance, orgA is orgB parent, but orgB parent is orgA as well. - for(int idx=0;idxCreate new lesson according to the learning design specified by the + * user. This involves following major steps:

    + * + *
  • 1. Make a runtime copy of static learning design defined in authoring
  • + *
  • 2. Go through all the tool activities defined in the learning design, + * create a runtime copy of all tool's content.
  • + * + *

    As a runtime design, it is not copied into any folder.

    + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#initializeLesson(String, String, long, Integer) + */ + public Lesson initializeLesson(String lessonName, String lessonDescription, Boolean learnerExportAvailable, + long learningDesignId, Integer organisationId, Integer userID, String customCSV) { + + LearningDesign originalLearningDesign = authoringService.getLearningDesign(new Long(learningDesignId)); + if (originalLearningDesign == null) { + throw new MonitoringServiceException("Learning design for id=" + learningDesignId + + " is missing. Unable to initialize lesson."); + } + + // The duplicated sequence should go in the run sequences folder under the given organisation + WorkspaceFolder runSeqFolder = null; + int MAX_DEEP_LEVEL_FOLDER = 50; + if (organisationId != null) { + Organisation org = (Organisation) baseDAO.find(Organisation.class, organisationId); + // Don't use unlimited loop to avoid dead lock. For instance, orgA is orgB parent, but orgB parent is orgA as well. + for (int idx = 0; idx < MAX_DEEP_LEVEL_FOLDER; idx++) { + if (org != null) { + Workspace workspace = org.getWorkspace(); + if (workspace != null) { + runSeqFolder = workspace.getDefaultRunSequencesFolder(); + } + if (runSeqFolder == null) { + org = org.getParentOrganisation(); + } + else { + break; + } + } + else { + break; + } + } + } + + User user = userID != null ? (User) baseDAO.find(User.class, userID) : null; + return initializeLesson(lessonName, lessonDescription, learnerExportAvailable, originalLearningDesign, user, + runSeqFolder, LearningDesign.COPY_TYPE_LESSON, customCSV); + + } + + /** + * Create new lesson according to the learning design specified by the + * user, but for a preview session rather than a normal learning session. + * The design is not assigned to any workspace folder. + */ + public Lesson initializeLessonForPreview(String lessonName, String lessonDescription, long learningDesignId, Integer userID, + String customCSV) { + LearningDesign originalLearningDesign = authoringService.getLearningDesign(new Long(learningDesignId)); + if (originalLearningDesign == null) { + throw new MonitoringServiceException("Learning design for id=" + learningDesignId + + " is missing. Unable to initialize lesson."); + } + User user = userID != null ? (User) baseDAO.find(User.class, userID) : null; + + return initializeLesson(lessonName, lessonDescription, Boolean.TRUE, originalLearningDesign, user, null, + LearningDesign.COPY_TYPE_PREVIEW, customCSV); + } + + public Lesson initializeLesson(String lessonName, String lessonDescription, Boolean learnerExportAvailable, + LearningDesign originalLearningDesign, User user, WorkspaceFolder workspaceFolder, int copyType, String customCSV) { + + //copy the current learning design + LearningDesign copiedLearningDesign = authoringService.copyLearningDesign(originalLearningDesign, new Integer(copyType), + user, workspaceFolder, true, null, customCSV); + authoringService.saveLearningDesign(copiedLearningDesign); + + // Make all efforts to make sure it has a title + String title = lessonName != null ? lessonName : copiedLearningDesign.getTitle(); + title = title != null ? title : "Unknown Lesson"; + + Lesson lesson = createNewLesson(title, lessonDescription, user, learnerExportAvailable, copiedLearningDesign); + auditAction(MonitoringService.AUDIT_LESSON_CREATED_KEY, new Object[] { lessonName, copiedLearningDesign.getTitle(), + learnerExportAvailable }); + return lesson; + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#initializeLesson(java.util.Integer, java.lang.String) + */ + public String initializeLesson(Integer creatorUserId, String lessonPacket) throws Exception { + FlashMessage flashMessage = null; + + try { + Hashtable table = (Hashtable) WDDXProcessor.deserialize(lessonPacket); + + // parse WDDX values + + String title = WDDXProcessor.convertToString("lessonName", table.get("lessonName")); + String desc = WDDXProcessor.convertToString("lessonDescription", table.get("lessonDescription")); + int copyType = WDDXProcessor.convertToInt("copyType", table.get("copyType")); + Integer organisationId = WDDXProcessor.convertToInteger("organisationID", table.get("organisationID")); + long ldId = WDDXProcessor.convertToLong(AttributeNames.PARAM_LEARNINGDESIGN_ID, table + .get(AttributeNames.PARAM_LEARNINGDESIGN_ID)); + boolean learnerExportAvailable = WDDXProcessor.convertToBoolean("learnerExportPortfolio", table + .get("learnerExportPortfolio")); + String customCSV = WDDXProcessor.convertToString(WDDXTAGS.CUSTOM_CSV, table.get(WDDXTAGS.CUSTOM_CSV)); + + // initialize lesson + + Lesson newLesson = null; + + if (copyType == LearningDesign.COPY_TYPE_PREVIEW) { + newLesson = initializeLessonForPreview(title, desc, ldId, creatorUserId, customCSV); + } + else { + newLesson = initializeLesson(title, desc, learnerExportAvailable, ldId, organisationId, creatorUserId, customCSV); + } + + if (newLesson != null) { + flashMessage = new FlashMessage("initializeLesson", newLesson.getLessonId()); + } + + return flashMessage.serializeMessage(); + + } + catch (Exception e) { + MonitoringService.log.error("Exception occured trying to create a lesson class ", e); throw new Exception(e); } - - } - - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#createLessonClassForLessonWDDX(Integer, String, java.util.Integer) - */ - public String createLessonClassForLessonWDDX(Integer creatorUserId, String lessonPacket) throws UserAccessDeniedException { - FlashMessage flashMessage = null; - - try{ - Hashtable table = (Hashtable)WDDXProcessor.deserialize(lessonPacket); - //todo: convert:data type: - Integer orgId = - WDDXProcessor.convertToInteger(MonitoringConstants.KEY_ORGANISATION_ID, table.get(MonitoringConstants.KEY_ORGANISATION_ID)); - long lessonId = - WDDXProcessor.convertToLong(MonitoringConstants.KEY_LESSON_ID, table.get(MonitoringConstants.KEY_LESSON_ID)).longValue(); - //get leaner group info - Hashtable learnerMap = (Hashtable) table.get(MonitoringConstants.KEY_LEARNER); - List learners = (List) learnerMap.get(MonitoringConstants.KEY_USERS); - String learnerGroupName = WDDXProcessor.convertToString(learnerMap, MonitoringConstants.KEY_GROUP_NAME); - //get staff group info - Hashtable staffMap = (Hashtable) table.get(MonitoringConstants.KEY_STAFF); - List staffs = (List) staffMap.get(MonitoringConstants.KEY_USERS); - String staffGroupName = WDDXProcessor.convertToString(staffMap, MonitoringConstants.KEY_GROUP_NAME); - - if(learners == null) - learners = new LinkedList(); - if(staffs == null) - staffs = new LinkedList(); - - Organisation organisation = (Organisation)baseDAO.find(Organisation.class,orgId); - User creator = (User)baseDAO.find(User.class,creatorUserId); - - // create the lesson class - add all the users in this organisation to the lesson class - // add user as staff - List learnerList = new LinkedList(); - Iterator iter = learners.iterator(); - while (iter.hasNext()) { - try { - int id = ((Double) iter.next()).intValue(); - learnerList.add((User)baseDAO.find(User.class,id)); - } catch (Exception e) { - log.error("Error parsing learner ID from " + lessonPacket); + + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#createLessonClassForLessonWDDX(Integer, String, java.util.Integer) + */ + public String createLessonClassForLessonWDDX(Integer creatorUserId, String lessonPacket) throws UserAccessDeniedException { + FlashMessage flashMessage = null; + + try { + Hashtable table = (Hashtable) WDDXProcessor.deserialize(lessonPacket); + //todo: convert:data type: + Integer orgId = WDDXProcessor.convertToInteger(MonitoringConstants.KEY_ORGANISATION_ID, table + .get(MonitoringConstants.KEY_ORGANISATION_ID)); + long lessonId = WDDXProcessor.convertToLong(MonitoringConstants.KEY_LESSON_ID, + table.get(MonitoringConstants.KEY_LESSON_ID)).longValue(); + //get leaner group info + Hashtable learnerMap = (Hashtable) table.get(MonitoringConstants.KEY_LEARNER); + List learners = (List) learnerMap.get(MonitoringConstants.KEY_USERS); + String learnerGroupName = WDDXProcessor.convertToString(learnerMap, MonitoringConstants.KEY_GROUP_NAME); + //get staff group info + Hashtable staffMap = (Hashtable) table.get(MonitoringConstants.KEY_STAFF); + List staffs = (List) staffMap.get(MonitoringConstants.KEY_USERS); + String staffGroupName = WDDXProcessor.convertToString(staffMap, MonitoringConstants.KEY_GROUP_NAME); + + if (learners == null) { + learners = new LinkedList(); + } + if (staffs == null) { + staffs = new LinkedList(); + } + + Organisation organisation = (Organisation) baseDAO.find(Organisation.class, orgId); + User creator = (User) baseDAO.find(User.class, creatorUserId); + + // create the lesson class - add all the users in this organisation to the lesson class + // add user as staff + List learnerList = new LinkedList(); + Iterator iter = learners.iterator(); + while (iter.hasNext()) { + try { + int id = ((Double) iter.next()).intValue(); + learnerList.add((User) baseDAO.find(User.class, id)); + } + catch (Exception e) { + MonitoringService.log.error("Error parsing learner ID from " + lessonPacket); continue; } } - //get staff user info - List staffList = new LinkedList(); - staffList.add(creator); - iter = staffs.iterator(); - while (iter.hasNext()) { - try { - int id = ((Double) iter.next()).intValue(); - staffList.add((User)baseDAO.find(User.class,id)); - } catch (Exception e) { - log.error("Error parsing staff ID from " + lessonPacket); + //get staff user info + List staffList = new LinkedList(); + staffList.add(creator); + iter = staffs.iterator(); + while (iter.hasNext()) { + try { + int id = ((Double) iter.next()).intValue(); + staffList.add((User) baseDAO.find(User.class, id)); + } + catch (Exception e) { + MonitoringService.log.error("Error parsing staff ID from " + lessonPacket); continue; } } - - //Create Lesson class - createLessonClassForLesson(lessonId, - organisation, - learnerGroupName, - learnerList, - staffGroupName, - staffList, - creatorUserId); - - flashMessage = new FlashMessage("createLesson",Boolean.TRUE); - } catch (Exception e) { - log.error("Exception occured trying to create a lesson class ",e); - flashMessage = new FlashMessage("createLesson", - e.getMessage(), - FlashMessage.ERROR); + + //Create Lesson class + createLessonClassForLesson(lessonId, organisation, learnerGroupName, learnerList, staffGroupName, staffList, + creatorUserId); + + flashMessage = new FlashMessage("createLesson", Boolean.TRUE); } - + catch (Exception e) { + MonitoringService.log.error("Exception occured trying to create a lesson class ", e); + flashMessage = new FlashMessage("createLesson", e.getMessage(), FlashMessage.ERROR); + } + String message = "Failed on creating flash message:" + flashMessage; try { message = flashMessage.serializeMessage(); - } catch (IOException e) { - log.error(message); } - - return message; - } - - /** - *

    Pre-condition: This method must be called under the condition of the - * the new lesson exists (without lesson class).

    - *

    A lesson class record should be inserted and organization should be - * setup after execution of this service.

    - * @param staffGroupName - * @param learnerGroupName - * - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#createLessonClassForLesson(long, org.lamsfoundation.lams.usermanagement.Organisation, java.util.List, java.util.List, java.util.Integer) - */ - public Lesson createLessonClassForLesson(long lessonId, - Organisation organisation, - String learnerGroupName, List organizationUsers, - String staffGroupName, List staffs, Integer userId) - { - Lesson newLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( newLesson == null) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to create class for lesson."); - } - checkOwnerOrStaffMember(userId, newLesson, "create lesson class"); + catch (IOException e) { + MonitoringService.log.error(message); + } - // if lesson isn't started, can add and remove users, so its just easier to recreate the lesson class - if ( ! newLesson.isLessonStarted() ) { - - if ( newLesson == null) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to create class for lesson."); - } - - LessonClass oldLessonClass = newLesson.getLessonClass(); - - LessonClass newLessonClass = this.createLessonClass(organisation, - learnerGroupName, - organizationUsers, - staffGroupName, - staffs, - newLesson); - newLessonClass.setLesson(newLesson); - newLesson.setLessonClass(newLessonClass); - newLesson.setOrganisation(organisation); - - lessonDAO.updateLesson(newLesson); - - if ( oldLessonClass != null ) { - lessonClassDAO.deleteLessonClass(oldLessonClass); - } + return message; + } - } else { // if started, can only add users - lessonService.addLearners(newLesson, organizationUsers); - lessonService.addStaffMembers(newLesson, staffs); - } - - return newLesson; - } - /** - * Start lesson on schedule. - * @param lessonId - * @param startDate - * @param userID: checks that this user is a staff member for this lesson - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#startLessonOnSchedule(long , Date, User) - */ - public void startLessonOnSchedule(long lessonId, Date startDate, Integer userId){ + /** + *

    Pre-condition: This method must be called under the condition of the + * the new lesson exists (without lesson class).

    + *

    A lesson class record should be inserted and organization should be + * setup after execution of this service.

    + * @param staffGroupName + * @param learnerGroupName + * + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#createLessonClassForLesson(long, org.lamsfoundation.lams.usermanagement.Organisation, java.util.List, java.util.List, java.util.Integer) + */ + public Lesson createLessonClassForLesson(long lessonId, Organisation organisation, String learnerGroupName, + List organizationUsers, String staffGroupName, List staffs, Integer userId) { + Lesson newLesson = lessonDAO.getLesson(new Long(lessonId)); + if (newLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to create class for lesson."); + } + checkOwnerOrStaffMember(userId, newLesson, "create lesson class"); - //we get the lesson just created - Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( requestedLesson == null) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to start lesson."); - } - checkOwnerOrStaffMember(userId, requestedLesson, "start lesson on schedule"); - - if ( requestedLesson.isLessonStarted() ) { - // can't schedule it as it is already started. If the UI is correct, this should never happen. - log.error("Lesson for id="+lessonId+" has been started. Unable to schedule lesson start."); - return; - } - - if ( requestedLesson.getScheduleStartDate() != null) { - // can't reschedule! - log.error("Lesson for id="+lessonId+" is already scheduled and cannot be rescheduled."); - return; - } + // if lesson isn't started, can add and remove users, so its just easier to recreate the lesson class + if (!newLesson.isLessonStarted()) { - JobDetail startLessonJob = getStartScheduleLessonJob(); - //setup the message for scheduling job - startLessonJob.setName("startLessonOnSchedule:" + lessonId); - - startLessonJob.setDescription(requestedLesson.getLessonName()+":" - + (requestedLesson.getUser() == null?"":requestedLesson.getUser().getFullName())); - startLessonJob.getJobDataMap().put(MonitoringConstants.KEY_LESSON_ID,new Long(lessonId)); - startLessonJob.getJobDataMap().put(MonitoringConstants.KEY_USER_ID,new Integer(userId)); + if (newLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + + " is missing. Unable to create class for lesson."); + } - //create customized triggers - Trigger startLessonTrigger = new SimpleTrigger("startLessonOnScheduleTrigger:"+ lessonId, - Scheduler.DEFAULT_GROUP, - startDate); - //start the scheduling job - try - { - requestedLesson.setScheduleStartDate(startDate); - scheduler.scheduleJob(startLessonJob, startLessonTrigger); - setLessonState(requestedLesson,Lesson.NOT_STARTED_STATE); - } - catch (SchedulerException e) - { - throw new MonitoringServiceException("Error occurred at " + - "[startLessonOnSchedule]- fail to start scheduling",e); - } - - if(log.isDebugEnabled()) - log.debug("Start lesson ["+lessonId+"] on schedule is configured"); - } + LessonClass oldLessonClass = newLesson.getLessonClass(); + LessonClass newLessonClass = this.createLessonClass(organisation, learnerGroupName, organizationUsers, + staffGroupName, staffs, newLesson); + newLessonClass.setLesson(newLesson); + newLesson.setLessonClass(newLessonClass); + newLesson.setOrganisation(organisation); - /** - * Finish lesson on schedule. - * @param lessonId - * @param endDate - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#finishLessonOnSchedule(long , Date , User) - */ + lessonDAO.updateLesson(newLesson); + + if (oldLessonClass != null) { + lessonClassDAO.deleteLessonClass(oldLessonClass); + } + + } + else { // if started, can only add users + lessonService.addLearners(newLesson, organizationUsers); + lessonService.addStaffMembers(newLesson, staffs); + } + + return newLesson; + } + + /** + * Start lesson on schedule. + * @param lessonId + * @param startDate + * @param userID: checks that this user is a staff member for this lesson + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#startLessonOnSchedule(long , Date, User) + */ + public void startLessonOnSchedule(long lessonId, Date startDate, Integer userId) { + + //we get the lesson just created + Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); + if (requestedLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to start lesson."); + } + checkOwnerOrStaffMember(userId, requestedLesson, "start lesson on schedule"); + + if (requestedLesson.isLessonStarted()) { + // can't schedule it as it is already started. If the UI is correct, this should never happen. + MonitoringService.log.error("Lesson for id=" + lessonId + " has been started. Unable to schedule lesson start."); + return; + } + + if (requestedLesson.getScheduleStartDate() != null) { + // can't reschedule! + MonitoringService.log.error("Lesson for id=" + lessonId + " is already scheduled and cannot be rescheduled."); + return; + } + + JobDetail startLessonJob = getStartScheduleLessonJob(); + //setup the message for scheduling job + startLessonJob.setName("startLessonOnSchedule:" + lessonId); + + startLessonJob.setDescription(requestedLesson.getLessonName() + ":" + + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser().getFullName())); + startLessonJob.getJobDataMap().put(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)); + startLessonJob.getJobDataMap().put(MonitoringConstants.KEY_USER_ID, new Integer(userId)); + + //create customized triggers + Trigger startLessonTrigger = new SimpleTrigger("startLessonOnScheduleTrigger:" + lessonId, Scheduler.DEFAULT_GROUP, + startDate); + //start the scheduling job + try { + requestedLesson.setScheduleStartDate(startDate); + scheduler.scheduleJob(startLessonJob, startLessonTrigger); + setLessonState(requestedLesson, Lesson.NOT_STARTED_STATE); + } + catch (SchedulerException e) { + throw new MonitoringServiceException("Error occurred at " + "[startLessonOnSchedule]- fail to start scheduling", e); + } + + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Start lesson [" + lessonId + "] on schedule is configured"); + } + } + + /** + * Finish lesson on schedule. + * @param lessonId + * @param endDate + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#finishLessonOnSchedule(long , Date , User) + */ public void finishLessonOnSchedule(long lessonId, Date endDate, Integer userId) { - //we get the lesson want to finish - Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( requestedLesson == null) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to start lesson."); - } - checkOwnerOrStaffMember(userId, requestedLesson, "finish lesson on schedule"); + //we get the lesson want to finish + Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); + if (requestedLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to start lesson."); + } + checkOwnerOrStaffMember(userId, requestedLesson, "finish lesson on schedule"); - JobDetail finishLessonJob = getFinishScheduleLessonJob(); - //setup the message for scheduling job - finishLessonJob.setName("finishLessonOnSchedule:"+lessonId); - finishLessonJob.setDescription(requestedLesson.getLessonName()+":" - + (requestedLesson.getUser() == null?"":requestedLesson.getUser().getFullName())); - finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_LESSON_ID,new Long(lessonId)); - finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_USER_ID,new Integer(userId)); - //create customized triggers - Trigger finishLessonTrigger = new SimpleTrigger("finishLessonOnScheduleTrigger:"+lessonId, - Scheduler.DEFAULT_GROUP, - endDate); - //start the scheduling job - try - { - requestedLesson.setScheduleEndDate(endDate); - scheduler.scheduleJob(finishLessonJob, finishLessonTrigger); - } - catch (SchedulerException e) - { - throw new MonitoringServiceException("Error occurred at " + - "[finishLessonOnSchedule]- fail to start scheduling",e); - } - - if(log.isDebugEnabled()) - log.debug("Finish lesson ["+lessonId+"] on schedule is configured"); + JobDetail finishLessonJob = getFinishScheduleLessonJob(); + //setup the message for scheduling job + finishLessonJob.setName("finishLessonOnSchedule:" + lessonId); + finishLessonJob.setDescription(requestedLesson.getLessonName() + ":" + + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser().getFullName())); + finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)); + finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_USER_ID, new Integer(userId)); + //create customized triggers + Trigger finishLessonTrigger = new SimpleTrigger("finishLessonOnScheduleTrigger:" + lessonId, Scheduler.DEFAULT_GROUP, + endDate); + //start the scheduling job + try { + requestedLesson.setScheduleEndDate(endDate); + scheduler.scheduleJob(finishLessonJob, finishLessonTrigger); + } + catch (SchedulerException e) { + throw new MonitoringServiceException("Error occurred at " + "[finishLessonOnSchedule]- fail to start scheduling", e); + } + + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Finish lesson [" + lessonId + "] on schedule is configured"); + } } /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#startlesson(long) - */ - public void startLesson(long lessonId, Integer userId) - { -// System.out.println(messageService.getMessage("NO.SUCH.LESSON",new Object[]{new Long(lessonId)})); -// System.out.println(messageService.getMessage("INVALID.ACTIVITYID.TYPE", new Object[]{ "activityID"})); -// System.out.println(messageService.getMessage("INVALID.ACTIVITYID.LESSONID",new Object[]{ "activityID","lessonID"})); - if(log.isDebugEnabled()) - log.debug("=============Starting Lesson "+lessonId+"=============="); + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#startlesson(long) + */ + public void startLesson(long lessonId, Integer userId) { + // System.out.println(messageService.getMessage("NO.SUCH.LESSON",new Object[]{new Long(lessonId)})); + // System.out.println(messageService.getMessage("INVALID.ACTIVITYID.TYPE", new Object[]{ "activityID"})); + // System.out.println(messageService.getMessage("INVALID.ACTIVITYID.LESSONID",new Object[]{ "activityID","lessonID"})); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("=============Starting Lesson " + lessonId + "=============="); + } - //we get the lesson just created - Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( requestedLesson == null) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to start lesson."); - } + //we get the lesson just created + Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); + if (requestedLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to start lesson."); + } - if ( requestedLesson.isLessonStarted() ) { - log.warn("Lesson for id="+lessonId+" has been started. No need to start the lesson. The lesson was probably scheduled, and then the staff used \"Start now\". This message would have then been created by the schedule start"); - return; - } + if (requestedLesson.isLessonStarted()) { + MonitoringService.log + .warn("Lesson for id=" + + lessonId + + " has been started. No need to start the lesson. The lesson was probably scheduled, and then the staff used \"Start now\". This message would have then been created by the schedule start"); + return; + } - checkOwnerOrStaffMember(userId, requestedLesson, "create lesson class"); + checkOwnerOrStaffMember(userId, requestedLesson, "create lesson class"); - Date lessonStartTime = new Date(); - //initialize tool sessions if necessary - LearningDesign design = requestedLesson.getLearningDesign(); - boolean designModified = false; - Set activities = design.getActivities(); - for (Iterator i = activities.iterator(); i.hasNext();) - { - Activity activity = (Activity) i.next(); - // if it is a non-grouped and non-branched Tool Activity, create the tool sessions now - if ( activity.isToolActivity() ) { - ToolActivity toolActivity = (ToolActivity) activityDAO.getActivityByActivityId(activity.getActivityId()); + Date lessonStartTime = new Date(); + //initialize tool sessions if necessary + LearningDesign design = requestedLesson.getLearningDesign(); + boolean designModified = false; + Set activities = design.getActivities(); + for (Iterator i = activities.iterator(); i.hasNext();) { + Activity activity = (Activity) i.next(); + // if it is a non-grouped and non-branched Tool Activity, create the tool sessions now + if (activity.isToolActivity()) { + ToolActivity toolActivity = (ToolActivity) activityDAO.getActivityByActivityId(activity.getActivityId()); initToolSessionIfSuitable(toolActivity, requestedLesson); - } + } - Integer newMaxId = startSystemActivity(activity, design.getMaxID(), lessonStartTime, requestedLesson.getLessonName()); - if ( newMaxId != null ) { + Integer newMaxId = startSystemActivity(activity, design.getMaxID(), lessonStartTime, requestedLesson.getLessonName()); + if (newMaxId != null) { design.setMaxID(newMaxId); designModified = true; - } - + } + activity.setInitialised(Boolean.TRUE); - activityDAO.update(activity); - - } - - if ( designModified ) - learningDesignDAO.update(design); - - //update lesson status - requestedLesson.setLessonStateId(Lesson.STARTED_STATE); - requestedLesson.setStartDateTime(lessonStartTime); - lessonDAO.updateLesson(requestedLesson); - - if(log.isDebugEnabled()) - log.debug("=============Lesson "+lessonId+" started==============="); - } - - /** Do any normal initialisation needed for gates and branching. Done both when a lesson is started, or for new activities - * added during a Live Edit. Returns a new MaxID for the design if needed. If MaxID is returned, update the design with this - * new value and save the whole design (as initialiseSystemActivities has changed the design). - */ - public Integer startSystemActivity( Activity activity, Integer currentMaxId, Date lessonStartTime, String lessonName ) { - Integer newMaxId = null; - - //if it is schedule gate, we need to initialize the sheduler for it. - if(activity.getActivityTypeId().intValue() == Activity.SCHEDULE_GATE_ACTIVITY_TYPE) { - ScheduleGateActivity gateActivity = (ScheduleGateActivity) activityDAO.getActivityByActivityId(activity.getActivityId()); - activity = runGateScheduler(gateActivity,lessonStartTime,lessonName); - } - if ( activity.isBranchingActivity() && activity.getGrouping() == null) { + activityDAO.update(activity); + + } + + if (designModified) { + learningDesignDAO.update(design); + } + + //update lesson status + requestedLesson.setLessonStateId(Lesson.STARTED_STATE); + requestedLesson.setStartDateTime(lessonStartTime); + lessonDAO.updateLesson(requestedLesson); + + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("=============Lesson " + lessonId + " started==============="); + } + } + + /** Do any normal initialisation needed for gates and branching. Done both when a lesson is started, or for new activities + * added during a Live Edit. Returns a new MaxID for the design if needed. If MaxID is returned, update the design with this + * new value and save the whole design (as initialiseSystemActivities has changed the design). + */ + public Integer startSystemActivity(Activity activity, Integer currentMaxId, Date lessonStartTime, String lessonName) { + Integer newMaxId = null; + + //if it is schedule gate, we need to initialize the sheduler for it. + if (activity.getActivityTypeId().intValue() == Activity.SCHEDULE_GATE_ACTIVITY_TYPE) { + ScheduleGateActivity gateActivity = (ScheduleGateActivity) activityDAO.getActivityByActivityId(activity + .getActivityId()); + activity = runGateScheduler(gateActivity, lessonStartTime, lessonName); + } + if (activity.isBranchingActivity() && activity.getGrouping() == null) { // all branching activities must have a grouping, as the learner will be allocated to a group linked to a sequence (branch) Grouping grouping = new ChosenGrouping(null, null, null); grouping.setGroupingUIID(currentMaxId); @@ -769,1582 +751,1657 @@ activity.setGroupingUIID(currentMaxId); activity.setApplyGrouping(Boolean.TRUE); groupingDAO.insert(grouping); - + activity.setGrouping(grouping); - if ( log.isDebugEnabled() ) { - log.debug( "startLesson: Created chosen grouping "+grouping+" for branching activity "+activity); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("startLesson: Created chosen grouping " + grouping + " for branching activity " + + activity); } - newMaxId = new Integer(currentMaxId.intValue()+1); + newMaxId = new Integer(currentMaxId.intValue() + 1); } return newMaxId; - } - - /** - *

    Runs the system scheduler to start the scheduling for opening gate and - * closing gate. It involves a couple of steps to start the scheduler:

    - *
  • 1. Initialize the resource needed by scheduling job by setting - * them into the job data map. - *
  • - *
  • 2. Create customized triggers for the scheduling.
  • - *
  • 3. start the scheduling job
  • - * - * @param scheduleGate the gate that needs to be scheduled. - * @param schedulingStartTime the time on which the gate open should be based if an offset is used. For starting - * a lesson, this is the lessonStartTime. For live edit, it is now. - * @param lessonName the name lesson incorporating this gate - used for the description of the Quartz job. Optional. - * @returns An updated gate, that should be saved by the calling code. - */ - public ScheduleGateActivity runGateScheduler(ScheduleGateActivity scheduleGate, - Date schedulingStartTime, String lessonName) - { - - if(log.isDebugEnabled()) - log.debug("Running scheduler for gate "+scheduleGate.getActivityId()+"..."); - JobDetail openScheduleGateJob = getOpenScheduleGateJob(); - JobDetail closeScheduleGateJob = getCloseScheduleGateJob(); - //setup the message for scheduling job - openScheduleGateJob.setName("openGate:" + scheduleGate.getActivityId()); - openScheduleGateJob.setDescription(scheduleGate.getTitle()+":"+lessonName); - openScheduleGateJob.getJobDataMap().put("gateId",scheduleGate.getActivityId()); - closeScheduleGateJob.setName("closeGate:" + scheduleGate.getActivityId()); - closeScheduleGateJob.getJobDataMap().put("gateId",scheduleGate.getActivityId()); - closeScheduleGateJob.setDescription(scheduleGate.getTitle()+":"+lessonName); + } - - //create customized triggers - Trigger openGateTrigger = new SimpleTrigger("openGateTrigger:" + scheduleGate.getActivityId(), - Scheduler.DEFAULT_GROUP, - scheduleGate.getLessonGateOpenTime(schedulingStartTime)); - - - Trigger closeGateTrigger = new SimpleTrigger("closeGateTrigger:" + scheduleGate.getActivityId(), - Scheduler.DEFAULT_GROUP, - scheduleGate.getLessonGateCloseTime(schedulingStartTime)); + /** + *

    Runs the system scheduler to start the scheduling for opening gate and + * closing gate. It involves a couple of steps to start the scheduler:

    + *
  • 1. Initialize the resource needed by scheduling job by setting + * them into the job data map. + *
  • + *
  • 2. Create customized triggers for the scheduling.
  • + *
  • 3. start the scheduling job
  • + * + * @param scheduleGate the gate that needs to be scheduled. + * @param schedulingStartTime the time on which the gate open should be based if an offset is used. For starting + * a lesson, this is the lessonStartTime. For live edit, it is now. + * @param lessonName the name lesson incorporating this gate - used for the description of the Quartz job. Optional. + * @returns An updated gate, that should be saved by the calling code. + */ + public ScheduleGateActivity runGateScheduler(ScheduleGateActivity scheduleGate, Date schedulingStartTime, String lessonName) { - //start the scheduling job - try - { - if((scheduleGate.getGateStartTimeOffset() == null && scheduleGate.getGateEndTimeOffset() == null) || - (scheduleGate.getGateStartTimeOffset() != null && scheduleGate.getGateEndTimeOffset() == null)) - scheduler.scheduleJob(openScheduleGateJob, openGateTrigger); - else if(openGateTrigger.getStartTime().before(closeGateTrigger.getStartTime())) { - scheduler.scheduleJob(openScheduleGateJob, openGateTrigger); - scheduler.scheduleJob(closeScheduleGateJob, closeGateTrigger); - } + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Running scheduler for gate " + scheduleGate.getActivityId() + "..."); + } + JobDetail openScheduleGateJob = getOpenScheduleGateJob(); + JobDetail closeScheduleGateJob = getCloseScheduleGateJob(); + //setup the message for scheduling job + openScheduleGateJob.setName("openGate:" + scheduleGate.getActivityId()); + openScheduleGateJob.setDescription(scheduleGate.getTitle() + ":" + lessonName); + openScheduleGateJob.getJobDataMap().put("gateId", scheduleGate.getActivityId()); + closeScheduleGateJob.setName("closeGate:" + scheduleGate.getActivityId()); + closeScheduleGateJob.getJobDataMap().put("gateId", scheduleGate.getActivityId()); + closeScheduleGateJob.setDescription(scheduleGate.getTitle() + ":" + lessonName); - } - catch (SchedulerException e) - { - throw new MonitoringServiceException("Error occurred at " + - "[runGateScheduler]- fail to start scheduling",e); - } - - if(log.isDebugEnabled()) - log.debug("Scheduler for Gate "+scheduleGate.getActivityId()+" started..."); - - return scheduleGate; - } + //create customized triggers + Trigger openGateTrigger = new SimpleTrigger("openGateTrigger:" + scheduleGate.getActivityId(), Scheduler.DEFAULT_GROUP, + scheduleGate.getLessonGateOpenTime(schedulingStartTime)); - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#finishLesson(long) - */ + Trigger closeGateTrigger = new SimpleTrigger("closeGateTrigger:" + scheduleGate.getActivityId(), Scheduler.DEFAULT_GROUP, + scheduleGate.getLessonGateCloseTime(schedulingStartTime)); + + //start the scheduling job + try { + if (scheduleGate.getGateStartTimeOffset() == null && scheduleGate.getGateEndTimeOffset() == null + || scheduleGate.getGateStartTimeOffset() != null && scheduleGate.getGateEndTimeOffset() == null) { + scheduler.scheduleJob(openScheduleGateJob, openGateTrigger); + } + else if (openGateTrigger.getStartTime().before(closeGateTrigger.getStartTime())) { + scheduler.scheduleJob(openScheduleGateJob, openGateTrigger); + scheduler.scheduleJob(closeScheduleGateJob, closeGateTrigger); + } + + } + catch (SchedulerException e) { + throw new MonitoringServiceException("Error occurred at " + "[runGateScheduler]- fail to start scheduling", e); + } + + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Scheduler for Gate " + scheduleGate.getActivityId() + " started..."); + } + + return scheduleGate; + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#finishLesson(long) + */ public void finishLesson(long lessonId, Integer userId) { - Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( requestedLesson == null ) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to set lesson to finished"); - } - checkOwnerOrStaffMember(userId, requestedLesson, "finish lesson"); - setLessonState(requestedLesson,Lesson.FINISHED_STATE); + Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); + if (requestedLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to set lesson to finished"); + } + checkOwnerOrStaffMember(userId, requestedLesson, "finish lesson"); + setLessonState(requestedLesson, Lesson.FINISHED_STATE); } - /** - * Archive the specified the lesson. When archived, the data is retained - * but the learners cannot access the details. - * @param lessonId the specified the lesson id. - */ - public void archiveLesson(long lessonId, Integer userId) { - Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( requestedLesson == null ) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to set lesson to archived"); - } - checkOwnerOrStaffMember(userId, requestedLesson, "archive lesson"); - if ( ! Lesson.ARCHIVED_STATE.equals(requestedLesson.getLessonStateId()) && - !Lesson.REMOVED_STATE.equals(requestedLesson.getLessonStateId()) ) { - setLessonState(requestedLesson,Lesson.ARCHIVED_STATE); - } - } - /** - * Unarchive the specified the lesson. Reverts back to its previous state. - * @param lessonId the specified the lesson id. - */ - public void unarchiveLesson(long lessonId, Integer userId) { - Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( requestedLesson == null ) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to set lesson to archived"); - } - checkOwnerOrStaffMember(userId, requestedLesson, "unarchive lesson"); + + /** + * Archive the specified the lesson. When archived, the data is retained + * but the learners cannot access the details. + * @param lessonId the specified the lesson id. + */ + public void archiveLesson(long lessonId, Integer userId) { + Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); + if (requestedLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to set lesson to archived"); + } + checkOwnerOrStaffMember(userId, requestedLesson, "archive lesson"); + if (!Lesson.ARCHIVED_STATE.equals(requestedLesson.getLessonStateId()) + && !Lesson.REMOVED_STATE.equals(requestedLesson.getLessonStateId())) { + setLessonState(requestedLesson, Lesson.ARCHIVED_STATE); + } + } + + /** + * Unarchive the specified the lesson. Reverts back to its previous state. + * @param lessonId the specified the lesson id. + */ + public void unarchiveLesson(long lessonId, Integer userId) { + Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); + if (requestedLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to set lesson to archived"); + } + checkOwnerOrStaffMember(userId, requestedLesson, "unarchive lesson"); revertLessonState(requestedLesson); - } - /** - * - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#suspendLesson(long) - */ + } + + /** + * + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#suspendLesson(long) + */ public void suspendLesson(long lessonId, Integer userId) { Lesson lesson = lessonDAO.getLesson(new Long(lessonId)); - checkOwnerOrStaffMember(userId, lesson, "suspend lesson"); - if ( lesson == null ) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to suspend lesson."); - } - if ( ! Lesson.SUSPENDED_STATE.equals(lesson.getLessonStateId()) && - !Lesson.REMOVED_STATE.equals(lesson.getLessonStateId()) ) { - setLessonState(lesson,Lesson.SUSPENDED_STATE); - } + checkOwnerOrStaffMember(userId, lesson, "suspend lesson"); + if (lesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to suspend lesson."); + } + if (!Lesson.SUSPENDED_STATE.equals(lesson.getLessonStateId()) && !Lesson.REMOVED_STATE.equals(lesson.getLessonStateId())) { + setLessonState(lesson, Lesson.SUSPENDED_STATE); + } } + /** * * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#unsuspendLesson(long) */ public void unsuspendLesson(long lessonId, Integer userId) { Lesson lesson = lessonDAO.getLesson(new Long(lessonId)); - checkOwnerOrStaffMember(userId, lesson, "unsuspend lesson"); + checkOwnerOrStaffMember(userId, lesson, "unsuspend lesson"); Integer state = lesson.getLessonStateId(); //only suspend started lesson - if(!Lesson.SUSPENDED_STATE.equals(state)){ + if (!Lesson.SUSPENDED_STATE.equals(state)) { throw new MonitoringServiceException("Lesson is not suspended lesson. It can not be unsuspended."); } - if ( lesson == null ) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to suspend lesson."); + if (lesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to suspend lesson."); } revertLessonState(lesson); } - /** - * Set a lesson to a particular state. Copies the current state to the previous lesson state. - * @param requestedLesson - * @param status - */ - private void setLessonState(Lesson requestedLesson,Integer status) { - - requestedLesson.setPreviousLessonStateId(requestedLesson.getLessonStateId()); - requestedLesson.setLessonStateId(status); - lessonDAO.updateLesson(requestedLesson); - } + /** + * Set a lesson to a particular state. Copies the current state to the previous lesson state. + * @param requestedLesson + * @param status + */ + private void setLessonState(Lesson requestedLesson, Integer status) { - /** - * Sets a lesson back to its previous state. Used when we "unsuspend" or "unarchive" - * @param requestedLesson - * @param status - */ - private void revertLessonState(Lesson requestedLesson) { - - Integer currentStatus = requestedLesson.getLessonStateId(); - if(requestedLesson.getPreviousLessonStateId() != null) { - if(requestedLesson.getPreviousLessonStateId().equals(Lesson.NOT_STARTED_STATE) && requestedLesson.getScheduleStartDate().before(new Date())) - requestedLesson.setLessonStateId(Lesson.STARTED_STATE); - else - requestedLesson.setLessonStateId(requestedLesson.getPreviousLessonStateId()); - requestedLesson.setPreviousLessonStateId(null); - } else { - if(requestedLesson.getStartDateTime() != null && requestedLesson.getScheduleStartDate() != null) - requestedLesson.setLessonStateId(Lesson.STARTED_STATE); - else if(requestedLesson.getScheduleStartDate() != null) - if(requestedLesson.getScheduleStartDate().after(new Date())) - requestedLesson.setLessonStateId(Lesson.NOT_STARTED_STATE); - else - requestedLesson.setLessonStateId(Lesson.STARTED_STATE); - else if(requestedLesson.getStartDateTime() != null) - requestedLesson.setLessonStateId(Lesson.STARTED_STATE); - else - requestedLesson.setLessonStateId(Lesson.CREATED); - - requestedLesson.setPreviousLessonStateId(currentStatus); - } - lessonDAO.updateLesson(requestedLesson); - } - - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#removeLesson(long) - */ - public void removeLesson(long lessonId, Integer userId) { - Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( requestedLesson == null) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to remove lesson."); - } - checkOwnerOrStaffMember(userId, requestedLesson, "remove lesson"); - - // TODO give sysadmin rights to do this too! - - setLessonState(requestedLesson,Lesson.REMOVED_STATE); - } - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#setLearnerPortfolioAvailable(long, java.lang.Integer, boolean) - */ + requestedLesson.setPreviousLessonStateId(requestedLesson.getLessonStateId()); + requestedLesson.setLessonStateId(status); + lessonDAO.updateLesson(requestedLesson); + } + + /** + * Sets a lesson back to its previous state. Used when we "unsuspend" or "unarchive" + * @param requestedLesson + * @param status + */ + private void revertLessonState(Lesson requestedLesson) { + + Integer currentStatus = requestedLesson.getLessonStateId(); + if (requestedLesson.getPreviousLessonStateId() != null) { + if (requestedLesson.getPreviousLessonStateId().equals(Lesson.NOT_STARTED_STATE) + && requestedLesson.getScheduleStartDate().before(new Date())) { + requestedLesson.setLessonStateId(Lesson.STARTED_STATE); + } + else { + requestedLesson.setLessonStateId(requestedLesson.getPreviousLessonStateId()); + } + requestedLesson.setPreviousLessonStateId(null); + } + else { + if (requestedLesson.getStartDateTime() != null && requestedLesson.getScheduleStartDate() != null) { + requestedLesson.setLessonStateId(Lesson.STARTED_STATE); + } + else if (requestedLesson.getScheduleStartDate() != null) { + if (requestedLesson.getScheduleStartDate().after(new Date())) { + requestedLesson.setLessonStateId(Lesson.NOT_STARTED_STATE); + } + else { + requestedLesson.setLessonStateId(Lesson.STARTED_STATE); + } + } + else if (requestedLesson.getStartDateTime() != null) { + requestedLesson.setLessonStateId(Lesson.STARTED_STATE); + } + else { + requestedLesson.setLessonStateId(Lesson.CREATED); + } + + requestedLesson.setPreviousLessonStateId(currentStatus); + } + lessonDAO.updateLesson(requestedLesson); + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#removeLesson(long) + */ + public void removeLesson(long lessonId, Integer userId) { + Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); + if (requestedLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + " is missing. Unable to remove lesson."); + } + checkOwnerOrStaffMember(userId, requestedLesson, "remove lesson"); + + // TODO give sysadmin rights to do this too! + + setLessonState(requestedLesson, Lesson.REMOVED_STATE); + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#setLearnerPortfolioAvailable(long, java.lang.Integer, boolean) + */ public Boolean setLearnerPortfolioAvailable(long lessonId, Integer userId, Boolean learnerExportAvailable) { - Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); - if ( requestedLesson == null ) { - throw new MonitoringServiceException("Lesson for id="+lessonId+" is missing. Unable to set learner portfolio available to "+learnerExportAvailable); - } - checkOwnerOrStaffMember(userId, requestedLesson, "set learner portfolio available"); + Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); + if (requestedLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + + " is missing. Unable to set learner portfolio available to " + learnerExportAvailable); + } + checkOwnerOrStaffMember(userId, requestedLesson, "set learner portfolio available"); requestedLesson.setLearnerExportAvailable(learnerExportAvailable != null ? learnerExportAvailable : Boolean.FALSE); - auditAction(AUDIT_LEARNER_PORTFOLIO_SET, new Object[]{requestedLesson.getLessonName(), requestedLesson.getLearnerExportAvailable()}); + auditAction(MonitoringService.AUDIT_LEARNER_PORTFOLIO_SET, new Object[] { requestedLesson.getLessonName(), + requestedLesson.getLearnerExportAvailable() }); lessonDAO.updateLesson(requestedLesson); return requestedLesson.getLearnerExportAvailable(); } - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#openGate(org.lamsfoundation.lams.learningdesign.GateActivity) - */ - public GateActivity openGate(Long gateId) - { - GateActivity gate = (GateActivity)activityDAO.getActivityByActivityId(gateId); - if ( gate != null ) { - gate.setGateOpen(new Boolean(true)); - - // we un-schedule the gate from the scheduler if it's of a scheduled gate (LDEV-1271) - if (gate.isScheduleGate()) { - - try { + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#openGate(org.lamsfoundation.lams.learningdesign.GateActivity) + */ + public GateActivity openGate(Long gateId) { + GateActivity gate = (GateActivity) activityDAO.getActivityByActivityId(gateId); + if (gate != null) { + gate.setGateOpen(new Boolean(true)); + + // we un-schedule the gate from the scheduler if it's of a scheduled gate (LDEV-1271) + if (gate.isScheduleGate()) { + + try { scheduler.unscheduleJob("openGateTrigger:" + gate.getActivityId(), Scheduler.DEFAULT_GROUP); - } catch (SchedulerException e) { - log.error("Error unscheduling trigger for gate activity id:"+ gate.getActivityId(), e); - throw new MonitoringServiceException("Error unscheduling trigger for gate activity id:"+ gate.getActivityId(), e); - } - - } - - activityDAO.update(gate); - } - return gate; - } + catch (SchedulerException e) { + MonitoringService.log.error("Error unscheduling trigger for gate activity id:" + gate.getActivityId(), e); + throw new MonitoringServiceException("Error unscheduling trigger for gate activity id:" + + gate.getActivityId(), e); - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#closeGate(org.lamsfoundation.lams.learningdesign.GateActivity) - */ - public GateActivity closeGate(Long gateId) - { - GateActivity gate = (GateActivity)activityDAO.getActivityByActivityId(gateId); - gate.setGateOpen(new Boolean(false)); - activityDAO.update(gate); - return gate; - } - /** - * @throws LamsToolServiceException - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#forceCompleteLessonByUser(Integer,long,long) - */ - public String forceCompleteLessonByUser(Integer learnerId, Integer requesterId, long lessonId,Long activityId) - { - Lesson lesson = lessonDAO.getLesson(new Long(lessonId)); - checkOwnerOrStaffMember(requesterId, lesson, "force complete"); - - User learner = (User)baseDAO.find(User.class,learnerId); - - LearnerProgress learnerProgress = learnerService.getProgress(learnerId, lessonId); - Activity stopActivity = null; - - if ( activityId != null ) { - stopActivity = activityDAO.getActivityByActivityId(activityId); - if ( stopActivity == null ) { - throw new MonitoringServiceException("Activity missing. Activity id"+activityId); - } - - // check if activity is already complete - if ( learnerProgress != null && learnerProgress.getCompletedActivities().contains(stopActivity) ) { - return messageService.getMessage(FORCE_COMPLETE_STOP_MESSAGE_ACTIVITY_DONE,new Object[]{stopActivity.getTitle()}); - } - } - - Activity currentActivity = learnerProgress.getCurrentActivity(); + } + + } + + activityDAO.update(gate); + } + return gate; + } + + public GateActivity openGateForSingleUser(Long gateId, Integer userId) { + GateActivity gate = (GateActivity) activityDAO.getActivityByActivityId(gateId); + if (gate != null && gate.isPermissionGate() && userId != null && userId >= 0) { + User user = (User) baseDAO.find(User.class, userId); + gate.addLeaner(user, true); + activityDAO.update(gate); + } + return gate; + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#closeGate(org.lamsfoundation.lams.learningdesign.GateActivity) + */ + public GateActivity closeGate(Long gateId) { + GateActivity gate = (GateActivity) activityDAO.getActivityByActivityId(gateId); + gate.setGateOpen(new Boolean(false)); + activityDAO.update(gate); + return gate; + } + + /** + * @throws LamsToolServiceException + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#forceCompleteLessonByUser(Integer,long,long) + */ + public String forceCompleteLessonByUser(Integer learnerId, Integer requesterId, long lessonId, Long activityId) { + Lesson lesson = lessonDAO.getLesson(new Long(lessonId)); + checkOwnerOrStaffMember(requesterId, lesson, "force complete"); + + User learner = (User) baseDAO.find(User.class, learnerId); + + LearnerProgress learnerProgress = learnerService.getProgress(learnerId, lessonId); + Activity stopActivity = null; + + if (activityId != null) { + stopActivity = activityDAO.getActivityByActivityId(activityId); + if (stopActivity == null) { + throw new MonitoringServiceException("Activity missing. Activity id" + activityId); + } + + // check if activity is already complete + if (learnerProgress != null && learnerProgress.getCompletedActivities().contains(stopActivity)) { + return messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_ACTIVITY_DONE, + new Object[] { stopActivity.getTitle() }); + } + } + + Activity currentActivity = learnerProgress.getCurrentActivity(); String stopReason = null; - if (currentActivity != null ) { - stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, currentActivity, stopActivity, new ArrayList()); + if (currentActivity != null) { + stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, currentActivity, stopActivity, + new ArrayList()); } - return stopReason != null ? stopReason : messageService.getMessage(FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY); - - - } - - /** - * Recursive method to step through a design and do the force complete. - * - * Special Cases: - * Gate -- LearnerService.knockGate(GateActivity gate, User knocker, List lessonLearners) - * Y - continue - * N - Stop - * Group -- getGroup -> exist? - * Y - continue - * N - PermissionGroup - Stop - * RandomGroup - create group, then complete it and continue. - * - * As we process an activity, we stick it in touchedActivityIds. Then we check this list before forwarding to an activity - this will - * stop us going into loops on the parallel activities and other complex activities that return to the parent activity after the child. - */ - private String forceCompleteActivity(User learner, Long lessonId, LearnerProgress progress, Activity activity, Activity stopActivity, ArrayList touchedActivityIds) { - - // TODO check performance - does it load the learner progress every time or do it load it from the cache. - String stopReason = null; + return stopReason != null ? stopReason : messageService + .getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY); - // activity likely to be a cglib so get the real activity - activity = activityDAO.getActivityByActivityId(activity.getActivityId()); - touchedActivityIds.add(activity.getActivityId()); - - if (activity.isGroupingActivity()){ - GroupingActivity groupActivity = (GroupingActivity) activity; - Grouping grouping = groupActivity.getCreateGrouping(); - Group myGroup = grouping.getGroupBy(learner); - if(myGroup == null || myGroup.isNull()){ - //group does not exist - if(grouping.isRandomGrouping()){ - //for random grouping, create then complete it. Continue - try { - lessonService.performGrouping(lessonId, groupActivity,learner); - } catch (LessonServiceException e) { - log.error("Force complete failed. Learner "+learner+" lessonId "+lessonId+" processing activity "+activity,e); - stopReason = messageService.getMessage(FORCE_COMPLETE_STOP_MESSAGE_GROUPING_ERROR,new Object[]{activity.getTitle()}); + } + + /** + * Recursive method to step through a design and do the force complete. + * + * Special Cases: + * Gate -- LearnerService.knockGate(GateActivity gate, User knocker, List lessonLearners) + * Y - continue + * N - Stop + * Group -- getGroup -> exist? + * Y - continue + * N - PermissionGroup - Stop + * RandomGroup - create group, then complete it and continue. + * + * As we process an activity, we stick it in touchedActivityIds. Then we check this list before forwarding to an activity - this will + * stop us going into loops on the parallel activities and other complex activities that return to the parent activity after the child. + */ + private String forceCompleteActivity(User learner, Long lessonId, LearnerProgress progress, Activity activity, + Activity stopActivity, ArrayList touchedActivityIds) { + + // TODO check performance - does it load the learner progress every time or do it load it from the cache. + String stopReason = null; + + // activity likely to be a cglib so get the real activity + activity = activityDAO.getActivityByActivityId(activity.getActivityId()); + touchedActivityIds.add(activity.getActivityId()); + + if (activity.isGroupingActivity()) { + GroupingActivity groupActivity = (GroupingActivity) activity; + Grouping grouping = groupActivity.getCreateGrouping(); + Group myGroup = grouping.getGroupBy(learner); + if (myGroup == null || myGroup.isNull()) { + //group does not exist + if (grouping.isRandomGrouping()) { + //for random grouping, create then complete it. Continue + try { + lessonService.performGrouping(lessonId, groupActivity, learner); } - learnerService.completeActivity(learner.getUserId(),activity,lessonId); - if ( log.isDebugEnabled()) { - log.debug("Grouping activity [" + activity.getActivityId() + "] is completed."); - } - }else{ - //except random grouping, stop here - stopReason = messageService.getMessage(FORCE_COMPLETE_STOP_MESSAGE_GROUPING,new Object[]{activity.getTitle()}); - } - }else{ - //if group already exist - learnerService.completeActivity(learner.getUserId(),activity,lessonId); - if ( log.isDebugEnabled()) { - log.debug("Grouping activity [" + activity.getActivityId() + "] is completed."); - } - } - - }else if ( activity.isGateActivity() ) { - GateActivity gate = (GateActivity) activity; - GateActivityDTO dto = learnerService.knockGate(gate,learner,false); - if ( dto.getGateOpen() ){ - //the gate is opened, continue to next activity to complete - learnerService.completeActivity(learner.getUserId(),activity,lessonId); - if ( log.isDebugEnabled()) { - log.debug("Gate activity [" + gate.getActivityId() + "] is completed."); - } - }else{ - //the gate is closed, stop here - stopReason = messageService.getMessage(FORCE_COMPLETE_STOP_MESSAGE_GATE,new Object[]{activity.getTitle()}); - } - - }else if(activity.isToolActivity()){ - ToolActivity toolActivity = (ToolActivity) activity; - try { - ToolSession toolSession = lamsCoreToolService.getToolSessionByActivity(learner,toolActivity); - if ( toolSession == null ) { - // grouped tool's tool session isn't created until the first user in the group reaches that - // point the tool session creation is normally triggered by LoadTooLActivityAction, so we need - // to do it here. Won't happen very often - normally another member of the group will have - // triggered the creation of the tool session. - learnerService.createToolSessionsIfNecessary(toolActivity,progress); - toolSession = lamsCoreToolService.getToolSessionByActivity(learner,toolActivity); + catch (LessonServiceException e) { + MonitoringService.log.error("Force complete failed. Learner " + learner + " lessonId " + lessonId + + " processing activity " + activity, e); + stopReason = messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_GROUPING_ERROR, + new Object[] { activity.getTitle() }); } - learnerService.completeToolSession(toolSession.getToolSessionId(),new Long(learner.getUserId().longValue())); - learnerService.completeActivity(learner.getUserId(),activity,lessonId); - if ( log.isDebugEnabled()) { - log.debug("Tool activity [" + activity.getActivityId() + "] is completed."); - } - } catch (LamsToolServiceException e) { - throw new MonitoringServiceException(e); + learnerService.completeActivity(learner.getUserId(), activity, lessonId); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Grouping activity [" + activity.getActivityId() + "] is completed."); + } } - - }else if(activity.isBranchingActivity() || activity.isOptionsActivity() ){ - // Can force complete over a branching activity, but none of the branches are marked as done. - // Ditto the two types of optional activities. - // Then if the user goes back to them, they will operate normally. - learnerService.completeActivity(learner.getUserId(), activity, lessonId); - - }else if(activity.isComplexActivity()){ - // expect it to be a parallel activity - ComplexActivity complexActivity = (ComplexActivity) activity; - Set allActivities = complexActivity.getActivities(); - Iterator iter = allActivities.iterator(); - while(stopReason==null && iter.hasNext()){ - Activity act = (Activity) iter.next(); - stopReason = forceCompleteActivity(learner, lessonId, progress, act, stopActivity, touchedActivityIds); - } - log.debug("Complex activity [" + activity.getActivityId() + "] is completed."); - } - - // complete to the given activity ID, then stop. To be sure, the given activity is forced to complete as well. - // if we had stopped due to a subactivity, the stop reason will already be set. - if ( stopReason == null ) { - LearnerProgress learnerProgress = learnerService.getProgress(learner.getUserId(), lessonId); - if ( learnerProgress.getCompletedActivities().contains(stopActivity) ) { - // we have reached the stop activity. It may have been the activity we just processed - // or it may have been a parent activity (e.g. an optional activity) and completing its last - // child has triggered the parent activity to be completed. Hence we have to check the actual - // completed activities list rather than just checking the id of the activity. - stopReason = messageService.getMessage(FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_ACTIVITY,new Object[]{activity.getTitle()}); + else { + //except random grouping, stop here + stopReason = messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_GROUPING, + new Object[] { activity.getTitle() }); + } + } + else { + //if group already exist + learnerService.completeActivity(learner.getUserId(), activity, lessonId); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Grouping activity [" + activity.getActivityId() + "] is completed."); + } + } - } else { - Activity nextActivity = learnerProgress.getNextActivity(); - - // now where? - if ( nextActivity == null || nextActivity.getActivityId().equals(activity.getActivityId()) ) { - // looks like we have reached the end of the sequence? - stopReason = messageService.getMessage(FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_END); - } else if ( touchedActivityIds.contains(nextActivity.getActivityId()) ) { - // processed this one before. Better cut at this point or we will end up in a loop. - // it's probably the parent activity - stopReason = null; // i.e. do nothing - } else { - // no where else to go - keep on pressing on down the sequence then. - stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, nextActivity, stopActivity, touchedActivityIds); - } - } - } - - return stopReason; + } + else if (activity.isGateActivity()) { + GateActivity gate = (GateActivity) activity; + GateActivityDTO dto = learnerService.knockGate(gate, learner, false); + if (dto.getGateOpen()) { + //the gate is opened, continue to next activity to complete + learnerService.completeActivity(learner.getUserId(), activity, lessonId); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Gate activity [" + gate.getActivityId() + "] is completed."); + } + } + else { + //the gate is closed, stop here + stopReason = messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_GATE, + new Object[] { activity.getTitle() }); + } - } - - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLessonDetails(java.lang.Long) - */ - public String getLessonDetails(Long lessonID, Integer userID)throws IOException{ - Lesson lesson = lessonDAO.getLesson(new Long(lessonID)); - checkOwnerOrStaffMember(userID, lesson, "get lesson deatils"); + } + else if (activity.isToolActivity()) { + ToolActivity toolActivity = (ToolActivity) activity; + try { + ToolSession toolSession = lamsCoreToolService.getToolSessionByActivity(learner, toolActivity); + if (toolSession == null) { + // grouped tool's tool session isn't created until the first user in the group reaches that + // point the tool session creation is normally triggered by LoadTooLActivityAction, so we need + // to do it here. Won't happen very often - normally another member of the group will have + // triggered the creation of the tool session. + learnerService.createToolSessionsIfNecessary(toolActivity, progress); + toolSession = lamsCoreToolService.getToolSessionByActivity(learner, toolActivity); + } + learnerService.completeToolSession(toolSession.getToolSessionId(), new Long(learner.getUserId().longValue())); + learnerService.completeActivity(learner.getUserId(), activity, lessonId); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Tool activity [" + activity.getActivityId() + "] is completed."); + } + } + catch (LamsToolServiceException e) { + throw new MonitoringServiceException(e); + } - User user = (User)baseDAO.find(User.class,userID); - LessonDetailsDTO dto = lessonService.getLessonDetails(lessonID); - - Locale userLocale = new Locale(user.getLocale().getLanguageIsoCode(), user.getLocale().getCountryIsoCode()); - - if(dto.getStartDateTime() != null) - dto.setStartDateTimeStr(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, userLocale).format(dto.getStartDateTime())); - - if(dto.getScheduleStartDate() != null) - dto.setScheduleStartDateStr(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, userLocale).format(dto.getScheduleStartDate())); - - FlashMessage flashMessage; - if(dto!=null){ - flashMessage = new FlashMessage("getLessonDetails",dto); - }else - flashMessage = new FlashMessage("getLessonDetails", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - - return flashMessage.serializeMessage(); - } - - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLessonLearners(java.lang.Long) - */ - public String getLessonLearners(Long lessonID, Integer userID)throws IOException{ - Lesson lesson = lessonDAO.getLesson(lessonID); - checkOwnerOrStaffMember(userID, lesson, "get lesson learners"); + } + else if (activity.isBranchingActivity() || activity.isOptionsActivity()) { + // Can force complete over a branching activity, but none of the branches are marked as done. + // Ditto the two types of optional activities. + // Then if the user goes back to them, they will operate normally. + learnerService.completeActivity(learner.getUserId(), activity, lessonId); - Vector lessonLearners = new Vector(); - FlashMessage flashMessage; - if(lesson!=null){ - Iterator iterator = lesson.getLessonClass().getLearners().iterator(); - while(iterator.hasNext()){ - User user = (User)iterator.next(); - lessonLearners.add(user.getUserFlashDTO()); - } - flashMessage = new FlashMessage("getLessonLearners",lessonLearners); - }else - flashMessage = new FlashMessage("getLessonLearners", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - return flashMessage.serializeMessage(); - } - - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLessonStaff(java.lang.Long) - */ - public String getLessonStaff(Long lessonID, Integer userID)throws IOException{ - Lesson lesson = lessonDAO.getLesson(lessonID); - checkOwnerOrStaffMember(userID, lesson, "get lesson staff"); + } + else if (activity.isComplexActivity()) { + // expect it to be a parallel activity + ComplexActivity complexActivity = (ComplexActivity) activity; + Set allActivities = complexActivity.getActivities(); + Iterator iter = allActivities.iterator(); + while (stopReason == null && iter.hasNext()) { + Activity act = (Activity) iter.next(); + stopReason = forceCompleteActivity(learner, lessonId, progress, act, stopActivity, touchedActivityIds); + } + MonitoringService.log.debug("Complex activity [" + activity.getActivityId() + "] is completed."); + } - Vector lessonStaff = new Vector(); - FlashMessage flashMessage; - if(lesson!=null){ - Iterator iterator = lesson.getLessonClass().getStaffGroup().getUsers().iterator(); - while(iterator.hasNext()){ - User user = (User)iterator.next(); - lessonStaff.add(user.getUserFlashDTO()); - } - flashMessage = new FlashMessage("getLessonStaff",lessonStaff); - }else - flashMessage = new FlashMessage("getLessonStaff", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - return flashMessage.serializeMessage(); - } - - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLearningDesignDetails(java.lang.Long) - */ - public String getLearningDesignDetails(Long lessonID)throws IOException{ - Lesson lesson = lessonDAO.getLesson(lessonID); - return authoringService.getLearningDesignDetails(lesson.getLearningDesign().getLearningDesignId(), ""); - } - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getAllLearnersProgress(java.lang.Long, java.lang.Integer) - */ - public String getAllLearnersProgress(Long lessonID, Integer userID)throws IOException { - Lesson lesson = lessonDAO.getLesson(lessonID); - FlashMessage flashMessage; - - if(lesson!=null){ - checkOwnerOrStaffMember(userID, lesson, "get all learners progress"); - Vector progressData = new Vector(); - Iterator iterator = lesson.getLearnerProgresses().iterator(); - while(iterator.hasNext()){ - LearnerProgress learnerProgress = (LearnerProgress)iterator.next(); - progressData.add(learnerProgress.getLearnerProgressData()); - } - flashMessage = new FlashMessage("getAllLearnersProgress",progressData); - }else{ - flashMessage = new FlashMessage("getAllLearnersProgress", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - } - return flashMessage.serializeMessage(); - } - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getInitialLearnersProgress(java.lang.Long, java.lang.Integer) - */ - public String getInitialLearnersProgress(Long lessonID, Integer userID)throws IOException { - Lesson lesson = lessonDAO.getLesson(lessonID); - FlashMessage flashMessage; + // complete to the given activity ID, then stop. To be sure, the given activity is forced to complete as well. + // if we had stopped due to a subactivity, the stop reason will already be set. + if (stopReason == null) { + LearnerProgress learnerProgress = learnerService.getProgress(learner.getUserId(), lessonId); + if (learnerProgress.getCompletedActivities().contains(stopActivity)) { + // we have reached the stop activity. It may have been the activity we just processed + // or it may have been a parent activity (e.g. an optional activity) and completing its last + // child has triggered the parent activity to be completed. Hence we have to check the actual + // completed activities list rather than just checking the id of the activity. + stopReason = messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_ACTIVITY, + new Object[] { activity.getTitle() }); - if(lesson!=null){ - checkOwnerOrStaffMember(userID, lesson, "get first batch of learners progress details"); - Vector progressData = new Vector(); - - int batchSize = Configuration.getAsInt(ConfigurationKeys.LEARNER_PROGRESS_BATCH_SIZE); - if ( batchSize < 1 ) - batchSize = DEFAULT_LEARNER_PROGRESS_BATCH_SIZE; - - Iterator iterator = learnerProgressDAO.getBatchLearnerProgress(lessonID, null, batchSize).iterator(); - while(iterator.hasNext()){ - LearnerProgress learnerProgress = (LearnerProgress)iterator.next(); - progressData.add(learnerProgress.getLearnerProgressData()); - } + } + else { + Activity nextActivity = learnerProgress.getNextActivity(); - Integer numAllLearnerProgress = learnerProgressDAO.getNumAllLearnerProgress(lessonID); - LearnerProgressBatchDTO dto = new LearnerProgressBatchDTO(progressData, batchSize, numAllLearnerProgress); - - flashMessage = new FlashMessage("getInitialLearnersProgress",dto); - }else{ - flashMessage = new FlashMessage("getInitialLearnersProgress", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - } - return flashMessage.serializeMessage(); - } - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getAdditionalLearnersProgress(java.lang.Long, java.lang.Integer, java.lang.Integer) - */ - public String getAdditionalLearnersProgress(Long lessonID, Integer lastUserID, Integer userID)throws IOException { - Lesson lesson = lessonDAO.getLesson(lessonID); - FlashMessage flashMessage; - - if(lesson!=null){ - checkOwnerOrStaffMember(userID, lesson, "get next batch of learners progress details"); - Vector progressData = new Vector(); - - User lastUser = (User)baseDAO.find(User.class,lastUserID); - int batchSize = Configuration.getAsInt(ConfigurationKeys.LEARNER_PROGRESS_BATCH_SIZE); - if ( batchSize < 1 ) - batchSize = DEFAULT_LEARNER_PROGRESS_BATCH_SIZE; - - Iterator iterator = learnerProgressDAO.getBatchLearnerProgress(lessonID, lastUser, batchSize).iterator(); - while(iterator.hasNext()){ - LearnerProgress learnerProgress = (LearnerProgress)iterator.next(); - progressData.add(learnerProgress.getLearnerProgressData()); - } - flashMessage = new FlashMessage("getAdditionalLearnersProgress",progressData); - }else{ - flashMessage = new FlashMessage("getAdditionalLearnersProgress", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - } - return flashMessage.serializeMessage(); - } + // now where? + if (nextActivity == null || nextActivity.getActivityId().equals(activity.getActivityId())) { + // looks like we have reached the end of the sequence? + stopReason = messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_END); + } + else if (touchedActivityIds.contains(nextActivity.getActivityId())) { + // processed this one before. Better cut at this point or we will end up in a loop. + // it's probably the parent activity + stopReason = null; // i.e. do nothing + } + else { + // no where else to go - keep on pressing on down the sequence then. + stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, nextActivity, stopActivity, + touchedActivityIds); + } + } + } - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityById(Long) - */ - public Activity getActivityById(Long activityId) - { - return activityDAO.getActivityByActivityId(activityId); - } - - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityById(Long, Class) - */ - public Activity getActivityById(Long activityId, Class clasz) - { - return activityDAO.getActivityByActivityId(activityId, clasz); - } + return stopReason; - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getGroupingActivityById(Long) - */ - public GroupingActivity getGroupingActivityById(Long activityID) { + } + + /** + * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLessonDetails(java.lang.Long) + */ + public String getLessonDetails(Long lessonID, Integer userID) throws IOException { + Lesson lesson = lessonDAO.getLesson(new Long(lessonID)); + checkOwnerOrStaffMember(userID, lesson, "get lesson deatils"); + + User user = (User) baseDAO.find(User.class, userID); + LessonDetailsDTO dto = lessonService.getLessonDetails(lessonID); + + Locale userLocale = new Locale(user.getLocale().getLanguageIsoCode(), user.getLocale().getCountryIsoCode()); + + if (dto.getStartDateTime() != null) { + dto.setStartDateTimeStr(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, userLocale).format( + dto.getStartDateTime())); + } + + if (dto.getScheduleStartDate() != null) { + dto.setScheduleStartDateStr(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, userLocale).format( + dto.getScheduleStartDate())); + } + + FlashMessage flashMessage; + if (dto != null) { + flashMessage = new FlashMessage("getLessonDetails", dto); + } + else { + flashMessage = new FlashMessage("getLessonDetails", messageService.getMessage("NO.SUCH.LESSON", + new Object[] { lessonID }), FlashMessage.ERROR); + } + + return flashMessage.serializeMessage(); + } + + /** + * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLessonLearners(java.lang.Long) + */ + public String getLessonLearners(Long lessonID, Integer userID) throws IOException { + Lesson lesson = lessonDAO.getLesson(lessonID); + checkOwnerOrStaffMember(userID, lesson, "get lesson learners"); + + Vector lessonLearners = new Vector(); + FlashMessage flashMessage; + if (lesson != null) { + Iterator iterator = lesson.getLessonClass().getLearners().iterator(); + while (iterator.hasNext()) { + User user = (User) iterator.next(); + lessonLearners.add(user.getUserFlashDTO()); + } + flashMessage = new FlashMessage("getLessonLearners", lessonLearners); + } + else { + flashMessage = new FlashMessage("getLessonLearners", messageService.getMessage("NO.SUCH.LESSON", + new Object[] { lessonID }), FlashMessage.ERROR); + } + return flashMessage.serializeMessage(); + } + + /** + * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLessonStaff(java.lang.Long) + */ + public String getLessonStaff(Long lessonID, Integer userID) throws IOException { + Lesson lesson = lessonDAO.getLesson(lessonID); + checkOwnerOrStaffMember(userID, lesson, "get lesson staff"); + + Vector lessonStaff = new Vector(); + FlashMessage flashMessage; + if (lesson != null) { + Iterator iterator = lesson.getLessonClass().getStaffGroup().getUsers().iterator(); + while (iterator.hasNext()) { + User user = (User) iterator.next(); + lessonStaff.add(user.getUserFlashDTO()); + } + flashMessage = new FlashMessage("getLessonStaff", lessonStaff); + } + else { + flashMessage = new FlashMessage("getLessonStaff", messageService.getMessage("NO.SUCH.LESSON", + new Object[] { lessonID }), FlashMessage.ERROR); + } + return flashMessage.serializeMessage(); + } + + /** + * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLearningDesignDetails(java.lang.Long) + */ + public String getLearningDesignDetails(Long lessonID) throws IOException { + Lesson lesson = lessonDAO.getLesson(lessonID); + return authoringService.getLearningDesignDetails(lesson.getLearningDesign().getLearningDesignId(), ""); + } + + /** + * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getAllLearnersProgress(java.lang.Long, java.lang.Integer) + */ + public String getAllLearnersProgress(Long lessonID, Integer userID) throws IOException { + Lesson lesson = lessonDAO.getLesson(lessonID); + FlashMessage flashMessage; + + if (lesson != null) { + checkOwnerOrStaffMember(userID, lesson, "get all learners progress"); + Vector progressData = new Vector(); + Iterator iterator = lesson.getLearnerProgresses().iterator(); + while (iterator.hasNext()) { + LearnerProgress learnerProgress = (LearnerProgress) iterator.next(); + progressData.add(learnerProgress.getLearnerProgressData()); + } + flashMessage = new FlashMessage("getAllLearnersProgress", progressData); + } + else { + flashMessage = new FlashMessage("getAllLearnersProgress", messageService.getMessage("NO.SUCH.LESSON", + new Object[] { lessonID }), FlashMessage.ERROR); + } + return flashMessage.serializeMessage(); + } + + /** + * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getInitialLearnersProgress(java.lang.Long, java.lang.Integer) + */ + public String getInitialLearnersProgress(Long lessonID, Integer userID) throws IOException { + Lesson lesson = lessonDAO.getLesson(lessonID); + FlashMessage flashMessage; + + if (lesson != null) { + checkOwnerOrStaffMember(userID, lesson, "get first batch of learners progress details"); + Vector progressData = new Vector(); + + int batchSize = Configuration.getAsInt(ConfigurationKeys.LEARNER_PROGRESS_BATCH_SIZE); + if (batchSize < 1) { + batchSize = MonitoringService.DEFAULT_LEARNER_PROGRESS_BATCH_SIZE; + } + + Iterator iterator = learnerProgressDAO.getBatchLearnerProgress(lessonID, null, batchSize).iterator(); + while (iterator.hasNext()) { + LearnerProgress learnerProgress = (LearnerProgress) iterator.next(); + progressData.add(learnerProgress.getLearnerProgressData()); + } + + Integer numAllLearnerProgress = learnerProgressDAO.getNumAllLearnerProgress(lessonID); + LearnerProgressBatchDTO dto = new LearnerProgressBatchDTO(progressData, batchSize, numAllLearnerProgress); + + flashMessage = new FlashMessage("getInitialLearnersProgress", dto); + } + else { + flashMessage = new FlashMessage("getInitialLearnersProgress", messageService.getMessage("NO.SUCH.LESSON", + new Object[] { lessonID }), FlashMessage.ERROR); + } + return flashMessage.serializeMessage(); + } + + /** + * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getAdditionalLearnersProgress(java.lang.Long, java.lang.Integer, java.lang.Integer) + */ + public String getAdditionalLearnersProgress(Long lessonID, Integer lastUserID, Integer userID) throws IOException { + Lesson lesson = lessonDAO.getLesson(lessonID); + FlashMessage flashMessage; + + if (lesson != null) { + checkOwnerOrStaffMember(userID, lesson, "get next batch of learners progress details"); + Vector progressData = new Vector(); + + User lastUser = (User) baseDAO.find(User.class, lastUserID); + int batchSize = Configuration.getAsInt(ConfigurationKeys.LEARNER_PROGRESS_BATCH_SIZE); + if (batchSize < 1) { + batchSize = MonitoringService.DEFAULT_LEARNER_PROGRESS_BATCH_SIZE; + } + + Iterator iterator = learnerProgressDAO.getBatchLearnerProgress(lessonID, lastUser, batchSize).iterator(); + while (iterator.hasNext()) { + LearnerProgress learnerProgress = (LearnerProgress) iterator.next(); + progressData.add(learnerProgress.getLearnerProgressData()); + } + flashMessage = new FlashMessage("getAdditionalLearnersProgress", progressData); + } + else { + flashMessage = new FlashMessage("getAdditionalLearnersProgress", messageService.getMessage("NO.SUCH.LESSON", + new Object[] { lessonID }), FlashMessage.ERROR); + } + return flashMessage.serializeMessage(); + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityById(Long) + */ + public Activity getActivityById(Long activityId) { + return activityDAO.getActivityByActivityId(activityId); + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityById(Long, Class) + */ + public Activity getActivityById(Long activityId, Class clasz) { + return activityDAO.getActivityByActivityId(activityId, clasz); + } + + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getGroupingActivityById(Long) + */ + public GroupingActivity getGroupingActivityById(Long activityID) { Activity activity = getActivityById(activityID); - if ( activity == null ) { - String error = "Activity missing. ActivityID was "+activityID; - log.error(error); + if (activity == null) { + String error = "Activity missing. ActivityID was " + activityID; + MonitoringService.log.error(error); throw new MonitoringServiceException(error); - } else if ( ! activity.isGroupingActivity() ) { - String error = "Activity should have been GroupingActivity but was a different kind of activity. "+activity; - log.error(error); + } + else if (!activity.isGroupingActivity()) { + String error = "Activity should have been GroupingActivity but was a different kind of activity. " + activity; + MonitoringService.log.error(error); throw new MonitoringServiceException(error); } - + return (GroupingActivity) activity; } - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getAllContributeActivities(java.lang.Long) - */ - public String getAllContributeActivities(Long lessonID)throws IOException, LearningDesignProcessorException{ - Lesson lesson = lessonDAO.getLesson(lessonID); - FlashMessage flashMessage; - if(lesson!=null){ - ContributeActivitiesProcessor processor = new ContributeActivitiesProcessor(lesson.getLearningDesign(), - lessonID, activityDAO,lamsCoreToolService); - processor.parseLearningDesign(); - Vector activities = processor.getMainActivityList(); - flashMessage = new FlashMessage("getAllContributeActivities",activities); - }else{ - flashMessage = new FlashMessage("getAllContributeActivities", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - } - return flashMessage.serializeMessage(); - } - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLearnerActivityURL(java.lang.Long, java.lang.Integer) - */ - public String getLearnerActivityURL(Long lessonID, Long activityID,Integer learnerUserID, Integer requestingUserId)throws IOException, LamsToolServiceException{ - Lesson lesson = lessonDAO.getLesson(lessonID); - checkOwnerOrStaffMember(requestingUserId, lesson, "get monitoring learner progress url"); + /** + * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getAllContributeActivities(java.lang.Long) + */ + public String getAllContributeActivities(Long lessonID) throws IOException, LearningDesignProcessorException { + Lesson lesson = lessonDAO.getLesson(lessonID); + FlashMessage flashMessage; + if (lesson != null) { + ContributeActivitiesProcessor processor = new ContributeActivitiesProcessor(lesson.getLearningDesign(), lessonID, + activityDAO, lamsCoreToolService); + processor.parseLearningDesign(); + Vector activities = processor.getMainActivityList(); + flashMessage = new FlashMessage("getAllContributeActivities", activities); + } + else { + flashMessage = new FlashMessage("getAllContributeActivities", messageService.getMessage("NO.SUCH.LESSON", + new Object[] { lessonID }), FlashMessage.ERROR); + } + return flashMessage.serializeMessage(); + } - Activity activity = activityDAO.getActivityByActivityId(activityID); - User learner = (User)baseDAO.find(User.class,learnerUserID); - - String url = null; - if(activity==null || learner==null){ - log.error("getLearnerActivityURL activity or user missing. Activity ID "+activityID+" activity " +activity+" userID "+learnerUserID+" user "+learner); - } else if ( activity.isToolActivity() ){ - url = lamsCoreToolService.getToolLearnerProgressURL(lessonID, activity, learner); - } else if (activity.isOptionsActivity() || activity.isParallelActivity()) { - url = "monitoring/complexProgress.do?" - + AttributeNames.PARAM_ACTIVITY_ID + "=" + activityID + "&" - + AttributeNames.PARAM_LESSON_ID + "=" + lessonID + "&" - + AttributeNames.PARAM_USER_ID + "=" + learnerUserID; - } else if (activity.isSystemToolActivity()) { - url = lamsCoreToolService.getToolLearnerProgressURL(lessonID, activity, learner); - } - log.debug("url: "+url); - return url; - } /** * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getLearnerActivityURL(java.lang.Long, java.lang.Integer) + */ + public String getLearnerActivityURL(Long lessonID, Long activityID, Integer learnerUserID, Integer requestingUserId) + throws IOException, LamsToolServiceException { + Lesson lesson = lessonDAO.getLesson(lessonID); + checkOwnerOrStaffMember(requestingUserId, lesson, "get monitoring learner progress url"); + + Activity activity = activityDAO.getActivityByActivityId(activityID); + User learner = (User) baseDAO.find(User.class, learnerUserID); + + String url = null; + if (activity == null || learner == null) { + MonitoringService.log.error("getLearnerActivityURL activity or user missing. Activity ID " + activityID + + " activity " + activity + " userID " + learnerUserID + " user " + learner); + } + else if (activity.isToolActivity()) { + url = lamsCoreToolService.getToolLearnerProgressURL(lessonID, activity, learner); + } + else if (activity.isOptionsActivity() || activity.isParallelActivity()) { + url = "monitoring/complexProgress.do?" + AttributeNames.PARAM_ACTIVITY_ID + "=" + activityID + "&" + + AttributeNames.PARAM_LESSON_ID + "=" + lessonID + "&" + AttributeNames.PARAM_USER_ID + "=" + learnerUserID; + } + else if (activity.isSystemToolActivity()) { + url = lamsCoreToolService.getToolLearnerProgressURL(lessonID, activity, learner); + } + MonitoringService.log.debug("url: " + url); + return url; + } + + /** + * (non-Javadoc) * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityDefineLaterURL(java.lang.Long) */ - public String getActivityDefineLaterURL(Long lessonID, Long activityID, Integer userID)throws IOException, LamsToolServiceException{ - Lesson lesson = lessonDAO.getLesson(lessonID); - checkOwnerOrStaffMember(userID, lesson, "get activity define later url"); + public String getActivityDefineLaterURL(Long lessonID, Long activityID, Integer userID) throws IOException, + LamsToolServiceException { + Lesson lesson = lessonDAO.getLesson(lessonID); + checkOwnerOrStaffMember(userID, lesson, "get activity define later url"); - Activity activity = activityDAO.getActivityByActivityId(activityID); - if(activity==null){ - log.error("getActivityMonitorURL activity missing. Activity ID "+activityID+" activity " +activity); - - } else if ( activity.isToolActivity() ){ + Activity activity = activityDAO.getActivityByActivityId(activityID); + if (activity == null) { + MonitoringService.log.error("getActivityMonitorURL activity missing. Activity ID " + activityID + " activity " + + activity); + + } + else if (activity.isToolActivity()) { ToolActivity toolActivity = (ToolActivity) activity; return lamsCoreToolService.getToolDefineLaterURL(toolActivity); - + } - return null; + return null; } + /** * (non-Javadoc) * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityMonitorURL(java.lang.Long) */ - public String getActivityMonitorURL(Long lessonID, Long activityID, String contentFolderID, Integer userID)throws IOException, LamsToolServiceException{ - Lesson lesson = lessonDAO.getLesson(lessonID); - checkOwnerOrStaffMember(userID, lesson, "get activity define later url"); + public String getActivityMonitorURL(Long lessonID, Long activityID, String contentFolderID, Integer userID) + throws IOException, LamsToolServiceException { + Lesson lesson = lessonDAO.getLesson(lessonID); + checkOwnerOrStaffMember(userID, lesson, "get activity define later url"); Activity activity = activityDAO.getActivityByActivityId(activityID); - - if(activity==null){ - log.error("getActivityMonitorURL activity missing. Activity ID "+activityID+" activity " +activity); - - } else if ( activity.isToolActivity() || activity.isSystemToolActivity() ){ - return lamsCoreToolService.getToolMonitoringURL(lessonID, activity) + "&contentFolderID=" + contentFolderID; - } - return null; + + if (activity == null) { + MonitoringService.log.error("getActivityMonitorURL activity missing. Activity ID " + activityID + " activity " + + activity); + + } + else if (activity.isToolActivity() || activity.isSystemToolActivity()) { + return lamsCoreToolService.getToolMonitoringURL(lessonID, activity) + "&contentFolderID=" + contentFolderID; + } + return null; } /** * (non-Javadoc) * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#moveLesson(java.lang.Long, java.lang.Integer, java.lang.Integer) */ - public String moveLesson(Long lessonID, Integer targetWorkspaceFolderID,Integer userID)throws IOException{ + public String moveLesson(Long lessonID, Integer targetWorkspaceFolderID, Integer userID) throws IOException { Lesson lesson = lessonDAO.getLesson(lessonID); - FlashMessage flashMessage; - if(lesson!=null){ - if(lesson.getUser().getUserId().equals(userID)){ - WorkspaceFolder workspaceFolder = (WorkspaceFolder)baseDAO.find(WorkspaceFolder.class,targetWorkspaceFolderID); - if(workspaceFolder!=null){ + FlashMessage flashMessage; + if (lesson != null) { + if (lesson.getUser().getUserId().equals(userID)) { + WorkspaceFolder workspaceFolder = (WorkspaceFolder) baseDAO.find(WorkspaceFolder.class, targetWorkspaceFolderID); + if (workspaceFolder != null) { LearningDesign learningDesign = lesson.getLearningDesign(); learningDesign.setWorkspaceFolder(workspaceFolder); learningDesignDAO.update(learningDesign); - flashMessage = new FlashMessage("moveLesson",targetWorkspaceFolderID); + flashMessage = new FlashMessage("moveLesson", targetWorkspaceFolderID); } - else - flashMessage = FlashMessage.getNoSuchWorkspaceFolderExsists("moveLesson",targetWorkspaceFolderID); - }else - flashMessage = FlashMessage.getUserNotAuthorized("moveLesson",userID); - }else{ - flashMessage = new FlashMessage("moveLesson", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - + else { + flashMessage = FlashMessage.getNoSuchWorkspaceFolderExsists("moveLesson", targetWorkspaceFolderID); + } + } + else { + flashMessage = FlashMessage.getUserNotAuthorized("moveLesson", userID); + } } + else { + flashMessage = new FlashMessage("moveLesson", messageService.getMessage("NO.SUCH.LESSON", new Object[] { lessonID }), + FlashMessage.ERROR); + + } return flashMessage.serializeMessage(); - + } - /** - * (non-Javadoc) - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#renameLesson(java.lang.Long, java.lang.String, java.lang.Integer) - */ - public String renameLesson(Long lessonID, String newName, Integer userID)throws IOException{ - Lesson lesson = lessonDAO.getLesson(lessonID); - FlashMessage flashMessage; - if(lesson!=null){ - if(lesson.getUser().getUserId().equals(userID)){ - lesson.setLessonName(newName); - lessonDAO.updateLesson(lesson); - flashMessage = new FlashMessage("renameLesson",newName); - }else - flashMessage = FlashMessage.getUserNotAuthorized("renameLesson",userID); - }else - flashMessage = new FlashMessage("renameLesson", - messageService.getMessage("NO.SUCH.LESSON",new Object[]{lessonID}), - FlashMessage.ERROR); - return flashMessage.serializeMessage(); - } /** * (non-Javadoc) + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#renameLesson(java.lang.Long, java.lang.String, java.lang.Integer) + */ + public String renameLesson(Long lessonID, String newName, Integer userID) throws IOException { + Lesson lesson = lessonDAO.getLesson(lessonID); + FlashMessage flashMessage; + if (lesson != null) { + if (lesson.getUser().getUserId().equals(userID)) { + lesson.setLessonName(newName); + lessonDAO.updateLesson(lesson); + flashMessage = new FlashMessage("renameLesson", newName); + } + else { + flashMessage = FlashMessage.getUserNotAuthorized("renameLesson", userID); + } + } + else { + flashMessage = new FlashMessage("renameLesson", messageService + .getMessage("NO.SUCH.LESSON", new Object[] { lessonID }), FlashMessage.ERROR); + } + return flashMessage.serializeMessage(); + } + + /** + * (non-Javadoc) * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#checkGateStatus(java.lang.Long, java.lang.Long) */ - public String checkGateStatus(Long activityID, Long lessonID) throws IOException - { - FlashMessage flashMessage; - GateActivity gate = (GateActivity)activityDAO.getActivityByActivityId(activityID); - Lesson lesson = lessonDAO.getLesson(lessonID); //used to calculate the total learners. - - if(gate==null || lesson==null){ - flashMessage = new FlashMessage("checkGateStatus", - messageService.getMessage("INVALID.ACTIVITYID.LESSONID",new Object[]{activityID,lessonID}), - FlashMessage.ERROR); - } - else - { - Hashtable table = new Hashtable(); - table = createGateStatusInfo(activityID, gate); - flashMessage = new FlashMessage("checkGateStatus", table); - } - return flashMessage.serializeMessage(); + public String checkGateStatus(Long activityID, Long lessonID) throws IOException { + FlashMessage flashMessage; + GateActivity gate = (GateActivity) activityDAO.getActivityByActivityId(activityID); + Lesson lesson = lessonDAO.getLesson(lessonID); //used to calculate the total learners. + + if (gate == null || lesson == null) { + flashMessage = new FlashMessage("checkGateStatus", messageService.getMessage("INVALID.ACTIVITYID.LESSONID", + new Object[] { activityID, lessonID }), FlashMessage.ERROR); + } + else { + Hashtable table = new Hashtable(); + table = createGateStatusInfo(activityID, gate); + flashMessage = new FlashMessage("checkGateStatus", table); + } + return flashMessage.serializeMessage(); } - + /** * (non-javadoc) * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#releaseGate(java.lang.Long) */ - public String releaseGate(Long activityID)throws IOException - { - GateActivity gate = (GateActivity)activityDAO.getActivityByActivityId(activityID); - FlashMessage flashMessage; - if (gate ==null) - { - flashMessage = new FlashMessage("releaseGate", - messageService.getMessage("INVALID.ACTIVITYID",new Object[]{activityID}), - FlashMessage.ERROR); - } - else - { - //release gate - gate = openGate(activityID); - - flashMessage = new FlashMessage("releaseGate", gate.getGateOpen()); - - } - return flashMessage.serializeMessage(); - + public String releaseGate(Long activityID) throws IOException { + GateActivity gate = (GateActivity) activityDAO.getActivityByActivityId(activityID); + FlashMessage flashMessage; + if (gate == null) { + flashMessage = new FlashMessage("releaseGate", messageService.getMessage("INVALID.ACTIVITYID", + new Object[] { activityID }), FlashMessage.ERROR); + } + else { + //release gate + gate = openGate(activityID); + + flashMessage = new FlashMessage("releaseGate", gate.getGateOpen()); + + } + return flashMessage.serializeMessage(); + } - /** - * @throws LessonServiceException - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#performChosenGrouping(GroupingActivity,java.util.List) - */ + + /** + * @throws LessonServiceException + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#performChosenGrouping(GroupingActivity,java.util.List) + */ public void performChosenGrouping(GroupingActivity groupingActivity, List groups) throws LessonServiceException { - Grouping grouping = groupingActivity.getCreateGrouping(); - - if(!grouping.isChosenGrouping()){ - log.error("GroupingActivity ["+groupingActivity.getActivityId() +"] does not have chosen grouping."); - throw new MonitoringServiceException("GroupingActivity ["+groupingActivity.getActivityId() - +"] is not chosen grouping."); - } - try { - //try to sorted group list by orderID. - Iterator iter = groups.iterator(); - Map sortedMap = new TreeMap(new Comparator(){ - public int compare(Object arg0, Object arg1) { - return ((Long)arg0).compareTo((Long)arg1); - } - } - ); - while(iter.hasNext()){ - Hashtable group = (Hashtable) iter.next(); - Long orderId = WDDXProcessor.convertToLong(group,MonitoringConstants.KEY_GROUP_ORDER_ID); - sortedMap.put(orderId,group); - } - iter = sortedMap.values().iterator(); - //grouping all group in list - for(int orderId=0;iter.hasNext();orderId++){ - Hashtable group = (Hashtable) iter.next(); - List learnerIdList = (List) group.get(MonitoringConstants.KEY_GROUP_LEARNERS); - String groupName = WDDXProcessor.convertToString(group,MonitoringConstants.KEY_GROUP_NAME); - List learners = new ArrayList(); - //? Seem too low efficient, is there a easy way? - for(int idx=0;idx organizationUsers, - String staffGroupName, - List staffs, - Lesson newLesson) - { - //create a new lesson class object - LessonClass newLessonClass = createNewLessonClass(newLesson.getLearningDesign()); - lessonClassDAO.saveLessonClass(newLessonClass); - //setup staff group - newLessonClass.setStaffGroup(Group.createStaffGroup(newLessonClass,staffGroupName, - new HashSet(staffs))); - //setup learner group - //TODO:need confirm group name! - newLessonClass.getGroups() - .add(Group.createLearnerGroup(newLessonClass, learnerGroupName, - new HashSet(organizationUsers))); - - lessonClassDAO.updateLessonClass(newLessonClass); - - return newLessonClass; - } - - /** - * Setup a new lesson object without class and insert it into the database. - * - * @param lessonName the name of the lesson - * @param lessonDescription the description of the lesson. - * @param user user the user who want to create this lesson. - * @param learnerExportAvailable should the export portfolio option be made available to the learner? - * @param copiedLearningDesign the copied learning design - * @return the lesson object without class. - * - */ - private Lesson createNewLesson(String lessonName, String lessonDescription, User user, - Boolean learnerExportAvailable, - LearningDesign copiedLearningDesign) - { - Lesson newLesson = Lesson.createNewLessonWithoutClass(lessonName, - lessonDescription, - user, - learnerExportAvailable, - copiedLearningDesign); - lessonDAO.saveLesson(newLesson); - return newLesson; - } - - /** - * Setup the empty lesson class according to the run-time learning design - * copy. - * @param copiedLearningDesign the run-time learning design instance. - * @return the new empty lesson class. - */ - private LessonClass createNewLessonClass(LearningDesign copiedLearningDesign) - { - //make a copy of lazily initialized activities - Set activities = new HashSet(copiedLearningDesign.getActivities()); - LessonClass newLessonClass = new LessonClass(null, //grouping id - new HashSet(),//groups - activities, null, //staff group - null);//lesson - return newLessonClass; - } + //--------------------------------------------------------------------- + // Helper Methods - create lesson + //--------------------------------------------------------------------- + /** + * Create a new lesson and setup all the staffs and learners who will be + * participating this less. + * @param organisation the organization this lesson belongs to. + * @param organizationUsers a list of learner will be in this new lessons. + * @param staffs a list of staffs who will be in charge of this lesson. + * @param newLesson + */ + private LessonClass createLessonClass(Organisation organisation, String learnerGroupName, List organizationUsers, + String staffGroupName, List staffs, Lesson newLesson) { + //create a new lesson class object + LessonClass newLessonClass = createNewLessonClass(newLesson.getLearningDesign()); + lessonClassDAO.saveLessonClass(newLessonClass); - //--------------------------------------------------------------------- - // Helper Methods - start lesson - //--------------------------------------------------------------------- - - /** - * If the activity is not grouped and not in a branch, then it create - * lams tool session for all the learners in the lesson. After the creation - * of lams tool session, it delegates to the tool instances to create tool's own tool session. - * Can't create it for a grouped activity or an activity in a branch - * as it may not be applicable to all users. - *

    - * @param activity the tool activity that all tool session reference to. - * @param lesson the target lesson that these tool sessions belongs to. - * @throws LamsToolServiceException the exception when lams is talking to tool. - */ - private void initToolSessionIfSuitable(ToolActivity activity, Lesson lesson) - { - if ( activity.getApplyGrouping().equals(Boolean.FALSE) && activity.getParentBranch() == null ) { - activity.setToolSessions(new HashSet()); - try { - - Set newToolSessions = lamsCoreToolService.createToolSessions(lesson.getAllLearners(), activity,lesson); - Iterator iter = newToolSessions.iterator(); - while (iter.hasNext()) { - // core has set up a new tool session, we need to ask tool to create their own - // tool sessions using the given id and attach the session to the activity. - ToolSession toolSession = (ToolSession) iter.next(); - lamsCoreToolService.notifyToolsToCreateSession(toolSession, activity); - activity.getToolSessions().add(toolSession); - } - } - catch (LamsToolServiceException e) - { - String error = "Unable to initialise tool session. Fail to call tool services. Error was " - +e.getMessage(); - log.error(error,e); - throw new MonitoringServiceException(error,e); - } - catch (ToolException e) - { - String error = "Unable to initialise tool session. Tool encountered an error. Error was " - +e.getMessage(); - log.error(error,e); - throw new MonitoringServiceException(error,e); - } - } - } - + //setup staff group + newLessonClass.setStaffGroup(Group.createStaffGroup(newLessonClass, staffGroupName, new HashSet(staffs))); + //setup learner group + //TODO:need confirm group name! + newLessonClass.getGroups() + .add(Group.createLearnerGroup(newLessonClass, learnerGroupName, new HashSet(organizationUsers))); + lessonClassDAO.updateLessonClass(newLessonClass); + + return newLessonClass; + } + + /** + * Setup a new lesson object without class and insert it into the database. + * + * @param lessonName the name of the lesson + * @param lessonDescription the description of the lesson. + * @param user user the user who want to create this lesson. + * @param learnerExportAvailable should the export portfolio option be made available to the learner? + * @param copiedLearningDesign the copied learning design + * @return the lesson object without class. + * + */ + private Lesson createNewLesson(String lessonName, String lessonDescription, User user, Boolean learnerExportAvailable, + LearningDesign copiedLearningDesign) { + Lesson newLesson = Lesson.createNewLessonWithoutClass(lessonName, lessonDescription, user, learnerExportAvailable, + copiedLearningDesign); + lessonDAO.saveLesson(newLesson); + return newLesson; + } + + /** + * Setup the empty lesson class according to the run-time learning design + * copy. + * @param copiedLearningDesign the run-time learning design instance. + * @return the new empty lesson class. + */ + private LessonClass createNewLessonClass(LearningDesign copiedLearningDesign) { + //make a copy of lazily initialized activities + Set activities = new HashSet(copiedLearningDesign.getActivities()); + LessonClass newLessonClass = new LessonClass(null, //grouping id + new HashSet(),//groups + activities, null, //staff group + null);//lesson + return newLessonClass; + } + //--------------------------------------------------------------------- - // Helper Methods - scheduling - //--------------------------------------------------------------------- + // Helper Methods - start lesson + //--------------------------------------------------------------------- - /** - * Returns the bean that defines the open schedule gate job. - */ - private JobDetail getOpenScheduleGateJob() - { - return (JobDetail)applicationContext.getBean("openScheduleGateJob"); - } - /** - * - * @return the bean that defines start lesson on schedule job. - */ - private JobDetail getStartScheduleLessonJob() { - return (JobDetail)applicationContext.getBean(MonitoringConstants.JOB_START_LESSON); + /** + * If the activity is not grouped and not in a branch, then it create + * lams tool session for all the learners in the lesson. After the creation + * of lams tool session, it delegates to the tool instances to create tool's own tool session. + * Can't create it for a grouped activity or an activity in a branch + * as it may not be applicable to all users. + *

    + * @param activity the tool activity that all tool session reference to. + * @param lesson the target lesson that these tool sessions belongs to. + * @throws LamsToolServiceException the exception when lams is talking to tool. + */ + private void initToolSessionIfSuitable(ToolActivity activity, Lesson lesson) { + if (activity.getApplyGrouping().equals(Boolean.FALSE) && activity.getParentBranch() == null) { + activity.setToolSessions(new HashSet()); + try { + + Set newToolSessions = lamsCoreToolService.createToolSessions(lesson.getAllLearners(), activity, lesson); + Iterator iter = newToolSessions.iterator(); + while (iter.hasNext()) { + // core has set up a new tool session, we need to ask tool to create their own + // tool sessions using the given id and attach the session to the activity. + ToolSession toolSession = (ToolSession) iter.next(); + lamsCoreToolService.notifyToolsToCreateSession(toolSession, activity); + activity.getToolSessions().add(toolSession); + } + } + catch (LamsToolServiceException e) { + String error = "Unable to initialise tool session. Fail to call tool services. Error was " + e.getMessage(); + MonitoringService.log.error(error, e); + throw new MonitoringServiceException(error, e); + } + catch (ToolException e) { + String error = "Unable to initialise tool session. Tool encountered an error. Error was " + e.getMessage(); + MonitoringService.log.error(error, e); + throw new MonitoringServiceException(error, e); + } + } } - /** - * - * @return the bean that defines start lesson on schedule job. - */ - private JobDetail getFinishScheduleLessonJob() { - return (JobDetail)applicationContext.getBean(MonitoringConstants.JOB_FINISH_LESSON); - } - /** - * Returns the bean that defines the close schdule gate job. - */ - private JobDetail getCloseScheduleGateJob() - { - return (JobDetail)applicationContext.getBean("closeScheduleGateJob"); - } - - private Hashtable createGateStatusInfo(Long activityID, GateActivity gate) - { - Hashtable table = new Hashtable(); - table.put("activityID", activityID); - table.put("activityTypeID", gate.getActivityTypeId()); - table.put("gateOpen", gate.getGateOpen()); - table.put("activityLevelID", gate.getGateActivityLevelId()); - table.put("learnersWaiting", new Integer(gate.getWaitingLearners().size())); - - /* if the gate is a schedule gate, include the information about gate opening - * and gate closing times */ - if (gate.isScheduleGate()) - { - ScheduleGateActivity scheduleGate = (ScheduleGateActivity)gate; - table.put("gateStartTime", scheduleGate.getGateStartDateTime()); - table.put("gateEndTime", scheduleGate.getGateEndDateTime()); - } - return table; - } - - //--------------------------------------------------------------------- - // Preview related methods - //--------------------------------------------------------------------- - /* (non-Javadoc) - * @see org.lamsfoundation.lams.preview.service.IMonitoringService#createPreviewClassForLesson(long, long) - */ - public Lesson createPreviewClassForLesson(int userID, long lessonID) throws UserAccessDeniedException { - User user = (User)baseDAO.find(User.class,userID); - if ( user == null ) { - throw new UserAccessDeniedException("User "+userID+" not found"); - } - - // create the lesson class - add the teacher as the learner and as staff - LinkedList learners = new LinkedList(); - learners.add(user); + //--------------------------------------------------------------------- + // Helper Methods - scheduling + //--------------------------------------------------------------------- - LinkedList staffs = new LinkedList(); - staffs.add(user); - - return createLessonClassForLesson(lessonID, - null, - "Learner Group", - learners, - "Staff Group", - staffs, - userID); + /** + * Returns the bean that defines the open schedule gate job. + */ + private JobDetail getOpenScheduleGateJob() { + return (JobDetail) applicationContext.getBean("openScheduleGateJob"); + } - } - - /** - * Delete a preview lesson and all its contents. Warning: can only delete preview lessons. - * Can't guarentee data integrity if it is done to any other type of lesson. See removeLesson() for hiding - * lessons from a teacher's view without removing them from the database. - * TODO remove the related tool data. - */ - public void deletePreviewLesson(long lessonID) { - Lesson lesson = lessonDAO.getLesson(new Long(lessonID)); - deletePreviewLesson(lesson); - } + /** + * + * @return the bean that defines start lesson on schedule job. + */ + private JobDetail getStartScheduleLessonJob() { + return (JobDetail) applicationContext.getBean(MonitoringConstants.JOB_START_LESSON); + } - private void deletePreviewLesson(Lesson lesson) { - if ( lesson != null ) { - if ( lesson.isPreviewLesson() ) { - - // get all the tool sessions for this lesson and remove all the tool session data - List toolSessions = lamsCoreToolService.getToolSessionsByLesson(lesson); - if ( toolSessions != null && toolSessions.size() > 0 ) { - Iterator iter = toolSessions.iterator(); - while ( iter.hasNext() ) { - ToolSession toolSession = (ToolSession) iter.next(); - lamsCoreToolService.deleteToolSession(toolSession); - } - } else { - log.debug("deletePreviewLesson: Removing tool sessions - none exist"); - } + /** + * + * @return the bean that defines start lesson on schedule job. + */ + private JobDetail getFinishScheduleLessonJob() { + return (JobDetail) applicationContext.getBean(MonitoringConstants.JOB_FINISH_LESSON); + } - // lesson has learning design as a foriegn key, so need to remove lesson before learning design - LearningDesign ld = lesson.getLearningDesign(); - lessonDAO.deleteLesson(lesson); - authoringService.deleteLearningDesign(ld); - - } else { - log.warn("Unable to delete lesson as lesson is not a preview lesson. Learning design copy type was "+lesson.getLearningDesign().getCopyTypeID()); - } - } - } + /** + * Returns the bean that defines the close schdule gate job. + */ + private JobDetail getCloseScheduleGateJob() { + return (JobDetail) applicationContext.getBean("closeScheduleGateJob"); + } - /* (non-Javadoc) - * @see org.lamsfoundation.lams.preview.service.IMonitoringService#deleteAllOldPreviewLessons(int) - */ - public int deleteAllOldPreviewLessons() { + private Hashtable createGateStatusInfo(Long activityID, GateActivity gate) { + Hashtable table = new Hashtable(); + table.put("activityID", activityID); + table.put("activityTypeID", gate.getActivityTypeId()); + table.put("gateOpen", gate.getGateOpen()); + table.put("activityLevelID", gate.getGateActivityLevelId()); + table.put("learnersWaiting", new Integer(gate.getWaitingLearners().size())); - int numDays = Configuration.getAsInt(ConfigurationKeys.PREVIEW_CLEANUP_NUM_DAYS); + /* if the gate is a schedule gate, include the information about gate opening + * and gate closing times */ + if (gate.isScheduleGate()) { + ScheduleGateActivity scheduleGate = (ScheduleGateActivity) gate; + table.put("gateStartTime", scheduleGate.getGateStartDateTime()); + table.put("gateEndTime", scheduleGate.getGateEndDateTime()); + } + return table; + } - // Contract checking - if ( numDays <= 0 ) { - log.error("deleteAllOldPreviewSessions: number of days invalid (" - +numDays - +"). See configuration file (option " - +ConfigurationKeys.PREVIEW_CLEANUP_NUM_DAYS - +" Unable to delete any preview lessons"); - return 0; - } - - int numDeleted = 0; + //--------------------------------------------------------------------- + // Preview related methods + //--------------------------------------------------------------------- + /* (non-Javadoc) + * @see org.lamsfoundation.lams.preview.service.IMonitoringService#createPreviewClassForLesson(long, long) + */ + public Lesson createPreviewClassForLesson(int userID, long lessonID) throws UserAccessDeniedException { - // calculate comparison date - long newestDateToKeep = System.currentTimeMillis() - ( numDays * numMilliSecondsInADay); - Date date = new Date(newestDateToKeep); - // convert data to UTC - log.info("Deleting all preview lessons before "+date.toString()+" (server time) ("+newestDateToKeep+")"); - - // get all the preview sessions older than a particular date. - List sessions = lessonDAO.getPreviewLessonsBeforeDate(date); - Iterator iter = sessions.iterator(); - while (iter.hasNext()) { - Lesson lesson = (Lesson) iter.next(); - try { - deletePreviewLesson(lesson); - numDeleted++; - } catch ( Exception e ) { - log.error("Unable to delete lesson "+lesson.getLessonId()+" due to exception.",e); - } - } - - return numDeleted; - } - - - /* ************** Grouping and branching related calls ***************************************/ - /** Get all the active learners in the lesson who are not in a group/branch - * - * If the activity is a grouping activity, then set useCreatingGrouping = true to - * base the list on the create grouping. Otherwise leave it false and it will use the - * grouping applied to the activity - this is used for branching activities. - * - * TODO Optimise the database query. Do a single query rather then large collection access - * - * @param activityID - * @param lessonID - * @param useCreateGrouping true/false for GroupingActivities, always false for non-GroupingActivities - * @return Sorted set of Users, sorted by surname - */ - public SortedSet getClassMembersNotGrouped(Long lessonID, Long activityID, boolean useCreateGrouping) { - Activity activity = getActivityById(activityID); - Grouping grouping = getGroupingForActivity(activity, useCreateGrouping,"getClassMembersNotGrouped"); - - // get all the learners in the class, irrespective of whether they have joined the lesson or not. - // then go through each group and remove the grouped users from the activeLearners set. - Lesson lesson = lessonDAO.getLesson(lessonID); - if ( lesson == null ) { - String error = "Lesson missing. LessonID was "+lessonID+" Activity id was "+activityID; - log.error(error); - throw new MonitoringServiceException(error); - } + User user = (User) baseDAO.find(User.class, userID); + if (user == null) { + throw new UserAccessDeniedException("User " + userID + " not found"); + } - Set learners = lesson.getAllLearners(); - if ( log.isDebugEnabled() ) { - log.debug("getClassMembersNotGrouped: Lesson "+lessonID+" has "+learners.size()+" learners."); - } - - Iterator iter = grouping.getGroups().iterator(); - while ( iter.hasNext() ) { - Group group = (Group) iter.next(); - learners.removeAll(group.getUsers()); - if ( log.isDebugEnabled() ) { - log.debug("getClassMembersNotGrouped: Group "+group.getGroupId()+" has "+group.getUsers().size()+" members."); + // create the lesson class - add the teacher as the learner and as staff + LinkedList learners = new LinkedList(); + learners.add(user); + + LinkedList staffs = new LinkedList(); + staffs.add(user); + + return createLessonClassForLesson(lessonID, null, "Learner Group", learners, "Staff Group", staffs, userID); + + } + + /** + * Delete a preview lesson and all its contents. Warning: can only delete preview lessons. + * Can't guarentee data integrity if it is done to any other type of lesson. See removeLesson() for hiding + * lessons from a teacher's view without removing them from the database. + * TODO remove the related tool data. + */ + public void deletePreviewLesson(long lessonID) { + Lesson lesson = lessonDAO.getLesson(new Long(lessonID)); + deletePreviewLesson(lesson); + } + + private void deletePreviewLesson(Lesson lesson) { + if (lesson != null) { + if (lesson.isPreviewLesson()) { + + // get all the tool sessions for this lesson and remove all the tool session data + List toolSessions = lamsCoreToolService.getToolSessionsByLesson(lesson); + if (toolSessions != null && toolSessions.size() > 0) { + Iterator iter = toolSessions.iterator(); + while (iter.hasNext()) { + ToolSession toolSession = (ToolSession) iter.next(); + lamsCoreToolService.deleteToolSession(toolSession); + } } + else { + MonitoringService.log.debug("deletePreviewLesson: Removing tool sessions - none exist"); + } + + // lesson has learning design as a foriegn key, so need to remove lesson before learning design + LearningDesign ld = lesson.getLearningDesign(); + lessonDAO.deleteLesson(lesson); + authoringService.deleteLearningDesign(ld); + } + else { + MonitoringService.log + .warn("Unable to delete lesson as lesson is not a preview lesson. Learning design copy type was " + + lesson.getLearningDesign().getCopyTypeID()); + } + } + } - if ( log.isDebugEnabled() ) { - log.debug("getClassMembersNotGrouped: Lesson "+lessonID+" has "+learners.size()+" learners."); + /* (non-Javadoc) + * @see org.lamsfoundation.lams.preview.service.IMonitoringService#deleteAllOldPreviewLessons(int) + */ + public int deleteAllOldPreviewLessons() { + + int numDays = Configuration.getAsInt(ConfigurationKeys.PREVIEW_CLEANUP_NUM_DAYS); + + // Contract checking + if (numDays <= 0) { + MonitoringService.log.error("deleteAllOldPreviewSessions: number of days invalid (" + numDays + + "). See configuration file (option " + ConfigurationKeys.PREVIEW_CLEANUP_NUM_DAYS + + " Unable to delete any preview lessons"); + return 0; + } + + int numDeleted = 0; + + // calculate comparison date + long newestDateToKeep = System.currentTimeMillis() - numDays * MonitoringService.numMilliSecondsInADay; + Date date = new Date(newestDateToKeep); + // convert data to UTC + MonitoringService.log.info("Deleting all preview lessons before " + date.toString() + " (server time) (" + + newestDateToKeep + ")"); + + // get all the preview sessions older than a particular date. + List sessions = lessonDAO.getPreviewLessonsBeforeDate(date); + Iterator iter = sessions.iterator(); + while (iter.hasNext()) { + Lesson lesson = (Lesson) iter.next(); + try { + deletePreviewLesson(lesson); + numDeleted++; } + catch (Exception e) { + MonitoringService.log.error("Unable to delete lesson " + lesson.getLessonId() + " due to exception.", e); + } + } - SortedSet sortedUsers = new TreeSet(new LastNameAlphabeticComparator()); - sortedUsers.addAll(learners); - return sortedUsers; - } - - /** Get the grouping appropriate for this activity. - * - * If the activity is a grouping activity, then set useCreatingGrouping = true to - * base the list on the create grouping. Otherwise leave it false and it will use the - * grouping applied to the activity - this is used for branching activities. - * - * If it is a teacher chosen branching activity and the grouping doesn't exist, it creates - * one. - */ + return numDeleted; + } + + /* ************** Grouping and branching related calls ***************************************/ + /** Get all the active learners in the lesson who are not in a group/branch + * + * If the activity is a grouping activity, then set useCreatingGrouping = true to + * base the list on the create grouping. Otherwise leave it false and it will use the + * grouping applied to the activity - this is used for branching activities. + * + * TODO Optimise the database query. Do a single query rather then large collection access + * + * @param activityID + * @param lessonID + * @param useCreateGrouping true/false for GroupingActivities, always false for non-GroupingActivities + * @return Sorted set of Users, sorted by surname + */ + public SortedSet getClassMembersNotGrouped(Long lessonID, Long activityID, boolean useCreateGrouping) { + Activity activity = getActivityById(activityID); + Grouping grouping = getGroupingForActivity(activity, useCreateGrouping, "getClassMembersNotGrouped"); + + // get all the learners in the class, irrespective of whether they have joined the lesson or not. + // then go through each group and remove the grouped users from the activeLearners set. + Lesson lesson = lessonDAO.getLesson(lessonID); + if (lesson == null) { + String error = "Lesson missing. LessonID was " + lessonID + " Activity id was " + activityID; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } + + Set learners = lesson.getAllLearners(); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() + + " learners."); + } + + Iterator iter = grouping.getGroups().iterator(); + while (iter.hasNext()) { + Group group = (Group) iter.next(); + learners.removeAll(group.getUsers()); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("getClassMembersNotGrouped: Group " + group.getGroupId() + " has " + + group.getUsers().size() + " members."); + } + } + + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() + + " learners."); + } + + SortedSet sortedUsers = new TreeSet(new LastNameAlphabeticComparator()); + sortedUsers.addAll(learners); + return sortedUsers; + } + + /** Get the grouping appropriate for this activity. + * + * If the activity is a grouping activity, then set useCreatingGrouping = true to + * base the list on the create grouping. Otherwise leave it false and it will use the + * grouping applied to the activity - this is used for branching activities. + * + * If it is a teacher chosen branching activity and the grouping doesn't exist, it creates + * one. + */ private Grouping getGroupingForActivity(Activity activity, boolean useCreateGrouping, String methodName) { - if ( useCreateGrouping && (activity == null || !activity.isGroupingActivity()) ) { - String error = methodName+": Trying to use the create grouping option but the activity isn't a grouping activity. Activity was "+activity; - log.error(error); + if (useCreateGrouping && (activity == null || !activity.isGroupingActivity())) { + String error = methodName + + ": Trying to use the create grouping option but the activity isn't a grouping activity. Activity was " + + activity; + MonitoringService.log.error(error); throw new MonitoringServiceException(error); } - + Grouping grouping = null; - - if ( useCreateGrouping ) { - GroupingActivity groupingActivity = (GroupingActivity) activity; + + if (useCreateGrouping) { + GroupingActivity groupingActivity = (GroupingActivity) activity; grouping = groupingActivity.getCreateGrouping(); - } else { + } + else { grouping = activity.getGrouping(); } - if ( grouping == null ) { - String error = methodName+": Grouping activity missing grouping. Activity was "+activity; - log.error(error); + if (grouping == null) { + String error = methodName + ": Grouping activity missing grouping. Activity was " + activity; + MonitoringService.log.error(error); throw new MonitoringServiceException(error); } return grouping; } - /** Add a new group to a grouping activity. If name already exists or the name is blank, does not add a new group. - * If the activity is a grouping activity, then set useCreatingGrouping = true to - * base the list on the create grouping. Otherwise leave it false and it will use the - * grouping applied to the activity - this is used for branching activities. - * - * If it is a teacher chosen branching activity and the grouping doesn't exist, it creates - * one. + * If the activity is a grouping activity, then set useCreatingGrouping = true to + * base the list on the create grouping. Otherwise leave it false and it will use the + * grouping applied to the activity - this is used for branching activities. + * + * If it is a teacher chosen branching activity and the grouping doesn't exist, it creates + * one. * @param activityID id of the grouping activity * @param name group name * @throws LessonServiceException, MonitoringServiceException */ - public void addGroup(Long activityID, String name, boolean overrideMaxNumberOfGroups) throws LessonServiceException, MonitoringServiceException { - Activity activity = getActivityById(activityID); - Grouping grouping = getGroupingForActivity(activity, true,"addGroup"); - if ( overrideMaxNumberOfGroups ) { + public void addGroup(Long activityID, String name, boolean overrideMaxNumberOfGroups) throws LessonServiceException, + MonitoringServiceException { + Activity activity = getActivityById(activityID); + Grouping grouping = getGroupingForActivity(activity, true, "addGroup"); + if (overrideMaxNumberOfGroups) { // Is this grouping used for branching. If it is, must honour the groups // set in authoring or some groups won't have a branch. - if ( grouping.getMaxNumberOfGroups() != null && grouping.getMaxNumberOfGroups() > 0 && grouping.getGroups() != null && grouping.getGroups().size() >= grouping.getMaxNumberOfGroups() ) { + if (grouping.getMaxNumberOfGroups() != null && grouping.getMaxNumberOfGroups() > 0 && grouping.getGroups() != null + && grouping.getGroups().size() >= grouping.getMaxNumberOfGroups()) { boolean usedForBranching = grouping.isUsedForBranching(); - if ( ! usedForBranching ) { - log.info("Setting max number of groups to null for grouping "+grouping+" we have been asked to add a group in excess of the max number of groups (probably via the Chosen Grouping screen)."); + if (!usedForBranching) { + MonitoringService.log + .info("Setting max number of groups to null for grouping " + + grouping + + " we have been asked to add a group in excess of the max number of groups (probably via the Chosen Grouping screen)."); grouping.setMaxNumberOfGroups(null); // must be null and not 0 or the groups will be lost via Live Edit. - } else { - log.error("Request made to add a group which would be more than the max number of groups for the grouping "+grouping+". This grouping is used for branching so we can't increase the max group number."); - throw new MonitoringServiceException("Cannot increase the number of groups for the grouping "+grouping+" as this grouping is used for a branching activity."); } + else { + MonitoringService.log + .error("Request made to add a group which would be more than the max number of groups for the grouping " + + grouping + + ". This grouping is used for branching so we can't increase the max group number."); + throw new MonitoringServiceException("Cannot increase the number of groups for the grouping " + grouping + + " as this grouping is used for a branching activity."); + } } } lessonService.createGroup(grouping, name); } - + /** Remove a group to from a grouping activity. If the group does not exists then nothing happens. * If the group is already used (e.g. a tool session exists) then it throws a LessonServiceException. - * - * If the activity is a grouping activity, then set useCreatingGrouping = true to - * base the list on the create grouping. Otherwise leave it false and it will use the - * grouping applied to the activity - this is used for branching activities. - * - * If it is a teacher chosen branching activity and the grouping doesn't exist, it creates - * one. + * + * If the activity is a grouping activity, then set useCreatingGrouping = true to + * base the list on the create grouping. Otherwise leave it false and it will use the + * grouping applied to the activity - this is used for branching activities. + * + * If it is a teacher chosen branching activity and the grouping doesn't exist, it creates + * one. * @param activityID id of the grouping activity * @param name group name * @throws LessonServiceException **/ public void removeGroup(Long activityID, Long groupId) throws LessonServiceException { - Activity activity = getActivityById(activityID); - Grouping grouping = getGroupingForActivity(activity, true,"removeGroup"); + Activity activity = getActivityById(activityID); + Grouping grouping = getGroupingForActivity(activity, true, "removeGroup"); lessonService.removeGroup(grouping, groupId); } /** Add learners to a group. Doesn't necessarily check if the user is already in another group. */ - public void addUsersToGroup(Long activityID, Long groupId, String learnerIDs[]) throws LessonServiceException { - Activity activity = getActivityById(activityID); - Grouping grouping = getGroupingForActivity(activity, true, "addUsersToGroup"); - ArrayList learners = createUserList(activityID, learnerIDs,"add"); + public void addUsersToGroup(Long activityID, Long groupId, String learnerIDs[]) throws LessonServiceException { + Activity activity = getActivityById(activityID); + Grouping grouping = getGroupingForActivity(activity, true, "addUsersToGroup"); + ArrayList learners = createUserList(activityID, learnerIDs, "add"); lessonService.performGrouping(grouping, groupId, learners); - } - - private ArrayList createUserList(Long activityIDForErrorMessage, String[] learnerIDs, String addRemoveTextForErrorMessage) { - ArrayList learners = new ArrayList(); - for ( String strlearnerID: learnerIDs ) { - boolean added = false; - try { - Integer learnerID = new Integer(Integer.parseInt(strlearnerID)); - User learner = (User)baseDAO.find(User.class,learnerID); - if ( learner != null ) { - learners.add(learner); - added = true; - } - } catch ( NumberFormatException e ) { } - if ( ! added ) { - log.warn("Unable to "+addRemoveTextForErrorMessage+" learner "+strlearnerID - +" for group in related to activity "+activityIDForErrorMessage+" as learner cannot be found."); + } + + private ArrayList createUserList(Long activityIDForErrorMessage, String[] learnerIDs, + String addRemoveTextForErrorMessage) { + ArrayList learners = new ArrayList(); + for (String strlearnerID : learnerIDs) { + boolean added = false; + try { + Integer learnerID = new Integer(Integer.parseInt(strlearnerID)); + User learner = (User) baseDAO.find(User.class, learnerID); + if (learner != null) { + learners.add(learner); + added = true; } } - return learners; + catch (NumberFormatException e) { + } + if (!added) { + MonitoringService.log.warn("Unable to " + addRemoveTextForErrorMessage + " learner " + strlearnerID + + " for group in related to activity " + activityIDForErrorMessage + " as learner cannot be found."); + } } + return learners; + } - /** Add learners to a branch. Doesn't necessarily check if the user is already in another branch. - * Assumes there should only be one group for this branch. Use for Teacher Chosen Branching. Don't use - * for Group Based Branching as there could be more than one group for the branch. - * - * @param sequenceActivityID Activity id of the sequenceActivity representing this branch - * @param learnerIDs the IDS of the learners to be added. - */ - public void addUsersToBranch(Long sequenceActivityID, String learnerIDs[]) throws LessonServiceException { + /** Add learners to a branch. Doesn't necessarily check if the user is already in another branch. + * Assumes there should only be one group for this branch. Use for Teacher Chosen Branching. Don't use + * for Group Based Branching as there could be more than one group for the branch. + * + * @param sequenceActivityID Activity id of the sequenceActivity representing this branch + * @param learnerIDs the IDS of the learners to be added. + */ + public void addUsersToBranch(Long sequenceActivityID, String learnerIDs[]) throws LessonServiceException { - SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); - if ( branch == null ) { - String error = "addUsersToBranch: Branch missing. ActivityID was "+sequenceActivityID; - log.error(error); + SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); + if (branch == null) { + String error = "addUsersToBranch: Branch missing. ActivityID was " + sequenceActivityID; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } + + Group group = branch.getSoleGroupForBranch(); + Grouping grouping = null; + if (group == null) { + // create a new group and a matching mapping entry + Activity parentActivity = branch.getParentActivity(); + if (parentActivity == null || !parentActivity.isBranchingActivity()) { + String error = "addUsersToBranch: Branching activity missing or not a branching activity. Branch was " + branch + + " parent activity was " + parentActivity; + MonitoringService.log.error(error); throw new MonitoringServiceException(error); } + BranchingActivity branchingActivity = (BranchingActivity) getActivityById(parentActivity.getActivityId()); + grouping = branchingActivity.getGrouping(); - Group group = branch.getSoleGroupForBranch(); - Grouping grouping = null; - if ( group == null ) { - // create a new group and a matching mapping entry - Activity parentActivity = branch.getParentActivity(); - if ( parentActivity == null || !parentActivity.isBranchingActivity()) { - String error = "addUsersToBranch: Branching activity missing or not a branching activity. Branch was "+branch+" parent activity was "+parentActivity; - log.error(error); - throw new MonitoringServiceException(error); - } - BranchingActivity branchingActivity = (BranchingActivity) getActivityById(parentActivity.getActivityId()); - grouping = branchingActivity.getGrouping(); + // Need the learning design to get the next uiid - which is needed if + // Live Edit is done, or Flash can't match the branch to the groups properly. + LearningDesign design = branchingActivity.getLearningDesign(); - // Need the learning design to get the next uiid - which is needed if - // Live Edit is done, or Flash can't match the branch to the groups properly. - LearningDesign design = branchingActivity.getLearningDesign(); + group = lessonService.createGroup(grouping, branch.getTitle()); + groupingDAO.insert(group); + Integer nextUIID = new Integer(design.getMaxID().intValue() + 1); + group.setGroupUIID(nextUIID); + nextUIID = new Integer(nextUIID.intValue() + 1); + group.allocateBranchToGroup(nextUIID, branch, branchingActivity); + groupingDAO.update(group); - group = lessonService.createGroup(grouping, branch.getTitle()); - groupingDAO.insert(group); - Integer nextUIID = new Integer(design.getMaxID().intValue()+1); - group.setGroupUIID(nextUIID); - nextUIID = new Integer(nextUIID.intValue()+1); - group.allocateBranchToGroup(nextUIID, branch, (BranchingActivity)branchingActivity); - groupingDAO.update(group); - - design.setMaxID(new Integer(nextUIID.intValue()+1)); - learningDesignDAO.update(design); - - } else { - grouping = group.getGrouping(); - } + design.setMaxID(new Integer(nextUIID.intValue() + 1)); + learningDesignDAO.update(design); - ArrayList learners = createUserList(sequenceActivityID, learnerIDs,"add"); - lessonService.performGrouping(grouping, group.getGroupId(), learners); - } - - /** Remove a user to a group. If the user is not in the group, then nothing is changed. - * @throws LessonServiceException */ - public void removeUsersFromGroup(Long activityID, Long groupId, String learnerIDs[]) throws LessonServiceException { - Activity activity = getActivityById(activityID); - Grouping grouping = getGroupingForActivity(activity, true,"removeUsersFromGroup"); - ArrayList learners = createUserList(activityID, learnerIDs,"remove"); - lessonService.removeLearnersFromGroup(grouping, groupId, learners); } - - /** Remove learners from a branch. - * Assumes there should only be one group for this branch. Use for Teacher Chosen Branching. Don't use - * for Group Based Branching as there could be more than one group for the branch. - * - * @param sequenceActivityID Activity id of the sequenceActivity representing this branch - * @param learnerIDs the IDS of the learners to be added. - */ - public void removeUsersFromBranch(Long sequenceActivityID, String learnerIDs[]) throws LessonServiceException { + else { + grouping = group.getGrouping(); + } - SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); - if ( branch == null ) { - String error = "addUsersToBranch: Branch missing. ActivityID was "+sequenceActivityID; - log.error(error); - throw new MonitoringServiceException(error); - } + ArrayList learners = createUserList(sequenceActivityID, learnerIDs, "add"); + lessonService.performGrouping(grouping, group.getGroupId(), learners); + } - Group group = branch.getSoleGroupForBranch(); - Grouping grouping = null; - if ( group != null ) { - grouping = group.getGrouping(); - ArrayList learners = createUserList(sequenceActivityID, learnerIDs,"remove"); - lessonService.removeLearnersFromGroup(grouping, group.getGroupId(), learners); - } else { - log.warn("Trying to remove users "+learnerIDs+" from branch "+branch+" but no group exists for this branch, so the users can't be in the group!"); - } + /** Remove a user to a group. If the user is not in the group, then nothing is changed. + * @throws LessonServiceException */ + public void removeUsersFromGroup(Long activityID, Long groupId, String learnerIDs[]) throws LessonServiceException { + Activity activity = getActivityById(activityID); + Grouping grouping = getGroupingForActivity(activity, true, "removeUsersFromGroup"); + ArrayList learners = createUserList(activityID, learnerIDs, "remove"); + lessonService.removeLearnersFromGroup(grouping, groupId, learners); + } + /** Remove learners from a branch. + * Assumes there should only be one group for this branch. Use for Teacher Chosen Branching. Don't use + * for Group Based Branching as there could be more than one group for the branch. + * + * @param sequenceActivityID Activity id of the sequenceActivity representing this branch + * @param learnerIDs the IDS of the learners to be added. + */ + public void removeUsersFromBranch(Long sequenceActivityID, String learnerIDs[]) throws LessonServiceException { + + SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); + if (branch == null) { + String error = "addUsersToBranch: Branch missing. ActivityID was " + sequenceActivityID; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); } - - /** Match group(s) to a branch. Doesn't necessarily check if the group is already assigned to another branch. - * Use for Group Based Branching and define later. - * - * @param sequenceActivityID Activity id of the sequenceActivity representing this branch - * @param learnerIDs the IDS of the learners to be added. - */ - public void addGroupToBranch(Long sequenceActivityID, String groupIDs[]) throws LessonServiceException { - SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); - if ( branch == null ) { - String error = "addGroupToBranch: Branch missing. ActivityID was "+sequenceActivityID; - log.error(error); - throw new MonitoringServiceException(error); - } + Group group = branch.getSoleGroupForBranch(); + Grouping grouping = null; + if (group != null) { + grouping = group.getGrouping(); + ArrayList learners = createUserList(sequenceActivityID, learnerIDs, "remove"); + lessonService.removeLearnersFromGroup(grouping, group.getGroupId(), learners); + } + else { + MonitoringService.log.warn("Trying to remove users " + learnerIDs + " from branch " + branch + + " but no group exists for this branch, so the users can't be in the group!"); + } - Activity parentActivity = branch.getParentActivity(); - if ( parentActivity == null || !parentActivity.isBranchingActivity()) { - String error = "addUsersToBranch: Branching activity missing or not a branching activity. Branch was "+branch+" parent activity was "+parentActivity; - log.error(error); - throw new MonitoringServiceException(error); - } - BranchingActivity branchingActivity = (BranchingActivity) getActivityById(parentActivity.getActivityId()); - Grouping grouping = branchingActivity.getGrouping(); + } - LearningDesign design = branchingActivity.getLearningDesign(); - Integer nextUIID = new Integer(design.getMaxID().intValue()+1); + /** Match group(s) to a branch. Doesn't necessarily check if the group is already assigned to another branch. + * Use for Group Based Branching and define later. + * + * @param sequenceActivityID Activity id of the sequenceActivity representing this branch + * @param learnerIDs the IDS of the learners to be added. + */ + public void addGroupToBranch(Long sequenceActivityID, String groupIDs[]) throws LessonServiceException { - for ( String groupIDString: groupIDs) { - Long groupID = Long.parseLong(groupIDString); + SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); + if (branch == null) { + String error = "addGroupToBranch: Branch missing. ActivityID was " + sequenceActivityID; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } - Group group = null; - Iterator groupIterator = grouping.getGroups().iterator(); - while (groupIterator.hasNext() && group == null) { - Group obj = (Group) groupIterator.next(); - if ( obj.getGroupId().equals(groupID) ) - group = obj; - } - if ( group == null ) { - String error = "addGroupToBranch: Group missing. Group ID was "+groupIDString; - log.error(error); - throw new MonitoringServiceException(error); - } - - group.allocateBranchToGroup(nextUIID, branch, branchingActivity); - groupingDAO.update(group); - } - - design.setMaxID(new Integer(nextUIID.intValue()+1)); - learningDesignDAO.update(design); + Activity parentActivity = branch.getParentActivity(); + if (parentActivity == null || !parentActivity.isBranchingActivity()) { + String error = "addUsersToBranch: Branching activity missing or not a branching activity. Branch was " + branch + + " parent activity was " + parentActivity; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } + BranchingActivity branchingActivity = (BranchingActivity) getActivityById(parentActivity.getActivityId()); + Grouping grouping = branchingActivity.getGrouping(); - } - - /** Remove group / branch mapping. Cannot be done if any users in the group have started the branch. - * Used for group based branching in define later. - * - * @param sequenceActivityID Activity id of the sequenceActivity representing this branch - * @param learnerIDs the IDS of the learners to be added. - */ - public void removeGroupFromBranch(Long sequenceActivityID, String groupIDs[]) throws LessonServiceException { + LearningDesign design = branchingActivity.getLearningDesign(); + Integer nextUIID = new Integer(design.getMaxID().intValue() + 1); - SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); - if ( branch == null ) { - String error = "addUsersToBranch: Branch missing. ActivityID was "+sequenceActivityID; - log.error(error); - throw new MonitoringServiceException(error); - } + for (String groupIDString : groupIDs) { + Long groupID = Long.parseLong(groupIDString); - Activity parentActivity = branch.getParentActivity(); - if ( parentActivity == null || !parentActivity.isBranchingActivity()) { - String error = "addUsersToBranch: Branching activity missing or not a branching activity. Branch was "+branch+" parent activity was "+parentActivity; - log.error(error); + Group group = null; + Iterator groupIterator = grouping.getGroups().iterator(); + while (groupIterator.hasNext() && group == null) { + Group obj = (Group) groupIterator.next(); + if (obj.getGroupId().equals(groupID)) { + group = obj; + } + } + if (group == null) { + String error = "addGroupToBranch: Group missing. Group ID was " + groupIDString; + MonitoringService.log.error(error); throw new MonitoringServiceException(error); } - BranchingActivity branchingActivity = (BranchingActivity) getActivityById(parentActivity.getActivityId()); - Grouping grouping = branchingActivity.getGrouping(); - for ( String groupIDString: groupIDs) { - Long groupID = Long.parseLong(groupIDString); + group.allocateBranchToGroup(nextUIID, branch, branchingActivity); + groupingDAO.update(group); + } - Group group = null; - Iterator groupIterator = grouping.getGroups().iterator(); - while (groupIterator.hasNext() && group == null) { - Group obj = (Group) groupIterator.next(); - if ( obj.getGroupId().equals(groupID) ) - group = obj; - } - if ( group == null ) { - String error = "removeGroupFromBranch: Group missing. Group ID was "+groupIDString; - log.error(error); - throw new MonitoringServiceException(error); - } - - // can't remove the group if someone has already started working on the branch. - if ( isActivityAttempted(branch) ) { - log.warn("removeGroupFromBranch: A group member has already started the branch. Unable to remove the group from the branch. Group ID was "+groupIDString); - } else { - branch.removeGroupFromBranch(group); - activityDAO.update(branch); - } - } - + design.setMaxID(new Integer(nextUIID.intValue() + 1)); + learningDesignDAO.update(design); + + } + + /** Remove group / branch mapping. Cannot be done if any users in the group have started the branch. + * Used for group based branching in define later. + * + * @param sequenceActivityID Activity id of the sequenceActivity representing this branch + * @param learnerIDs the IDS of the learners to be added. + */ + public void removeGroupFromBranch(Long sequenceActivityID, String groupIDs[]) throws LessonServiceException { + + SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); + if (branch == null) { + String error = "addUsersToBranch: Branch missing. ActivityID was " + sequenceActivityID; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); } - /** Has anyone started this branch / branching activity ? Irrespective of the groups. - * Used to determine if a branch mapping can be removed. */ - public boolean isActivityAttempted(Activity activity) { - Integer numAttempted = lessonService.getCountLearnersHaveAttemptedActivity(activity); - if ( log.isDebugEnabled() ) { - log.debug("isActivityAttempted: num attempts for activity "+activity.getActivityId()+" is "+numAttempted); - } - return numAttempted !=null && numAttempted.intValue() > 0; + Activity parentActivity = branch.getParentActivity(); + if (parentActivity == null || !parentActivity.isBranchingActivity()) { + String error = "addUsersToBranch: Branching activity missing or not a branching activity. Branch was " + branch + + " parent activity was " + parentActivity; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); } - - /** - * Get all the groups that exist for the related grouping activity that have not been allocated to a branch. - * - * @param branchingActivityID Activity id of the branchingActivity - */ - public SortedSet getGroupsNotAssignedToBranch(Long branchingActivityID) throws LessonServiceException { + BranchingActivity branchingActivity = (BranchingActivity) getActivityById(parentActivity.getActivityId()); + Grouping grouping = branchingActivity.getGrouping(); - BranchingActivity branchingActivity = (BranchingActivity) getActivityById(branchingActivityID); - if ( branchingActivity == null ) { - String error = "getGroupsNotAssignedToBranch: Branching Activity missing missing. ActivityID was "+branchingActivityID; - log.error(error); - throw new MonitoringServiceException(error); - } + for (String groupIDString : groupIDs) { + Long groupID = Long.parseLong(groupIDString); - TreeSet unassignedGroups = new TreeSet(); - - Grouping grouping = branchingActivity.getGrouping(); + Group group = null; Iterator groupIterator = grouping.getGroups().iterator(); - while (groupIterator.hasNext() ) { - Group group = (Group) groupIterator.next(); - if ( group.getBranchActivities() == null || group.getBranchActivities().size()==0 ) { - unassignedGroups.add(group); + while (groupIterator.hasNext() && group == null) { + Group obj = (Group) groupIterator.next(); + if (obj.getGroupId().equals(groupID)) { + group = obj; } } - - return unassignedGroups; - + if (group == null) { + String error = "removeGroupFromBranch: Group missing. Group ID was " + groupIDString; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } + + // can't remove the group if someone has already started working on the branch. + if (isActivityAttempted(branch)) { + MonitoringService.log + .warn("removeGroupFromBranch: A group member has already started the branch. Unable to remove the group from the branch. Group ID was " + + groupIDString); + } + else { + branch.removeGroupFromBranch(group); + activityDAO.update(branch); + } } - /** - * Get the list of users who have attempted an activity. This is based on the progress engine records. - * This will give the users in all tool sessions for an activity (if it is a tool activity) or - * it will give all the users who have attempted an activity that doesn't have any tool sessions, i.e. - * system activities such as branching. - */ - public List getLearnersHaveAttemptedActivity(Activity activity) throws LessonServiceException { - return lessonService.getLearnersHaveAttemptedActivity(activity); + } + + /** Has anyone started this branch / branching activity ? Irrespective of the groups. + * Used to determine if a branch mapping can be removed. */ + public boolean isActivityAttempted(Activity activity) { + Integer numAttempted = lessonService.getCountLearnersHaveAttemptedActivity(activity); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("isActivityAttempted: num attempts for activity " + activity.getActivityId() + " is " + + numAttempted); } - - public LearnerProgress getLearnerProgress(Integer learnerId, Long lessonId) { - return learnerService.getProgress(learnerId, lessonId); + return numAttempted != null && numAttempted.intValue() > 0; + } + + /** + * Get all the groups that exist for the related grouping activity that have not been allocated to a branch. + * + * @param branchingActivityID Activity id of the branchingActivity + */ + public SortedSet getGroupsNotAssignedToBranch(Long branchingActivityID) throws LessonServiceException { + + BranchingActivity branchingActivity = (BranchingActivity) getActivityById(branchingActivityID); + if (branchingActivity == null) { + String error = "getGroupsNotAssignedToBranch: Branching Activity missing missing. ActivityID was " + + branchingActivityID; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); } + TreeSet unassignedGroups = new TreeSet(); + + Grouping grouping = branchingActivity.getGrouping(); + Iterator groupIterator = grouping.getGroups().iterator(); + while (groupIterator.hasNext()) { + Group group = (Group) groupIterator.next(); + if (group.getBranchActivities() == null || group.getBranchActivities().size() == 0) { + unassignedGroups.add(group); + } + } + + return unassignedGroups; + + } + + /** + * Get the list of users who have attempted an activity. This is based on the progress engine records. + * This will give the users in all tool sessions for an activity (if it is a tool activity) or + * it will give all the users who have attempted an activity that doesn't have any tool sessions, i.e. + * system activities such as branching. + */ + public List getLearnersHaveAttemptedActivity(Activity activity) throws LessonServiceException { + return lessonService.getLearnersHaveAttemptedActivity(activity); + } + + public LearnerProgress getLearnerProgress(Integer learnerId, Long lessonId) { + return learnerService.getProgress(learnerId, lessonId); + } + } \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateAction.java =================================================================== diff -u -rfdca3605f0b782b19e214abbe94df6f4a457b88e -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateAction.java (.../GateAction.java) (revision fdca3605f0b782b19e214abbe94df6f4a457b88e) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GateAction.java (.../GateAction.java) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -25,6 +25,8 @@ package org.lamsfoundation.lams.monitoring.web; import java.io.IOException; +import java.util.Collection; +import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -35,19 +37,16 @@ import org.apache.struts.action.ActionMapping; import org.apache.struts.action.DynaActionForm; import org.lamsfoundation.lams.learning.service.ICoreLearnerService; -import org.lamsfoundation.lams.learningdesign.Activity; import org.lamsfoundation.lams.learningdesign.GateActivity; -import org.lamsfoundation.lams.learningdesign.PermissionGateActivity; import org.lamsfoundation.lams.learningdesign.ScheduleGateActivity; -import org.lamsfoundation.lams.learningdesign.SynchGateActivity; 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.util.WebUtil; import org.lamsfoundation.lams.web.action.LamsDispatchAction; import org.lamsfoundation.lams.web.util.AttributeNames; - /** *

    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 @@ -81,201 +80,217 @@ * ----------------XDoclet Tags-------------------- * */ -public class GateAction extends LamsDispatchAction -{ - //--------------------------------------------------------------------- - // Instance variables - //--------------------------------------------------------------------- +public class GateAction extends LamsDispatchAction { + //--------------------------------------------------------------------- + // Instance variables + //--------------------------------------------------------------------- // private static Logger log = Logger.getLogger(GateAction.class); - + private IMonitoringService monitoringService; private ICoreLearnerService learnerService; - //--------------------------------------------------------------------- - // Class level constants - Struts forward - //--------------------------------------------------------------------- - private static final String VIEW_SYNCH_GATE = "viewSynchGate"; - private static final String VIEW_PERMISSION_GATE = "viewPermissionGate"; - private static final String VIEW_SCHEDULE_GATE="viewScheduleGate"; - - // Gate Form fields + //--------------------------------------------------------------------- + // Class level constants - Struts forward + //--------------------------------------------------------------------- + private static final String VIEW_SYNCH_GATE = "viewSynchGate"; + private static final String VIEW_PERMISSION_GATE = "viewPermissionGate"; + private static final String VIEW_SCHEDULE_GATE = "viewScheduleGate"; + + // Gate Form fields private static final String ACTIVITY_FORM_FIELD = "activityId"; private static final String TOTAL_LEARNERS_FORM_FIELD = "totalLearners"; - public static final String READ_ONLY= "readOnly"; - public static final String LOCAL_FILES= "localFiles"; + public static final String READ_ONLY = "readOnly"; + public static final String LOCAL_FILES = "localFiles"; + private static final String USER_ID = "userId"; - //--------------------------------------------------------------------- - // Struts Dispatch 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. - * - * - * @param mapping An ActionMapping class that will be used by the Action - * class to tell the ActionServlet where to send the end-user. - * - * @param form The ActionForm class that will contain any data submitted - * by the end-user via a form. - * @param request A standard Servlet HttpServletRequest class. - * @param response A standard Servlet HttpServletResponse class. - * @return An ActionForward class that will be returned to the ActionServlet - * indicating where the user is to go next. - * @throws IOException - * @throws ServletException - */ - public ActionForward viewGate(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) throws IOException, - ServletException - { - DynaActionForm gateForm = (DynaActionForm)form; - - // 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 = (Long)gateForm.get(ACTIVITY_FORM_FIELD); - } - long gateId = gateIdLong != null ? gateIdLong.longValue() : -1 ; - - this.monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - this.learnerService = MonitoringServiceProxy.getLearnerService(getServlet().getServletContext()); - - GateActivity gate = (GateActivity) monitoringService.getActivityById(gateId); - - if ( gate == null ) { - throw new MonitoringServiceException("Gate activity missing. Activity id"+gateId); - } - - //setup the total learners - int totalLearners = learnerService.getLearnersForGate(gate).size(); - gateForm.set(TOTAL_LEARNERS_FORM_FIELD,new Integer(totalLearners)); - gateForm.set(ACTIVITY_FORM_FIELD,gateIdLong); - gateForm.set(LOCAL_FILES, Boolean.FALSE); + //--------------------------------------------------------------------- + // Struts Dispatch 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. + * + * + * @param mapping An ActionMapping class that will be used by the Action + * class to tell the ActionServlet where to send the end-user. + * + * @param form The ActionForm class that will contain any data submitted + * by the end-user via a form. + * @param request A standard Servlet HttpServletRequest class. + * @param response A standard Servlet HttpServletResponse class. + * @return An ActionForward class that will be returned to the ActionServlet + * indicating where the user is to go next. + * @throws IOException + * @throws ServletException + */ + public ActionForward viewGate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + DynaActionForm gateForm = (DynaActionForm) form; - - return findViewByGateType(mapping, gateForm, gate); - } - /** - * Open the gate if is closed. - * - * @param mapping An ActionMapping class that will be used by the Action - * class to tell the ActionServlet where to send the end-user. - * - * @param form The ActionForm class that will contain any data submitted - * by the end-user via a form. - * @param request A standard Servlet HttpServletRequest class. - * @param response A standard Servlet HttpServletResponse class. - * @return An ActionForward class that will be returned to the ActionServlet - * indicating where the user is to go next. - * @throws IOException - * @throws ServletException - */ - public ActionForward openGate(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) throws IOException, - ServletException - { - this.monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); + // 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 = (Long) gateForm.get(GateAction.ACTIVITY_FORM_FIELD); + } + long gateId = gateIdLong != null ? gateIdLong.longValue() : -1; - DynaActionForm gateForm = (DynaActionForm)form; - - GateActivity gate = monitoringService.openGate((Long)gateForm.get(ACTIVITY_FORM_FIELD)); + monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); + learnerService = MonitoringServiceProxy.getLearnerService(getServlet().getServletContext()); - return findViewByGateType(mapping, gateForm, gate); - } - - /** - * Export Portfolio Page - */ - public ActionForward exportPortfolio(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) throws IOException, - ServletException - { - DynaActionForm gateForm = (DynaActionForm)form; - ActionForward forward = viewGate(mapping, form, request, response); - gateForm.set(READ_ONLY, Boolean.TRUE); - gateForm.set(LOCAL_FILES, Boolean.TRUE); - return forward; - } - + GateActivity gate = (GateActivity) monitoringService.getActivityById(gateId); - //--------------------------------------------------------------------- - // Helper Methods - //--------------------------------------------------------------------- - /** - * Dispatch view the according to the gate type. - * - * @param mapping An ActionMapping class that will be used by the Action - * class to tell the ActionServlet where to send the end-user. - * @param gateForm The ActionForm class that will contain any data submitted - * by the end-user via a form. - * @param permissionGate the gate acitivty object - * @return An ActionForward class that will be returned to the ActionServlet - * indicating where the user is to go next. - */ - private ActionForward findViewByGateType(ActionMapping mapping, - DynaActionForm gateForm, - GateActivity gate) - { - // reset all the other fields, so that the following code only has to set up its own values (LDEV-1237) - gateForm.set(READ_ONLY, Boolean.FALSE); - gateForm.set("gate",null); - gateForm.set("waitingLearners",null); - gateForm.set("startingTime",null); - gateForm.set("endingTime",null); - gateForm.set("gate",gate); - gateForm.set("waitingLearners",new Integer(gate.getWaitingLearners().size())); + if (gate == null) { + throw new MonitoringServiceException("Gate activity missing. Activity id" + gateId); + } - //dispatch the view according to the type of the gate. - if(gate.isSynchGate()) - return mapping.findForward(VIEW_SYNCH_GATE); - else if(gate.isScheduleGate()) - return viewScheduleGate(mapping,gateForm,(ScheduleGateActivity)gate); - else if(gate.isPermissionGate() || gate.isSystemGate()) - return mapping.findForward(VIEW_PERMISSION_GATE); - else - throw new MonitoringServiceException("Invalid gate activity. " + - "gate id ["+gate.getActivityId()+"] - the type ["+ - gate.getActivityTypeId()+"] is not a gate type"); - } + //setup the total learners + int totalLearners = learnerService.getLearnersForGate(gate).size(); + gateForm.set(GateAction.TOTAL_LEARNERS_FORM_FIELD, new Integer(totalLearners)); + gateForm.set(GateAction.ACTIVITY_FORM_FIELD, gateIdLong); + gateForm.set(GateAction.LOCAL_FILES, Boolean.FALSE); + return findViewByGateType(mapping, gateForm, gate); + } + /** + * Open the gate if is closed. + * + * @param mapping An ActionMapping class that will be used by the Action + * class to tell the ActionServlet where to send the end-user. + * + * @param form The ActionForm class that will contain any data submitted + * by the end-user via a form. + * @param request A standard Servlet HttpServletRequest class. + * @param response A standard Servlet HttpServletResponse class. + * @return An ActionForward class that will be returned to the ActionServlet + * indicating where the user is to go next. + * @throws IOException + * @throws ServletException + */ + public ActionForward openGate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - /** - * Set up the form attributes specific to the schedule gate and navigate - * to the schedule gate view. - * - * @param mapping An ActionMapping class that will be used by the Action - * class to tell the ActionServlet where to send the end-user. - * @param gateForm The ActionForm class that will contain any data submitted - * by the end-user via a form. - * @param permissionGate the gate acitivty object - * @return An ActionForward class that will be returned to the ActionServlet - * indicating where the user is to go next. - */ - private ActionForward viewScheduleGate(ActionMapping mapping, - DynaActionForm gateForm, - ScheduleGateActivity scheduleGate) - { - gateForm.set("startingTime",scheduleGate.getGateStartDateTime()); - gateForm.set("endingTime",scheduleGate.getGateEndDateTime()); - return mapping.findForward(VIEW_SCHEDULE_GATE); - } + DynaActionForm gateForm = (DynaActionForm) form; + GateActivity gate = monitoringService.openGate((Long) gateForm.get(GateAction.ACTIVITY_FORM_FIELD)); + + return findViewByGateType(mapping, gateForm, gate); + } + + /** + * Allows a single learner to pass the gate. + * @param mapping An ActionMapping class that will be used by the Action + * class to tell the ActionServlet where to send the end-user. + * @param form he ActionForm class that will contain any data submitted + * by the end-user via a form. + * @param request A standard Servlet HttpServletRequest class. + * @param response A standard Servlet HttpServletRequest class. + * @return An ActionForward class that will be returned to the ActionServlet + * indicating where the user is to go next. + * @throws IOException + * @throws ServletException + */ + public ActionForward openGateForSingleUser(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); + + DynaActionForm gateForm = (DynaActionForm) form; + Long gateIdLong = (Long) gateForm.get(GateAction.ACTIVITY_FORM_FIELD); + Integer userId = (Integer) gateForm.get(GateAction.USER_ID); + GateActivity gate = monitoringService.openGateForSingleUser(gateIdLong, userId); + return findViewByGateType(mapping, gateForm, gate); + } + + /** + * Export Portfolio Page + */ + public ActionForward exportPortfolio(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + DynaActionForm gateForm = (DynaActionForm) form; + ActionForward forward = viewGate(mapping, form, request, response); + gateForm.set(GateAction.READ_ONLY, Boolean.TRUE); + gateForm.set(GateAction.LOCAL_FILES, Boolean.TRUE); + return forward; + } + + //--------------------------------------------------------------------- + // Helper Methods + //--------------------------------------------------------------------- + /** + * Dispatch view the according to the gate type. + * + * @param mapping An ActionMapping class that will be used by the Action + * class to tell the ActionServlet where to send the end-user. + * @param gateForm The ActionForm class that will contain any data submitted + * by the end-user via a form. + * @param permissionGate the gate acitivty object + * @return An ActionForward class that will be returned to the ActionServlet + * indicating where the user is to go next. + */ + private ActionForward findViewByGateType(ActionMapping mapping, DynaActionForm gateForm, GateActivity gate) { + // reset all the other fields, so that the following code only has to set up its own values (LDEV-1237) + gateForm.set(GateAction.READ_ONLY, Boolean.FALSE); + gateForm.set("gate", null); + gateForm.set("waitingLearners", null); + gateForm.set("waitingLearnerList", null); + gateForm.set("allowedToPassLearnerList", null); + gateForm.set("forbiddenLearnerList", null); + gateForm.set("startingTime", null); + gateForm.set("endingTime", null); + + Set waitingLearnersList = gate.getWaitingLearners(); + gateForm.set("gate", gate); + gateForm.set("waitingLearners", new Integer(waitingLearnersList.size())); + + //dispatch the view according to the type of the gate. + if (gate.isSynchGate()) { + return mapping.findForward(GateAction.VIEW_SYNCH_GATE); + } + else if (gate.isScheduleGate()) { + return viewScheduleGate(mapping, gateForm, (ScheduleGateActivity) gate); + } + else if (gate.isPermissionGate() || gate.isSystemGate()) { + gateForm.set("waitingLearnerList", waitingLearnersList); + gateForm.set("allowedToPassLearnerList", gate.getAllowedToPassLearners()); + Collection forbiddenUsers = learnerService.getLearnersForGate(gate); + forbiddenUsers.removeAll(gate.getAllowedToPassLearners()); + gateForm.set("forbiddenLearnerList", forbiddenUsers); + return mapping.findForward(GateAction.VIEW_PERMISSION_GATE); + } + 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. + * + * @param mapping An ActionMapping class that will be used by the Action + * class to tell the ActionServlet where to send the end-user. + * @param gateForm The ActionForm class that will contain any data submitted + * by the end-user via a form. + * @param permissionGate the gate acitivty object + * @return An ActionForward class that will be returned to the ActionServlet + * indicating where the user is to go next. + */ + private ActionForward viewScheduleGate(ActionMapping mapping, DynaActionForm gateForm, ScheduleGateActivity scheduleGate) { + gateForm.set("startingTime", scheduleGate.getGateStartDateTime()); + gateForm.set("endingTime", scheduleGate.getGateEndDateTime()); + return mapping.findForward(GateAction.VIEW_SCHEDULE_GATE); + } + } Index: lams_monitoring/web/gate/permissionGateContent.jsp =================================================================== diff -u -r614080cb0cdefaf39c8453eeaa7977e17d2b0882 -r418a30a76094c56762b5beb23cb2dd72619e316c --- lams_monitoring/web/gate/permissionGateContent.jsp (.../permissionGateContent.jsp) (revision 614080cb0cdefaf39c8453eeaa7977e17d2b0882) +++ lams_monitoring/web/gate/permissionGateContent.jsp (.../permissionGateContent.jsp) (revision 418a30a76094c56762b5beb23cb2dd72619e316c) @@ -24,20 +24,79 @@ <%@ taglib uri="tags-logic" prefix="logic" %> <%@ taglib uri="tags-core" prefix="c" %> <%@ taglib uri="tags-fmt" prefix="fmt" %> - +

    <%@ include file="gateInfo.jsp" %> + -

    +

    -

    + <%@ include file="gateStatus.jsp" %> - -
    - + +
    +

    + + + + + + + + + + + + + + + + + + + +
    + + + + + +
    + " onclick="document.pressed='forbidden'"/> + + " onclick="document.pressed='waiting'"/> + +   +
    +
    +
    + \ No newline at end of file