Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java =================================================================== RCS file: /usr/local/cvsroot/lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java,v diff -u -r1.78 -r1.79 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java 5 Nov 2008 04:05:37 -0000 1.78 +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java 5 Nov 2008 05:56:41 -0000 1.79 @@ -46,600 +46,762 @@ /** * Interface defines all monitoring services needed by presentation tier. + * * @author Jacky Fang 2/02/2005 * @author Manpreet Minhas */ public interface IMonitoringService { - /** Get the message service, which gives access to the I18N text */ - public MessageService getMessageService(); + /** 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, Boolean learnerPresenceAvailable, Boolean learnerImAvailable); + /** + * 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, + Boolean learnerPresenceAvailable, Boolean learnerImAvailable); - /** - * 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; + /** + * 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, Boolean learnerPresenceAvailable, Boolean learnerImAvailable); + /** + * 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, Boolean learnerPresenceAvailable, Boolean learnerImAvailable); - /** - * Create a lession according to the input lession WDDX package. The sample package is following: - * - *
-	 * 
- * 135 - * 10 - * 78 - * 12 - * 3456 - * - *
+ /** + * Create a lession according to the input lession WDDX package. The sample + * package is following: + *
+     * 
+ * 135 + * 10 + * 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; + *

+ * 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; + /** + * 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; + /** + * 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); + /** + * 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); + /** + *

    + * 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; + /** + * 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 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; + /** + * 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); + /** + * 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); + /** + * 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; + /** + * 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); + /** + * 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; + /** + * 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; + /** + * 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; + /** + *

    + * 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 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); - /** - * Allows a single learner to pass the gate. - * @param gateId - * @param userId - * @return - */ - public GateActivity openGateForSingleUser(Long gateId, Integer userId); + /** + * Allows a single learner to pass the gate. + * + * @param gateId + * @param userId + * @return + */ + public GateActivity openGateForSingleUser(Long gateId, Integer userId); - /** - * Set the gate to closed. - * @param gate the id of the gate we need to close. - */ - public GateActivity closeGate(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; + /** + * 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 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; + /** + * 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 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 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 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 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 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 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 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 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 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; + /** + * 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. + * + * @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. 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); + /** + * 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 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; + /** + * 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; + /** + * 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 - //--------------------------------------------------------------------- - /** - * Create the lesson class and the staff class for a preview lesson. - * The lesson is not attached to any organisation. - * - * @param userID User ID of the teacher running the preview. Mandatory. - * @param lessonID ID of the lesson - * @return Lesson - */ - public abstract Lesson createPreviewClassForLesson(int userID, long lessonID) throws UserAccessDeniedException; + // --------------------------------------------------------------------- + // Preview Methods + // --------------------------------------------------------------------- + /** + * Create the lesson class and the staff class for a preview lesson. The + * lesson is not attached to any organisation. + * + * @param userID + * User ID of the teacher running the preview. Mandatory. + * @param lessonID + * ID of the lesson + * @return Lesson + */ + public abstract Lesson createPreviewClassForLesson(int userID, long lessonID) throws UserAccessDeniedException; - /** - * Remove all the details for a particular preview lessons. The transaction - * handling for this method should be REQUIRES_NEW, which allows - * each lesson to be deleted separately. - * - * @param lessonID ID of the lesson which is the preview session. Mandatory. - */ - public abstract void deletePreviewLesson(long lessonID); + /** + * Remove all the details for a particular preview lessons. The transaction + * handling for this method should be REQUIRES_NEW, which allows each lesson + * to be deleted separately. + * + * @param lessonID + * ID of the lesson which is the preview session. Mandatory. + */ + public abstract void deletePreviewLesson(long lessonID); - /** - * Remove all the "old" preview lessons. Removes all preview lessons older than - * the number of days specified in the configuration file. - *

    - * Calls deletePreviewLesson(long lessonID) to do the actual deletion, so - * if one lesson throws a database exception when deleting, the other lessons - * should delete okay (as deletePreviewLesson uses a REQUIRES_NEW transaction) - * - * @return number of lessons deleted. - */ - public abstract int deleteAllOldPreviewLessons(); + /** + * Remove all the "old" preview lessons. Removes all preview lessons older + * than the number of days specified in the configuration file. + *

    + * Calls deletePreviewLesson(long lessonID) to do the actual deletion, so if + * one lesson throws a database exception when deleting, the other lessons + * should delete okay (as deletePreviewLesson uses a REQUIRES_NEW + * transaction) + * + * @return number of lessons deleted. + */ + 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); + /* 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); - /** 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 name group name - * @throws LessonServiceException - */ - public abstract void addGroup(Long activityID, String name, boolean overrideMaxNumberOfGroups) throws LessonServiceException, - MonitoringServiceException; + /** + * 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 name + * group name + * @throws LessonServiceException + */ + 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 name group name - * @throws LessonServiceException - **/ - public abstract void removeGroup(Long activityID, Long groupID) throws LessonServiceException; + /** + * 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 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; + /** + * 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; - /** Remove a user to a group. If the user is not in the group, then nothing is changed. - * @throws LessonServiceException */ - public abstract void removeUsersFromGroup(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 + */ + public abstract void removeUsersFromGroup(Long activityID, Long groupID, 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; + /** + * 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; - /** 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; + /** + * 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; - /** 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; + /** + * 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; + /** + * 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; - /** 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; + /** + * 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; - /** - * 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; + /** + * 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; - /** - * 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; + /** + * 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; - /** Get the record of the learner's progress for a particular lesson */ - public LearnerProgress getLearnerProgress(Integer learnerId, Long lessonId); + /** Get the record of the learner's progress for a particular lesson */ + public LearnerProgress getLearnerProgress(Integer learnerId, Long lessonId); - /** - * Start multiple lessons in one call. - */ - public boolean startLessons(Integer creatorUserId, String lessonPacket) throws Exception; + /** + * Start multiple lessons in one call. + */ + public boolean startLessons(Integer creatorUserId, String lessonPacket) throws Exception; } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== RCS file: /usr/local/cvsroot/lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java,v diff -u -r1.137 -r1.138 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java 5 Nov 2008 04:05:37 -0000 1.137 +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java 5 Nov 2008 05:56:41 -0000 1.138 @@ -106,2317 +106,2494 @@ import org.springframework.context.ApplicationContextAware; /** - *

    This is the major service facade for all monitoring functionalities. It is + *

    + * 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 - * declarative transaction management.

    - *

    It needs to implement ApplicationContextAware interface + * declarative transaction management. + *

    + *

    + * It needs to implement ApplicationContextAware interface * because we need to load up tool's service dynamically according to the - * selected learning design.

    + * selected learning design. + *

    * - * TODO Analyse the efficiency of the grouping algorithms for adding/removing users. Possible performance issue. + * TODO Analyse the efficiency of the grouping algorithms for adding/removing + * users. Possible performance issue. * - * @author Jacky Fang + * @author Jacky Fang * @author Manpreet Minhas * @since 2/02/2005 * @version 1.1 */ public class MonitoringService implements IMonitoringService, ApplicationContextAware { - //--------------------------------------------------------------------- - // 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; + // --------------------------------------------------------------------- + // Instance variables + // --------------------------------------------------------------------- + private static Logger log = Logger.getLogger(MonitoringService.class); - 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; + private static final long numMilliSecondsInADay = 24 * 60 * 60 * 1000; - /** 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 String AUDIT_LEARNER_PORTFOLIO_SET = "audit.learner.portfolio.set"; - //--------------------------------------------------------------------- - // Inversion of Control Methods - Method injection - //--------------------------------------------------------------------- - /** - * @param messageService the i18n Service bean. - */ - public void setMessageService(MessageService messageService) { - this.messageService = messageService; - } + private static final String AUDIT_LESSON_CREATED_KEY = "audit.lesson.created"; - public MessageService getMessageService() { - return messageService; - } + private static final int DEFAULT_LEARNER_PROGRESS_BATCH_SIZE = 15; - /** - * @param userManagementService The userManagementService to set. - */ - public void setUserManagementService(IUserManagementService userManagementService) { - this.userManagementService = userManagementService; - } + private ILessonDAO lessonDAO; - /** - * @param learningDesignDAO The learningDesignDAO to set. - */ - public void setLearningDesignDAO(ILearningDesignDAO learningDesignDAO) { - this.learningDesignDAO = learningDesignDAO; - } + private ILessonClassDAO lessonClassDAO; - /** - * @param transitionDAO The transitionDAO to set. - */ - public void setTransitionDAO(ITransitionDAO transitionDAO) { - this.transitionDAO = transitionDAO; - } + private ITransitionDAO transitionDAO; - /** - * - * @param learnerService - */ - public void setLearnerService(ICoreLearnerService learnerService) { - this.learnerService = learnerService; - } + private IActivityDAO activityDAO; - /** - * - * @param lessonService - */ - public void setLessonService(ILessonService lessonService) { - this.lessonService = lessonService; - } + private IBaseDAO baseDAO; - /** - * @param authoringService The authoringService to set. - */ - public void setAuthoringService(IAuthoringService authoringService) { - this.authoringService = authoringService; - } + private ILearningDesignDAO learningDesignDAO; - /** - * @param lessonClassDAO The lessonClassDAO to set. - */ - public void setLessonClassDAO(ILessonClassDAO lessonClassDAO) { - this.lessonClassDAO = lessonClassDAO; - } + private IGroupingDAO groupingDAO; - /** - * @param lessonDAO The lessonDAO to set. - */ - public void setLessonDAO(ILessonDAO lessonDAO) { - this.lessonDAO = lessonDAO; - } + private ILearnerProgressDAO learnerProgressDAO; - /** - * @param learnerProgressDAO The learnerProgressDAO to set. - */ - public void setLearnerProgressDAO(ILearnerProgressDAO learnerProgressDAO) { - this.learnerProgressDAO = learnerProgressDAO; - } + private IAuthoringService authoringService; - /** - * @param userDAO - */ - public void setBaseDAO(IBaseDAO baseDAO) { - this.baseDAO = baseDAO; - } + private ICoreLearnerService learnerService; - /** - * @param groupingDAO - */ - public void setGroupingDAO(IGroupingDAO groupingDAO) { - this.groupingDAO = groupingDAO; - } + private ILessonService lessonService; - /** - * @param lamsToolService The lamsToolService to set. - */ - public void setLamsCoreToolService(ILamsCoreToolService lamsToolService) { - lamsCoreToolService = lamsToolService; - } + private ILamsCoreToolService lamsCoreToolService; - /** - * @param activityDAO The activityDAO to set. - */ - public void setActivityDAO(IActivityDAO activityDAO) { - this.activityDAO = activityDAO; - } + private IUserManagementService userManagementService; - /** - * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) - */ - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } + private Scheduler scheduler; - /** - * @param scheduler The scheduler to set. - */ - public void setScheduler(Scheduler scheduler) { - this.scheduler = scheduler; - } + private ApplicationContext applicationContext; - public void setAuditService(AuditService auditService) { - this.auditService = auditService; - } + private MessageService messageService; - //--------------------------------------------------------------------- - // Service Methods - //--------------------------------------------------------------------- + private AuditService auditService; - private void auditAction(String messageKey, Object[] args) { - String message = messageService.getMessage(messageKey, args); - auditService.log("Monitoring", message); - } + /** Message keys */ + private static final String FORCE_COMPLETE_STOP_MESSAGE_ACTIVITY_DONE = "force.complete.stop.message.activity.done"; - /** 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); + private static final String FORCE_COMPLETE_STOP_MESSAGE_GROUPING_ERROR = "force.complete.stop.message.grouping.error"; - if (lesson.getUser() != null && lesson.getUser().getUserId().equals(userId)) { - return; - } + private static final String FORCE_COMPLETE_STOP_MESSAGE_GROUPING = "force.complete.stop.message.grouping"; - if (lesson == null || lesson.getLessonClass() == null || !lesson.getLessonClass().isStaffMember(user)) { - throw new UserAccessDeniedException("User " + userId + " may not " + actionDescription + " for lesson " - + lesson.getLessonId()); - } - } + private static final String FORCE_COMPLETE_STOP_MESSAGE_GATE = "force.complete.stop.message.gate"; - /** - *

    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, Boolean learnerPresenceAvailable, Boolean learnerImAvailable) { + private static final String FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_ACTIVITY = "force.complete.stop.message.completed.to.activity"; - LearningDesign originalLearningDesign = authoringService.getLearningDesign(new Long(learningDesignId)); - if (originalLearningDesign == null) { - throw new MonitoringServiceException("Learning design for id=" + learningDesignId - + " is missing. Unable to initialize lesson."); - } + private static final String FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_END = "force.complete.stop.message.completed.to.end"; - // 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; - } - } - } + private static final String FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY = "force.complete.stop.message.stopped.unexpectedly"; - User user = userID != null ? (User) baseDAO.find(User.class, userID) : null; - return initializeLesson(lessonName, lessonDescription, learnerExportAvailable, originalLearningDesign, user, - runSeqFolder, LearningDesign.COPY_TYPE_LESSON, customCSV, learnerPresenceAvailable, learnerImAvailable); + // --------------------------------------------------------------------- + // 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; + } - /** - * 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, Boolean learnerPresenceAvailable, Boolean learnerImAvailable) { - 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; + /** + * @param userManagementService + * The userManagementService to set. + */ + public void setUserManagementService(IUserManagementService userManagementService) { + this.userManagementService = userManagementService; + } - return initializeLesson(lessonName, lessonDescription, Boolean.TRUE, originalLearningDesign, user, null, - LearningDesign.COPY_TYPE_PREVIEW, customCSV, learnerPresenceAvailable, learnerImAvailable); - } + /** + * @param learningDesignDAO + * The learningDesignDAO to set. + */ + public void setLearningDesignDAO(ILearningDesignDAO learningDesignDAO) { + this.learningDesignDAO = learningDesignDAO; + } - public Lesson initializeLesson(String lessonName, String lessonDescription, Boolean learnerExportAvailable, - LearningDesign originalLearningDesign, User user, WorkspaceFolder workspaceFolder, int copyType, String customCSV, Boolean learnerPresenceAvailable, Boolean learnerImAvailable) { + /** + * @param transitionDAO + * The transitionDAO to set. + */ + public void setTransitionDAO(ITransitionDAO transitionDAO) { + this.transitionDAO = transitionDAO; + } - //copy the current learning design - LearningDesign copiedLearningDesign = authoringService.copyLearningDesign(originalLearningDesign, new Integer(copyType), - user, workspaceFolder, true, null, customCSV); - authoringService.saveLearningDesign(copiedLearningDesign); + /** + * + * @param learnerService + */ + public void setLearnerService(ICoreLearnerService learnerService) { + this.learnerService = learnerService; + } - // Make all efforts to make sure it has a title - String title = lessonName != null ? lessonName : copiedLearningDesign.getTitle(); - title = title != null ? title : "Unknown Lesson"; + /** + * + * @param lessonService + */ + public void setLessonService(ILessonService lessonService) { + this.lessonService = lessonService; + } - Lesson lesson = createNewLesson(title, lessonDescription, user, learnerExportAvailable, copiedLearningDesign, learnerPresenceAvailable, learnerImAvailable); - auditAction(MonitoringService.AUDIT_LESSON_CREATED_KEY, new Object[] { lessonName, copiedLearningDesign.getTitle(), - learnerExportAvailable }); - return lesson; - } + /** + * @param authoringService + * The authoringService to set. + */ + public void setAuthoringService(IAuthoringService authoringService) { + this.authoringService = authoringService; + } - /** - * @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; + /** + * @param lessonClassDAO + * The lessonClassDAO to set. + */ + public void setLessonClassDAO(ILessonClassDAO lessonClassDAO) { + this.lessonClassDAO = lessonClassDAO; + } - try { - Hashtable table = (Hashtable) WDDXProcessor.deserialize(lessonPacket); + /** + * @param lessonDAO + * The lessonDAO to set. + */ + public void setLessonDAO(ILessonDAO lessonDAO) { + this.lessonDAO = lessonDAO; + } - // parse WDDX values + /** + * @param learnerProgressDAO + * The learnerProgressDAO to set. + */ + public void setLearnerProgressDAO(ILearnerProgressDAO learnerProgressDAO) { + this.learnerProgressDAO = learnerProgressDAO; + } - 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")); - boolean learnerPresenceAvailable = WDDXProcessor.convertToBoolean("enablePresence", table - .get("enablePresence")); - boolean learnerImAvailable = WDDXProcessor.convertToBoolean("enableIm", table - .get("enableIm")); - String customCSV = WDDXProcessor.convertToString(WDDXTAGS.CUSTOM_CSV, table.get(WDDXTAGS.CUSTOM_CSV)); + /** + * @param userDAO + */ + public void setBaseDAO(IBaseDAO baseDAO) { + this.baseDAO = baseDAO; + } - // initialize lesson + /** + * @param groupingDAO + */ + public void setGroupingDAO(IGroupingDAO groupingDAO) { + this.groupingDAO = groupingDAO; + } - Lesson newLesson = null; + /** + * @param lamsToolService + * The lamsToolService to set. + */ + public void setLamsCoreToolService(ILamsCoreToolService lamsToolService) { + lamsCoreToolService = lamsToolService; + } - if (copyType == LearningDesign.COPY_TYPE_PREVIEW) { - newLesson = initializeLessonForPreview(title, desc, ldId, creatorUserId, customCSV, learnerPresenceAvailable, learnerImAvailable); - } - else { - newLesson = initializeLesson(title, desc, learnerExportAvailable, ldId, organisationId, creatorUserId, customCSV, learnerPresenceAvailable, learnerImAvailable); - } + /** + * @param activityDAO + * The activityDAO to set. + */ + public void setActivityDAO(IActivityDAO activityDAO) { + this.activityDAO = activityDAO; + } - if (newLesson != null) { - flashMessage = new FlashMessage("initializeLesson", newLesson.getLessonId()); - } + /** + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } - return flashMessage.serializeMessage(); + /** + * @param scheduler + * The scheduler to set. + */ + public void setScheduler(Scheduler scheduler) { + this.scheduler = scheduler; + } - } - catch (Exception e) { - MonitoringService.log.error("Exception occured trying to create a lesson class ", e); - throw new Exception(e); - } + public void setAuditService(AuditService auditService) { + this.auditService = auditService; + } - } + // --------------------------------------------------------------------- + // Service Methods + // --------------------------------------------------------------------- - /** - * @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; + private void auditAction(String messageKey, Object[] args) { + String message = messageService.getMessage(messageKey, args); + auditService.log("Monitoring", message); + } - 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); + /** + * 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 (learners == null) { - learners = new LinkedList(); - } - if (staffs == null) { - staffs = new LinkedList(); - } + if (lesson.getUser() != null && lesson.getUser().getUserId().equals(userId)) { + return; + } - Organisation organisation = (Organisation) baseDAO.find(Organisation.class, orgId); - User creator = (User) baseDAO.find(User.class, creatorUserId); + if (lesson == null || lesson.getLessonClass() == null || !lesson.getLessonClass().isStaffMember(user)) { + throw new UserAccessDeniedException("User " + userId + " may not " + actionDescription + " for lesson " + + lesson.getLessonId()); + } + } - // 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) { - MonitoringService.log.error("Error parsing staff ID from " + lessonPacket); - continue; - } - } + /** + *

    + * 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, + Boolean learnerPresenceAvailable, Boolean learnerImAvailable) { - //Create Lesson class - createLessonClassForLesson(lessonId, organisation, learnerGroupName, learnerList, staffGroupName, staffList, - creatorUserId); + LearningDesign originalLearningDesign = authoringService.getLearningDesign(new Long(learningDesignId)); + if (originalLearningDesign == null) { + throw new MonitoringServiceException("Learning design for id=" + learningDesignId + + " is missing. Unable to initialize lesson."); + } - flashMessage = new FlashMessage("createLesson", Boolean.TRUE); + // 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; } - 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) { - MonitoringService.log.error(message); - } + User user = userID != null ? (User) baseDAO.find(User.class, userID) : null; + return initializeLesson(lessonName, lessonDescription, learnerExportAvailable, originalLearningDesign, user, + runSeqFolder, LearningDesign.COPY_TYPE_LESSON, customCSV, learnerPresenceAvailable, learnerImAvailable); - return message; + } + + /** + * 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, Boolean learnerPresenceAvailable, Boolean learnerImAvailable) { + 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; - /** - *

    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"); + return initializeLesson(lessonName, lessonDescription, Boolean.TRUE, originalLearningDesign, user, null, + LearningDesign.COPY_TYPE_PREVIEW, customCSV, learnerPresenceAvailable, learnerImAvailable); + } - // if lesson isn't started, can add and remove users, so its just easier to recreate the lesson class - if (!newLesson.isLessonStarted()) { + public Lesson initializeLesson(String lessonName, String lessonDescription, Boolean learnerExportAvailable, + LearningDesign originalLearningDesign, User user, WorkspaceFolder workspaceFolder, int copyType, + String customCSV, Boolean learnerPresenceAvailable, Boolean learnerImAvailable) { - if (newLesson == null) { - throw new MonitoringServiceException("Lesson for id=" + lessonId - + " is missing. Unable to create class for lesson."); - } + // copy the current learning design + LearningDesign copiedLearningDesign = authoringService.copyLearningDesign(originalLearningDesign, new Integer( + copyType), user, workspaceFolder, true, null, customCSV); + authoringService.saveLearningDesign(copiedLearningDesign); - LessonClass oldLessonClass = newLesson.getLessonClass(); + // Make all efforts to make sure it has a title + String title = lessonName != null ? lessonName : copiedLearningDesign.getTitle(); + title = title != null ? title : "Unknown Lesson"; - LessonClass newLessonClass = this.createLessonClass(organisation, learnerGroupName, organizationUsers, - staffGroupName, staffs, newLesson); - newLessonClass.setLesson(newLesson); - newLesson.setLessonClass(newLessonClass); - newLesson.setOrganisation(organisation); + Lesson lesson = createNewLesson(title, lessonDescription, user, learnerExportAvailable, copiedLearningDesign, + learnerPresenceAvailable, learnerImAvailable); + auditAction(MonitoringService.AUDIT_LESSON_CREATED_KEY, new Object[] { lessonName, + copiedLearningDesign.getTitle(), learnerExportAvailable }); + return lesson; + } - lessonDAO.updateLesson(newLesson); + /** + * @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; - if (oldLessonClass != null) { - lessonClassDAO.deleteLessonClass(oldLessonClass); - } + try { + Hashtable table = (Hashtable) WDDXProcessor.deserialize(lessonPacket); - } - else { // if started, can only add users - lessonService.addLearners(newLesson, organizationUsers); - lessonService.addStaffMembers(newLesson, staffs); - } + // parse WDDX values - return newLesson; - } + 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")); + boolean learnerPresenceAvailable = WDDXProcessor.convertToBoolean("enablePresence", table + .get("enablePresence")); + boolean learnerImAvailable = WDDXProcessor.convertToBoolean("enableIm", table.get("enableIm")); + String customCSV = WDDXProcessor.convertToString(WDDXTAGS.CUSTOM_CSV, table.get(WDDXTAGS.CUSTOM_CSV)); - /** - * 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) { + // initialize 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."); - } - checkOwnerOrStaffMember(userId, requestedLesson, "start lesson on schedule"); + Lesson newLesson = null; - 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 (copyType == LearningDesign.COPY_TYPE_PREVIEW) { + newLesson = initializeLessonForPreview(title, desc, ldId, creatorUserId, customCSV, + learnerPresenceAvailable, learnerImAvailable); + } else { + newLesson = initializeLesson(title, desc, learnerExportAvailable, ldId, organisationId, creatorUserId, + customCSV, learnerPresenceAvailable, learnerImAvailable); + } - if (requestedLesson.getScheduleStartDate() != null) { - // can't reschedule! - MonitoringService.log.error("Lesson for id=" + lessonId + " is already scheduled and cannot be rescheduled."); - return; - } + if (newLesson != null) { + flashMessage = new FlashMessage("initializeLesson", newLesson.getLessonId()); + } - JobDetail startLessonJob = getStartScheduleLessonJob(); - //setup the message for scheduling job - startLessonJob.setName("startLessonOnSchedule:" + lessonId); + return flashMessage.serializeMessage(); - 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)); + } catch (Exception e) { + MonitoringService.log.error("Exception occured trying to create a lesson class ", e); + throw new Exception(e); + } - //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"); - } - } + /** + * @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; - /** - * 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"); + 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); - 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 + 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 { - requestedLesson.setScheduleEndDate(endDate); - scheduler.scheduleJob(finishLessonJob, finishLessonTrigger); + 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; } - catch (SchedulerException e) { - throw new MonitoringServiceException("Error occurred at " + "[finishLessonOnSchedule]- fail to start scheduling", e); + } + // 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; } + } - if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("Finish lesson [" + lessonId + "] on schedule is configured"); - } + // 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); } - /** - * @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 + "=============="); - } + String message = "Failed on creating flash message:" + flashMessage; + try { + message = flashMessage.serializeMessage(); + } catch (IOException e) { + MonitoringService.log.error(message); + } - //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."); - } + return message; + } - 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; - } + /** + *

    + * 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"); - checkOwnerOrStaffMember(userId, requestedLesson, "create lesson class"); + // if lesson isn't started, can add and remove users, so its just easier + // to recreate the lesson class + if (!newLesson.isLessonStarted()) { - 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); - } + if (newLesson == null) { + throw new MonitoringServiceException("Lesson for id=" + lessonId + + " is missing. Unable to create class for lesson."); + } - Integer newMaxId = startSystemActivity(activity, design.getMaxID(), lessonStartTime, requestedLesson.getLessonName()); - if (newMaxId != null) { - design.setMaxID(newMaxId); - designModified = true; - } + LessonClass oldLessonClass = newLesson.getLessonClass(); - activity.setInitialised(Boolean.TRUE); - activityDAO.update(activity); + LessonClass newLessonClass = this.createLessonClass(organisation, learnerGroupName, organizationUsers, + staffGroupName, staffs, newLesson); + newLessonClass.setLesson(newLesson); + newLesson.setLessonClass(newLessonClass); + newLesson.setOrganisation(organisation); - } + lessonDAO.updateLesson(newLesson); - if (designModified) { - learningDesignDAO.update(design); - } + if (oldLessonClass != null) { + lessonClassDAO.deleteLessonClass(oldLessonClass); + } - //update lesson status - requestedLesson.setLessonStateId(Lesson.STARTED_STATE); - requestedLesson.setStartDateTime(lessonStartTime); - lessonDAO.updateLesson(requestedLesson); - - if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("=============Lesson " + lessonId + " started==============="); - } + } else { // if started, can only add users + lessonService.addLearners(newLesson, organizationUsers); + lessonService.addStaffMembers(newLesson, staffs); } - /** 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; + return newLesson; + } - //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); - grouping.getActivities().add(activity); - activity.setGrouping(grouping); - activity.setGroupingUIID(currentMaxId); - activity.setApplyGrouping(Boolean.TRUE); - groupingDAO.insert(grouping); + /** + * 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) { - activity.setGrouping(grouping); - if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("startLesson: Created chosen grouping " + grouping + " for branching activity " - + activity); - } - newMaxId = new Integer(currentMaxId.intValue() + 1); - } - return newMaxId; + // 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"); - /** - *

    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 (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 (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); + if (requestedLesson.getScheduleStartDate() != null) { + // can't reschedule! + MonitoringService.log.error("Lesson for id=" + lessonId + + " is already scheduled and cannot be rescheduled."); + return; + } - //create customized triggers - Trigger openGateTrigger = new SimpleTrigger("openGateTrigger:" + scheduleGate.getActivityId(), Scheduler.DEFAULT_GROUP, - scheduleGate.getLessonGateOpenTime(schedulingStartTime)); + JobDetail startLessonJob = getStartScheduleLessonJob(); + // setup the message for scheduling job + startLessonJob.setName("startLessonOnSchedule:" + lessonId); - Trigger closeGateTrigger = new SimpleTrigger("closeGateTrigger:" + scheduleGate.getActivityId(), Scheduler.DEFAULT_GROUP, - scheduleGate.getLessonGateCloseTime(schedulingStartTime)); + 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)); - //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); - } + // 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); + } - } - catch (SchedulerException e) { - throw new MonitoringServiceException("Error occurred at " + "[runGateScheduler]- fail to start scheduling", e); - } + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Start lesson [" + lessonId + "] on schedule is configured"); + } + } - if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("Scheduler for Gate " + scheduleGate.getActivityId() + " started..."); - } - - return scheduleGate; + /** + * 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"); - /** - * @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); + 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); } - /** - * 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); - } + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Finish lesson [" + lessonId + "] on schedule is configured"); } + } - /** - * 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#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 + "=============="); } - /** - * - * @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); - } + // 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."); } - /** - * - * @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"); - Integer state = lesson.getLessonStateId(); - //only suspend started lesson - 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."); - } - revertLessonState(lesson); + 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; } - /** - * 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) { + checkOwnerOrStaffMember(userId, requestedLesson, "create lesson class"); - requestedLesson.setPreviousLessonStateId(requestedLesson.getLessonStateId()); - requestedLesson.setLessonStateId(status); - lessonDAO.updateLesson(requestedLesson); + 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) { + design.setMaxID(newMaxId); + designModified = true; + } + + activity.setInitialised(Boolean.TRUE); + activityDAO.update(activity); + } - /** - * Sets a lesson back to its previous state. Used when we "unsuspend" or "unarchive" - * @param requestedLesson - * @param status - */ - private void revertLessonState(Lesson requestedLesson) { + if (designModified) { + learningDesignDAO.update(design); + } - 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); - } + // update lesson status + requestedLesson.setLessonStateId(Lesson.STARTED_STATE); + requestedLesson.setStartDateTime(lessonStartTime); + lessonDAO.updateLesson(requestedLesson); - requestedLesson.setPreviousLessonStateId(currentStatus); - } - lessonDAO.updateLesson(requestedLesson); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("=============Lesson " + lessonId + " started==============="); } + } - /** - * @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"); + /** + * 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; - // TODO give sysadmin rights to do this too! + // 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); + grouping.getActivities().add(activity); + activity.setGrouping(grouping); + activity.setGroupingUIID(currentMaxId); + activity.setApplyGrouping(Boolean.TRUE); + groupingDAO.insert(grouping); - setLessonState(requestedLesson, Lesson.REMOVED_STATE); + activity.setGrouping(grouping); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("startLesson: Created chosen grouping " + grouping + + " for branching activity " + activity); + } + newMaxId = new Integer(currentMaxId.intValue() + 1); } + return newMaxId; + } - /** - * @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"); - requestedLesson.setLearnerExportAvailable(learnerExportAvailable != null ? learnerExportAvailable : Boolean.FALSE); - auditAction(MonitoringService.AUDIT_LEARNER_PORTFOLIO_SET, new Object[] { requestedLesson.getLessonName(), - requestedLesson.getLearnerExportAvailable() }); - lessonDAO.updateLesson(requestedLesson); - return requestedLesson.getLearnerExportAvailable(); + /** + *

    + * 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 (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); - /** - * @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)); + // create customized triggers + Trigger openGateTrigger = new SimpleTrigger("openGateTrigger:" + scheduleGate.getActivityId(), + Scheduler.DEFAULT_GROUP, scheduleGate.getLessonGateOpenTime(schedulingStartTime)); - // we un-schedule the gate from the scheduler if it's of a scheduled gate (LDEV-1271) - if (gate.isScheduleGate()) { + Trigger closeGateTrigger = new SimpleTrigger("closeGateTrigger:" + scheduleGate.getActivityId(), + Scheduler.DEFAULT_GROUP, scheduleGate.getLessonGateCloseTime(schedulingStartTime)); - try { - scheduler.unscheduleJob("openGateTrigger:" + gate.getActivityId(), Scheduler.DEFAULT_GROUP); - } - 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); + // 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..."); + } - activityDAO.update(gate); - } - return gate; + 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); + } - public GateActivity openGateForSingleUser(Long gateId, Integer userId) { - GateActivity gate = (GateActivity) activityDAO.getActivityByActivityId(gateId); - if (gate != null && userId != null && userId >= 0) { - User user = (User) baseDAO.find(User.class, userId); - gate.addLeaner(user, true); - activityDAO.update(gate); - } - return gate; + /** + * 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); + } + } - /** - * @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; + /** + * 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); - /** - * @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); + /** + * + * @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); + } + } - LearnerProgress learnerProgress = learnerService.getProgress(learnerId, lessonId); - Activity stopActivity = null; + /** + * + * @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"); + Integer state = lesson.getLessonStateId(); + // only suspend started lesson + 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."); + } + revertLessonState(lesson); + } - if (activityId != null) { - stopActivity = activityDAO.getActivityByActivityId(activityId); - if (stopActivity == null) { - throw new MonitoringServiceException("Activity missing. Activity id" + activityId); - } + /** + * 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) { - // 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() }); - } - } + requestedLesson.setPreviousLessonStateId(requestedLesson.getLessonStateId()); + requestedLesson.setLessonStateId(status); + lessonDAO.updateLesson(requestedLesson); + } - Activity currentActivity = learnerProgress.getCurrentActivity(); - String stopReason = null; - if (currentActivity != null) { - stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, currentActivity, stopActivity, - new ArrayList()); + /** + * 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); } - return stopReason != null ? stopReason : messageService - .getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY); + } else if (requestedLesson.getStartDateTime() != null) { + requestedLesson.setLessonStateId(Lesson.STARTED_STATE); + } else { + requestedLesson.setLessonStateId(Lesson.CREATED); + } + requestedLesson.setPreviousLessonStateId(currentStatus); } + lessonDAO.updateLesson(requestedLesson); + } - /** - * 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) { + /** + * @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 check performance - does it load the learner progress every time or do it load it from the cache. - String stopReason = null; + // TODO give sysadmin rights to do this too! - // activity likely to be a cglib so get the real activity - activity = activityDAO.getActivityByActivityId(activity.getActivityId()); - touchedActivityIds.add(activity.getActivityId()); + setLessonState(requestedLesson, Lesson.REMOVED_STATE); + } - 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) { - 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.completeActivity(learner.getUserId(), activity, lessonId); - if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("Grouping activity [" + activity.getActivityId() + "] is completed."); - } - } - 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."); - } - } + /** + * @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"); + requestedLesson.setLearnerExportAvailable(learnerExportAvailable != null ? learnerExportAvailable + : Boolean.FALSE); + auditAction(MonitoringService.AUDIT_LEARNER_PORTFOLIO_SET, new Object[] { requestedLesson.getLessonName(), + requestedLesson.getLearnerExportAvailable() }); + lessonDAO.updateLesson(requestedLesson); + return requestedLesson.getLearnerExportAvailable(); + } - } - else if (activity.isGateActivity()) { - GateActivity gate = (GateActivity) activity; - GateActivityDTO dto = learnerService.knockGate(gate, learner, false); - if (dto.getAllowToPass()) { - //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() }); - } + /** + * @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)); - } - 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); - } + // we un-schedule the gate from the scheduler if it's of a scheduled + // gate (LDEV-1271) + if (gate.isScheduleGate()) { - } - 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); + try { + scheduler.unscheduleJob("openGateTrigger:" + gate.getActivityId(), Scheduler.DEFAULT_GROUP); + } 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); } - 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."); - } - // 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() }); + } - } - else { - Activity nextActivity = learnerProgress.getNextActivity(); + activityDAO.update(gate); + } + return gate; + } - // 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); - } - } - } - - return stopReason; - + public GateActivity openGateForSingleUser(Long gateId, Integer userId) { + GateActivity gate = (GateActivity) activityDAO.getActivityByActivityId(gateId); + if (gate != null && userId != null && userId >= 0) { + User user = (User) baseDAO.find(User.class, userId); + gate.addLeaner(user, true); + activityDAO.update(gate); } + return gate; + } - /** - * (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"); + /** + * @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; + } - User user = (User) baseDAO.find(User.class, userID); - LessonDetailsDTO dto = lessonService.getLessonDetails(lessonID); + /** + * @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"); - Locale userLocale = new Locale(user.getLocale().getLanguageIsoCode(), user.getLocale().getCountryIsoCode()); + User learner = (User) baseDAO.find(User.class, learnerId); - if (dto.getStartDateTime() != null) { - dto.setStartDateTimeStr(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, userLocale).format( - dto.getStartDateTime())); - } + LearnerProgress learnerProgress = learnerService.getProgress(learnerId, lessonId); + Activity stopActivity = null; - if (dto.getScheduleStartDate() != null) { - dto.setScheduleStartDateStr(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, userLocale).format( - dto.getScheduleStartDate())); - } + if (activityId != null) { + stopActivity = activityDAO.getActivityByActivityId(activityId); + if (stopActivity == null) { + throw new MonitoringServiceException("Activity missing. Activity id" + activityId); + } - 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); - } + // 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() }); + } + } - return flashMessage.serializeMessage(); + Activity currentActivity = learnerProgress.getCurrentActivity(); + String stopReason = null; + if (currentActivity != null) { + stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, currentActivity, stopActivity, + new ArrayList()); } + return stopReason != null ? stopReason : messageService + .getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY); - /** - * (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(); - } + /** + * 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) { - /** - * (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"); + // TODO check performance - does it load the learner progress every time + // or do it load it from the cache. + String stopReason = null; - 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); + // 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) { + 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.completeActivity(learner.getUserId(), activity, lessonId); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Grouping activity [" + activity.getActivityId() + + "] is completed."); + } + } else { + // except random grouping, stop here + stopReason = messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_GROUPING, + new Object[] { activity.getTitle() }); } - else { - flashMessage = new FlashMessage("getLessonStaff", messageService.getMessage("NO.SUCH.LESSON", - new Object[] { lessonID }), FlashMessage.ERROR); + } else { + // if group already exist + learnerService.completeActivity(learner.getUserId(), activity, lessonId); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("Grouping activity [" + activity.getActivityId() + "] is completed."); } - 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(), ""); - } + } else if (activity.isGateActivity()) { + GateActivity gate = (GateActivity) activity; + GateActivityDTO dto = learnerService.knockGate(gate, learner, false); + if (dto.getAllowToPass()) { + // 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#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 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); } - else { - flashMessage = new FlashMessage("getAllLearnersProgress", messageService.getMessage("NO.SUCH.LESSON", - new Object[] { lessonID }), FlashMessage.ERROR); + 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."); } - return flashMessage.serializeMessage(); - } + } catch (LamsToolServiceException e) { + throw new MonitoringServiceException(e); + } - /** - * (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; + } 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); - if (lesson != null) { - checkOwnerOrStaffMember(userID, lesson, "get first batch of learners progress details"); - Vector progressData = new Vector(); + } 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."); + } - int batchSize = Configuration.getAsInt(ConfigurationKeys.LEARNER_PROGRESS_BATCH_SIZE); - if (batchSize < 1) { - batchSize = MonitoringService.DEFAULT_LEARNER_PROGRESS_BATCH_SIZE; - } + // 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() }); - 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); + // 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); } - 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; + return stopReason; - 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; - } + /** + * (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"); - 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(); + 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())); } - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityById(Long) - */ - public Activity getActivityById(Long activityId) { - return activityDAO.getActivityByActivityId(activityId); + if (dto.getScheduleStartDate() != null) { + dto.setScheduleStartDateStr(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, userLocale) + .format(dto.getScheduleStartDate())); } - /** - * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityById(Long, Class) - */ - public Activity getActivityById(Long activityId, Class clasz) { - return activityDAO.getActivityByActivityId(activityId, clasz); + 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); } - /** - * @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; - 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; - MonitoringService.log.error(error); - throw new MonitoringServiceException(error); - } + return flashMessage.serializeMessage(); + } - return (GroupingActivity) activity; + /** + * (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#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#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#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#getLearningDesignDetails(java.lang.Long) + */ + public String getLearningDesignDetails(Long lessonID) throws IOException { + Lesson lesson = lessonDAO.getLesson(lessonID); + return authoringService.getLearningDesignDetails(lesson.getLearningDesign().getLearningDesignId(), ""); + } - Activity activity = activityDAO.getActivityByActivityId(activityID); - User learner = (User) baseDAO.find(User.class, learnerUserID); + /** + * (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; - 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; + 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#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"); + /** + * (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; - Activity activity = activityDAO.getActivityByActivityId(activityID); - if (activity == null) { - MonitoringService.log.error("getActivityMonitorURL activity missing. Activity ID " + activityID + " activity " - + activity); + if (lesson != null) { + checkOwnerOrStaffMember(userID, lesson, "get first batch of learners progress details"); + Vector progressData = new Vector(); - } - else if (activity.isToolActivity()) { - ToolActivity toolActivity = (ToolActivity) activity; - return lamsCoreToolService.getToolDefineLaterURL(toolActivity); + int batchSize = Configuration.getAsInt(ConfigurationKeys.LEARNER_PROGRESS_BATCH_SIZE); + if (batchSize < 1) { + batchSize = MonitoringService.DEFAULT_LEARNER_PROGRESS_BATCH_SIZE; + } - } - return null; + 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#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"); + /** + * (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; - Activity activity = activityDAO.getActivityByActivityId(activityID); + if (lesson != null) { + checkOwnerOrStaffMember(userID, lesson, "get next batch of learners progress details"); + Vector progressData = new Vector(); - if (activity == null) { - MonitoringService.log.error("getActivityMonitorURL activity missing. Activity ID " + activityID + " activity " - + activity); + 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; + } - } - else if (activity.isToolActivity() || activity.isSystemToolActivity()) { - return lamsCoreToolService.getToolMonitoringURL(lessonID, activity) + "&contentFolderID=" + contentFolderID; - } - return null; + 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(); + } - /** - * (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 { - 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) { - LearningDesign learningDesign = lesson.getLearningDesign(); - learningDesign.setWorkspaceFolder(workspaceFolder); - learningDesignDAO.update(learningDesign); - 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); + /** + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#getActivityById(Long) + */ + public Activity getActivityById(Long activityId) { + return activityDAO.getActivityByActivityId(activityId); + } - } - return flashMessage.serializeMessage(); + /** + * @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; + 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; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); } - /** - * (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(); + 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#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. + /** + * (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"); - 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(); + 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#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); + /** + * (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"); - flashMessage = new FlashMessage("releaseGate", gate.getGateOpen()); + Activity activity = activityDAO.getActivityByActivityId(activityID); + if (activity == null) { + MonitoringService.log.error("getActivityMonitorURL activity missing. Activity ID " + activityID + + " activity " + activity); - } - return flashMessage.serializeMessage(); + } else if (activity.isToolActivity()) { + ToolActivity toolActivity = (ToolActivity) activity; + return lamsCoreToolService.getToolDefineLaterURL(toolActivity); } + return null; + } - /** - * @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(); + /** + * (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"); - if (!grouping.isChosenGrouping()) { - MonitoringService.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 < learnerIdList.size(); idx++) { - User user = (User) baseDAO.find(User.class, ((Double) learnerIdList.get(idx)).intValue()); - learners.add(user); + Activity activity = activityDAO.getActivityByActivityId(activityID); - } - MonitoringService.log.debug("Performing grouping for " + groupName + "..."); - lessonService.performGrouping(groupingActivity, groupName, learners); - MonitoringService.log.debug("Finish grouping for " + groupName); - } + if (activity == null) { + MonitoringService.log.error("getActivityMonitorURL activity missing. Activity ID " + activityID + + " activity " + activity); - MonitoringService.log.debug("Persist grouping for [" + grouping.getGroupingId() + "]..."); - groupingDAO.update(grouping); - MonitoringService.log.debug("Persist grouping for [" + grouping.getGroupingId() + "] success."); + } 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 { + 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) { + LearningDesign learningDesign = lesson.getLearningDesign(); + learningDesign.setWorkspaceFolder(workspaceFolder); + learningDesignDAO.update(learningDesign); + flashMessage = new FlashMessage("moveLesson", targetWorkspaceFolderID); + } else { + flashMessage = FlashMessage.getNoSuchWorkspaceFolderExsists("moveLesson", targetWorkspaceFolderID); } - catch (WDDXProcessorConversionException e) { - throw new MonitoringServiceException("Perform chosen grouping occurs error when parsing WDDX package:" - + e.getMessage()); - } + } else { + flashMessage = FlashMessage.getUserNotAuthorized("moveLesson", userID); + } + } else { + flashMessage = new FlashMessage("moveLesson", messageService.getMessage("NO.SUCH.LESSON", + new Object[] { lessonID }), FlashMessage.ERROR); } + return flashMessage.serializeMessage(); - //--------------------------------------------------------------------- - // 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); + } - //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))); + /** + * (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(); + } - lessonClassDAO.updateLessonClass(newLessonClass); + /** + * (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. - return newLessonClass; + 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(); + } - /** - * 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, Boolean learnerPresenceAvailable, Boolean learnerImAvailable) { - Lesson newLesson = Lesson.createNewLessonWithoutClass(lessonName, lessonDescription, user, learnerExportAvailable, - copiedLearningDesign, learnerPresenceAvailable, learnerImAvailable); - lessonDAO.saveLesson(newLesson); - return newLesson; - } + /** + * (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); - /** - * 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; + flashMessage = new FlashMessage("releaseGate", gate.getGateOpen()); + } + return flashMessage.serializeMessage(); - //--------------------------------------------------------------------- - // 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 { + /** + * @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(); - 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); - } - } + if (!grouping.isChosenGrouping()) { + MonitoringService.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 < learnerIdList.size(); idx++) { + User user = (User) baseDAO.find(User.class, ((Double) learnerIdList.get(idx)).intValue()); + learners.add(user); - //--------------------------------------------------------------------- - // Helper Methods - scheduling - //--------------------------------------------------------------------- + } + MonitoringService.log.debug("Performing grouping for " + groupName + "..."); + lessonService.performGrouping(groupingActivity, groupName, learners); + MonitoringService.log.debug("Finish grouping for " + groupName); + } - /** - * Returns the bean that defines the open schedule gate job. - */ - private JobDetail getOpenScheduleGateJob() { - return (JobDetail) applicationContext.getBean("openScheduleGateJob"); - } + MonitoringService.log.debug("Persist grouping for [" + grouping.getGroupingId() + "]..."); + groupingDAO.update(grouping); + MonitoringService.log.debug("Persist grouping for [" + grouping.getGroupingId() + "] success."); - /** - * - * @return the bean that defines start lesson on schedule job. - */ - private JobDetail getStartScheduleLessonJob() { - return (JobDetail) applicationContext.getBean(MonitoringConstants.JOB_START_LESSON); + } catch (WDDXProcessorConversionException e) { + throw new MonitoringServiceException("Perform chosen grouping occurs error when parsing WDDX package:" + + e.getMessage()); } - /** - * - * @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"); - } + // --------------------------------------------------------------------- + // 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); - 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())); + // 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))); - /* 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()); + 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, Boolean learnerPresenceAvailable, + Boolean learnerImAvailable) { + Lesson newLesson = Lesson.createNewLessonWithoutClass(lessonName, lessonDescription, user, + learnerExportAvailable, copiedLearningDesign, learnerPresenceAvailable, learnerImAvailable); + 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 - 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); } - else if (gate.isPermissionGate() || gate.isConditionGate()) { - table.put("allowedToPassLearnerList", gate.getAllowedToPassLearners()); - } - return table; + } 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); + } } + } - //--------------------------------------------------------------------- - // Preview related methods - //--------------------------------------------------------------------- - /* (non-Javadoc) - * @see org.lamsfoundation.lams.preview.service.IMonitoringService#createPreviewClassForLesson(long, long) - */ - public Lesson createPreviewClassForLesson(int userID, long lessonID) throws UserAccessDeniedException { + // --------------------------------------------------------------------- + // Helper Methods - scheduling + // --------------------------------------------------------------------- - User user = (User) baseDAO.find(User.class, userID); - if (user == null) { - throw new UserAccessDeniedException("User " + userID + " not found"); - } + /** + * Returns the bean that defines the open schedule gate job. + */ + private JobDetail getOpenScheduleGateJob() { + return (JobDetail) applicationContext.getBean("openScheduleGateJob"); + } - // create the lesson class - add the teacher as the learner and as staff - LinkedList learners = new LinkedList(); - learners.add(user); + /** + * + * @return the bean that defines start lesson on schedule job. + */ + private JobDetail getStartScheduleLessonJob() { + return (JobDetail) applicationContext.getBean(MonitoringConstants.JOB_START_LESSON); + } - LinkedList staffs = new LinkedList(); - staffs.add(user); + /** + * + * @return the bean that defines start lesson on schedule job. + */ + private JobDetail getFinishScheduleLessonJob() { + return (JobDetail) applicationContext.getBean(MonitoringConstants.JOB_FINISH_LESSON); + } - return createLessonClassForLesson(lessonID, null, "Learner Group", learners, "Staff Group", staffs, userID); + /** + * 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())); - /** - * 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. + /* + * if the gate is a schedule gate, include the information about gate + * opening and gate closing times */ - public void deletePreviewLesson(long lessonID) { - Lesson lesson = lessonDAO.getLesson(new Long(lessonID)); - deletePreviewLesson(lesson); + if (gate.isScheduleGate()) { + ScheduleGateActivity scheduleGate = (ScheduleGateActivity) gate; + table.put("gateStartTime", scheduleGate.getGateStartDateTime()); + table.put("gateEndTime", scheduleGate.getGateEndDateTime()); + } else if (gate.isPermissionGate() || gate.isConditionGate()) { + table.put("allowedToPassLearnerList", gate.getAllowedToPassLearners()); } + return table; + } - private void deletePreviewLesson(Lesson lesson) { - if (lesson != null) { - if (lesson.isPreviewLesson()) { + // --------------------------------------------------------------------- + // Preview related methods + // --------------------------------------------------------------------- + /* + * (non-Javadoc) + * + * @seeorg.lamsfoundation.lams.preview.service.IMonitoringService# + * createPreviewClassForLesson(long, long) + */ + public Lesson createPreviewClassForLesson(int userID, long lessonID) throws UserAccessDeniedException { - // 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()); - } - } + User user = (User) baseDAO.find(User.class, userID); + if (user == null) { + throw new UserAccessDeniedException("User " + userID + " not found"); } - /* (non-Javadoc) - * @see org.lamsfoundation.lams.preview.service.IMonitoringService#deleteAllOldPreviewLessons(int) - */ - public int deleteAllOldPreviewLessons() { + // create the lesson class - add the teacher as the learner and as staff + LinkedList learners = new LinkedList(); + learners.add(user); - int numDays = Configuration.getAsInt(ConfigurationKeys.PREVIEW_CLEANUP_NUM_DAYS); + LinkedList staffs = new LinkedList(); + staffs.add(user); - // 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; - } + return createLessonClassForLesson(lessonID, null, "Learner Group", learners, "Staff Group", staffs, userID); - 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 + ")"); + /** + * 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); + } - // 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); - } + 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"); } - return numDeleted; + // 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()); + } } + } - /* ************** 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"); + /* + * (non-Javadoc) + * + * @seeorg.lamsfoundation.lams.preview.service.IMonitoringService# + * deleteAllOldPreviewLessons(int) + */ + public int deleteAllOldPreviewLessons() { - // 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); - } + int numDays = Configuration.getAsInt(ConfigurationKeys.PREVIEW_CLEANUP_NUM_DAYS); - Set learners = lesson.getAllLearners(); - if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() - + " learners."); - } + // 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; + } - 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."); - } - } + int numDeleted = 0; - if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() - + " learners."); - } + // 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 + ")"); - SortedSet sortedUsers = new TreeSet(new LastNameAlphabeticComparator()); - sortedUsers.addAll(learners); - return sortedUsers; + // 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); + } } - /** 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; - MonitoringService.log.error(error); - throw new MonitoringServiceException(error); - } + return numDeleted; + } - Grouping grouping = null; + /* 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"); - if (useCreateGrouping) { - GroupingActivity groupingActivity = (GroupingActivity) activity; - grouping = groupingActivity.getCreateGrouping(); - } - else { - grouping = activity.getGrouping(); - } + // 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); + } - if (grouping == null) { - String error = methodName + ": Grouping activity missing grouping. Activity was " + activity; - MonitoringService.log.error(error); - throw new MonitoringServiceException(error); - } - return grouping; + Set learners = lesson.getAllLearners(); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() + + " learners."); } - /** 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. - * @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) { - // 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()) { - boolean usedForBranching = grouping.isUsedForBranching(); - 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 { - 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); + 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."); + } } - /** 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. - * @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"); - lessonService.removeGroup(grouping, groupId); + if (MonitoringService.log.isDebugEnabled()) { + MonitoringService.log.debug("getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() + + " learners."); } - /** 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"); - lessonService.performGrouping(grouping, groupId, 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; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); } - 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) { - MonitoringService.log.warn("Unable to " + addRemoveTextForErrorMessage + " learner " + strlearnerID - + " for group in related to activity " + activityIDForErrorMessage + " as learner cannot be found."); - } - } - return learners; + Grouping grouping = null; + + if (useCreateGrouping) { + GroupingActivity groupingActivity = (GroupingActivity) activity; + grouping = groupingActivity.getCreateGrouping(); + } else { + grouping = activity.getGrouping(); } - /** 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 { + if (grouping == null) { + String error = methodName + ": Grouping activity missing grouping. Activity was " + activity; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } + return grouping; + } - SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); - if (branch == null) { - String error = "addUsersToBranch: Branch missing. ActivityID was " + sequenceActivityID; - MonitoringService.log.error(error); - throw new MonitoringServiceException(error); + /** + * 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. + * + * @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) { + // 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()) { + boolean usedForBranching = grouping.isUsedForBranching(); + 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 { + 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); + } - 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(); + /** + * 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. + * + * @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"); + lessonService.removeGroup(grouping, groupId); + } - // 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(); + /** + * 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"); + lessonService.performGrouping(grouping, groupId, learners); + } - 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); - - design.setMaxID(new Integer(nextUIID.intValue() + 1)); - learningDesignDAO.update(design); - + 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; } - else { - grouping = group.getGrouping(); - } - - ArrayList learners = createUserList(sequenceActivityID, learnerIDs, "add"); - lessonService.performGrouping(grouping, group.getGroupId(), 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; + } - /** 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); + /** + * 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; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); } - /** 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 { + 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(); - SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); - if (branch == null) { - String error = "addUsersToBranch: Branch missing. ActivityID was " + sequenceActivityID; - MonitoringService.log.error(error); - throw new MonitoringServiceException(error); - } + // 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 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!"); - } + 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); + design.setMaxID(new Integer(nextUIID.intValue() + 1)); + learningDesignDAO.update(design); + + } else { + grouping = group.getGrouping(); } - /** 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 { + ArrayList learners = createUserList(sequenceActivityID, learnerIDs, "add"); + lessonService.performGrouping(grouping, group.getGroupId(), learners); + } - SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); - if (branch == null) { - String error = "addGroupToBranch: Branch missing. ActivityID was " + sequenceActivityID; - MonitoringService.log.error(error); - throw new MonitoringServiceException(error); - } + /** + * 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); + } - 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 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 { - 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; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } - for (String groupIDString : groupIDs) { - Long groupID = Long.parseLong(groupIDString); + 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!"); + } - 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); - } + } - group.allocateBranchToGroup(nextUIID, branch, branchingActivity); - groupingDAO.update(group); - } + /** + * 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 { - design.setMaxID(new Integer(nextUIID.intValue() + 1)); - learningDesignDAO.update(design); + SequenceActivity branch = (SequenceActivity) getActivityById(sequenceActivityID); + if (branch == null) { + String error = "addGroupToBranch: Branch missing. ActivityID was " + sequenceActivityID; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } + 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; - MonitoringService.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; - 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; } - BranchingActivity branchingActivity = (BranchingActivity) getActivityById(parentActivity.getActivityId()); - Grouping grouping = branchingActivity.getGrouping(); + } + if (group == null) { + String error = "addGroupToBranch: Group missing. Group ID was " + groupIDString; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } - 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; - MonitoringService.log.error(error); - throw new MonitoringServiceException(error); - } + design.setMaxID(new Integer(nextUIID.intValue() + 1)); + learningDesignDAO.update(design); - // 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); - } - } + } + /** + * 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 (MonitoringService.log.isDebugEnabled()) { - MonitoringService.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); } + BranchingActivity branchingActivity = (BranchingActivity) getActivityById(parentActivity.getActivityId()); + Grouping grouping = branchingActivity.getGrouping(); - /** - * 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 { + for (String groupIDString : groupIDs) { + Long groupID = Long.parseLong(groupIDString); - 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); + 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; + MonitoringService.log.error(error); + throw new MonitoringServiceException(error); + } - TreeSet unassignedGroups = new TreeSet(); + // 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); + } + } - 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; - + /** + * 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); } + return numAttempted != null && numAttempted.intValue() > 0; + } - /** - * 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); + /** + * 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); } - public LearnerProgress getLearnerProgress(Integer learnerId, Long lessonId) { - return learnerService.getProgress(learnerId, lessonId); + 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; + + } + /** - * Same as initializeLesson, but for multiple lessons, returning multiple lesson ids. - * Used internally by startLessons + * 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); + } + + /** + * Same as initializeLesson, but for multiple lessons, returning multiple + * lesson ids. Used internally by startLessons + */ private List initializeLessons(Integer creatorUserId, String lessonPacket) throws Exception { ArrayList lessonIds = new ArrayList(); @@ -2480,8 +2657,9 @@ // get learner group info Hashtable learnerMap = (Hashtable) table.get(MonitoringConstants.KEY_LEARNER); - //ignored - //List learners = (List) learnerMap.get(MonitoringConstants.KEY_USERS); + // ignored + // 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);