Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -rde44e2e970afac102177634d2e9106919f65f773 -r65dc0e557544d65bf37488fbd11567599181ad84 Binary files differ Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/CompletedActivityProgress.hbm.xml =================================================================== diff -u -r771e53ce121281172ff7101bd9f4eac1b02d674c -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/CompletedActivityProgress.hbm.xml (.../CompletedActivityProgress.hbm.xml) (revision 771e53ce121281172ff7101bd9f4eac1b02d674c) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/CompletedActivityProgress.hbm.xml (.../CompletedActivityProgress.hbm.xml) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -4,18 +4,11 @@ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > - + - - - + Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/CompletedActivityProgressArchive.hbm.xml =================================================================== diff -u --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/CompletedActivityProgressArchive.hbm.xml (revision 0) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/CompletedActivityProgressArchive.hbm.xml (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/LearnerProgressArchive.hbm.xml =================================================================== diff -u --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/LearnerProgressArchive.hbm.xml (revision 0) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/lesson/LearnerProgressArchive.hbm.xml (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040064.sql =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040064.sql (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040064.sql (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -0,0 +1,49 @@ +-- Turn off autocommit, so nothing is committed if there is an error +SET AUTOCOMMIT = 0; + +CREATE TABLE lams_learner_progress_archive ( + learner_progress_id bigint(20) NOT NULL AUTO_INCREMENT, + user_id bigint(20) NOT NULL, + lesson_id bigint(20) NOT NULL, + attempt_id TINYINT NOT NULL DEFAULT 1, + lesson_completed_flag tinyint(1) NOT NULL DEFAULT 0, + start_date_time datetime NOT NULL, + finish_date_time datetime, + current_activity_id bigint(20), + PRIMARY KEY (learner_progress_id), + UNIQUE KEY IX_lams_learner_progress_archive_1 (user_id, lesson_id, attempt_id), + CONSTRAINT FK_lams_learner_progress_archive_1 FOREIGN KEY (user_id) + REFERENCES lams_user (user_id) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_lams_learner_progress_archive_2 FOREIGN KEY (lesson_id) + REFERENCES lams_lesson (lesson_id) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_lams_learner_progress_archive_3 FOREIGN KEY (current_activity_id) + REFERENCES lams_learning_activity (activity_id) ON UPDATE CASCADE ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET utf8mb4; + + +CREATE TABLE lams_progress_attempted_archive ( + learner_progress_id bigint(20) NOT NULL, + activity_id bigint(20) NOT NULL, + start_date_time datetime, + PRIMARY KEY (learner_progress_id, activity_id), + CONSTRAINT FK_lams_progress_current_archive_1 FOREIGN KEY (learner_progress_id) + REFERENCES lams_learner_progress_archive (learner_progress_id) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_lams_progress_current_archive_2 FOREIGN KEY (activity_id) + REFERENCES lams_learning_activity (activity_id) ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET utf8mb4; + + +CREATE TABLE lams_progress_completed_archive ( + learner_progress_id bigint(20) NOT NULL, + activity_id bigint(20) NOT NULL, + completed_date_time datetime, + start_date_time datetime, + PRIMARY KEY (learner_progress_id,activity_id), + CONSTRAINT FK_lams_progress_completed_archive_1 FOREIGN KEY (learner_progress_id) + REFERENCES lams_learner_progress_archive (learner_progress_id) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_lams_progress_completed_archive_2 FOREIGN KEY (activity_id) + REFERENCES lams_learning_activity (activity_id) ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +COMMIT; +SET AUTOCOMMIT = 1; \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/lesson/CompletedActivityProgressArchive.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/lesson/CompletedActivityProgressArchive.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/CompletedActivityProgressArchive.java (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -0,0 +1,104 @@ +/**************************************************************** + * Copyright (C) 2008 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +package org.lamsfoundation.lams.lesson; + +import java.io.Serializable; +import java.util.Date; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.lamsfoundation.lams.learningdesign.Activity; + +public class CompletedActivityProgressArchive implements Serializable { + + LearnerProgress learnerProgress; + Activity activity; + Date startDate; + Date finishDate; + + public CompletedActivityProgressArchive() { + } + + public CompletedActivityProgressArchive(LearnerProgress learnerProgress, Activity activity, Date startDate, + Date finishDate) { + this.learnerProgress = learnerProgress; + this.activity = activity; + this.startDate = startDate; + this.finishDate = finishDate; + } + + public LearnerProgress getLearnerProgress() { + return learnerProgress; + } + + public void setLearnerProgress(LearnerProgress learnerProgress) { + this.learnerProgress = learnerProgress; + } + + public Activity getActivity() { + return activity; + } + + public void setActivity(Activity activity) { + this.activity = activity; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getFinishDate() { + return finishDate; + } + + public void setFinishDate(Date finishDate) { + this.finishDate = finishDate; + } + + @Override + public boolean equals(Object other) { + if ((this == other)) { + return true; + } + if (!(other instanceof CompletedActivityProgressArchive)) { + return false; + } + CompletedActivityProgressArchive castOther = (CompletedActivityProgressArchive) other; + + EqualsBuilder eq = new EqualsBuilder(); + eq.append(this.getActivity().getActivityId(), castOther.getActivity().getActivityId()); + eq.append(this.getLearnerProgress().getLearnerProgressId(), + castOther.getLearnerProgress().getLearnerProgressId()); + return eq.isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(this.getActivity().getActivityId().toString() + + this.getLearnerProgress().getLearnerProgressId().toString()).toHashCode(); + } +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/lesson/LearnerProgressArchive.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/lesson/LearnerProgressArchive.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/LearnerProgressArchive.java (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -0,0 +1,220 @@ +/*************************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * ***********************************************************************/ + +package org.lamsfoundation.lams.lesson; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.lamsfoundation.lams.learningdesign.Activity; +import org.lamsfoundation.lams.usermanagement.User; + +public class LearnerProgressArchive implements Serializable { + /** Identifier field */ + private Long learnerProgressId; + + /** The User to whom this progress data belongs. */ + private User user; + + /** The Lesson this progress data is for */ + private Lesson lesson; + + private Integer attemptId; + + /** Map of attempted activities with their start date */ + private Map attemptedActivities; + + /** + * Set of completed activities that includes all completed activities before + * current activity + */ + private Map completedActivities; + + /** + * The current activity always present the activity with transition, which + * means it won't be leaf node of a complex activity. To understand the + * activity tree, please read relevant documentation and comment. The + * current content could be the same as next activity if next activity is + * not the leaf node. The main purpose of current activity is to restore the + * progress states if the user exist without finishing the activity. + */ + private Activity currentActivity; + + /** + * Indicates is the User has completed this lesson. + */ + private Byte lessonComplete; + + private Date startDate; + private Date finishDate; + + //--------------------------------------------------------------------- + // Constructors + //--------------------------------------------------------------------- + /** default constructor */ + public LearnerProgressArchive() { + } + + public LearnerProgressArchive(User user, Lesson lesson, Integer attemptId, Map attemptedActivities, + Map completedActivities, Activity currentActivity, + Byte lessonComplete, Date startDate, Date finishDate) { + this.user = user; + this.lesson = lesson; + this.attemptId = attemptId; + this.attemptedActivities = attemptedActivities; + this.completedActivities = completedActivities; + this.currentActivity = currentActivity; + this.lessonComplete = lessonComplete; + this.startDate = startDate; + this.finishDate = finishDate; + } + + //--------------------------------------------------------------------- + // Getters and Setters + //--------------------------------------------------------------------- + public Long getLearnerProgressId() { + return this.learnerProgressId; + } + + public void setLearnerProgressId(Long learnerProgressId) { + this.learnerProgressId = learnerProgressId; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public Lesson getLesson() { + return this.lesson; + } + + public void setLesson(Lesson lesson) { + this.lesson = lesson; + } + + public Integer getAttemptId() { + return attemptId; + } + + public void setAttemptId(Integer attemptId) { + this.attemptId = attemptId; + } + + public Map getAttemptedActivities() { + return this.attemptedActivities; + } + + public void setAttemptedActivities(Map attemptedActivities) { + this.attemptedActivities = attemptedActivities; + } + + public Map getCompletedActivities() { + return this.completedActivities; + } + + public void setCompletedActivities(Map completedActivities) { + this.completedActivities = completedActivities; + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("learnerProgressId", getLearnerProgressId()).toString(); + } + + @Override + public boolean equals(Object other) { + if ((this == other)) { + return true; + } + if (!(other instanceof LearnerProgressArchive)) { + return false; + } + LearnerProgressArchive castOther = (LearnerProgressArchive) other; + return new EqualsBuilder().append(this.getLearnerProgressId(), castOther.getLearnerProgressId()).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(getLearnerProgressId()).toHashCode(); + } + + public Activity getCurrentActivity() { + return this.currentActivity; + } + + public void setCurrentActivity(Activity currentActivity) { + this.currentActivity = currentActivity; + } + + /** + * Has the user completed the lesson? We don't care how (ie at end of + * sequence or after a "stop after activity") + */ + public boolean isComplete() { + return lessonComplete == LearnerProgress.LESSON_END_OF_DESIGN_COMPLETE + || lessonComplete == LearnerProgress.LESSON_IN_DESIGN_COMPLETE; + } + + /** + * The "real" value for lessonComplete. + * + * @return LESSON_NOT_COMPLETE, LESSON_END_OF_DESIGN_COMPLETE, + * LESSON_IN_DESIGN_COMPLETE + */ + public Byte getLessonComplete() { + return lessonComplete; + } + + /** + * Setter for property lessonComplete. + * + * @param lessonComplete + * New value of property lessonComplete. + */ + public void setLessonComplete(Byte lessonComplete) { + this.lessonComplete = lessonComplete; + } + + public Date getFinishDate() { + return finishDate; + } + + public void setFinishDate(Date finishDate) { + this.finishDate = finishDate; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } +} \ No newline at end of file Fisheye: Tag 65dc0e557544d65bf37488fbd11567599181ad84 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/lesson/ProgressCompletedActivity.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java (.../ILearnerProgressDAO.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java (.../ILearnerProgressDAO.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -196,4 +196,9 @@ * Get number of learners who are at the given activities at the moment. */ Map getNumUsersCurrentActivities(Long[] activityIds); -} + + /** + * Get the last attempt ID for the given learner and lesson. + */ + Integer getLearnerProgressArchiveMaxAttemptID(Integer userId, Long lessonId); +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LearnerProgressDAO.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LearnerProgressDAO.java (.../LearnerProgressDAO.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LearnerProgressDAO.java (.../LearnerProgressDAO.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -51,7 +51,8 @@ private final static String LOAD_PROGRESS_BY_LEARNER = "from LearnerProgress p where p.user.id = :learnerId and p.lesson.id = :lessonId"; - private final static String LOAD_PROGRESS_REFFERING_TO_ACTIVITY = "from LearnerProgress p where p.previousActivity = :activity or p.currentActivity = :activity or p.nextActivity = :activity "; + private final static String LOAD_PROGRESS_REFFERING_TO_ACTIVITY = "from LearnerProgress p " + + "where p.previousActivity = :activity or p.currentActivity = :activity or p.nextActivity = :activity "; private final static String LOAD_COMPLETED_PROGRESS_BY_LESSON = "FROM LearnerProgress p WHERE p.lessonComplete > 0 " + "AND p.lesson.id = :lessonId ORDER BY p.user.firstName , p.user.lastName , p.user.login "; @@ -99,7 +100,8 @@ + "ORDER BY prog.user.firstName , prog.user.lastName , prog.user.login "; private final static String COUNT_LEARNERS_BY_LESSON = "COUNT(*) FROM LearnerProgress prog WHERE prog.lesson.id = :lessonId"; - private final static String COUNT_LEARNERS_BY_LESSON_ORDER_CLAUSE = " ORDER BY prog.user.firstName ASC, prog.user.lastName ASC, prog.user.login ASC"; + private final static String COUNT_LEARNERS_BY_LESSON_ORDER_CLAUSE = " ORDER BY prog.user.firstName ASC, " + + "prog.user.lastName ASC, prog.user.login ASC"; // find Learners for the given Lesson first, then see if they have Progress, i.e. started the lesson private final static String LOAD_LEARNERS_BY_MOST_PROGRESS = "SELECT u.*, COUNT(comp.activity_id) AS comp_count FROM lams_lesson AS lesson " @@ -112,6 +114,9 @@ private final static String LOAD_LEARNERS_BY_MOST_PROGRESS_ORDER_CLAUSE = " GROUP BY u.user_id " + "ORDER BY prog.lesson_completed_flag DESC, comp_count DESC, u.first_name ASC, u.last_name ASC, u.login ASC"; + private final static String FIND_PROGRESS_ARCHIVE_MAX_ATTEMPT = "SELECT MAX(p.attemptId) FROM LearnerProgressArchive p " + + "WHERE p.user.id = :learnerId AND p.lesson.id = :lessonId"; + @Override public LearnerProgress getLearnerProgress(Long learnerProgressId) { return (LearnerProgress) getHibernateTemplate().get(LearnerProgress.class, learnerProgressId); @@ -467,4 +472,17 @@ } }); } + + @Override + public Integer getLearnerProgressArchiveMaxAttemptID(final Integer userId, final Long lessonId) { + HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); + return (Integer) hibernateTemplate.execute(new HibernateCallback() { + @Override + public Object doInHibernate(Session session) throws HibernateException { + Object value = session.createQuery(LearnerProgressDAO.FIND_PROGRESS_ARCHIVE_MAX_ATTEMPT) + .setInteger("learnerId", userId).setLong("lessonId", lessonId).uniqueResult(); + return value == null ? null : new Integer(((Number) value).intValue()); + } + }); + } } \ No newline at end of file Index: lams_learning/.classpath =================================================================== diff -u -rf964c1062c52d93be83ace1aa252b45deff63d5e -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/.classpath (.../.classpath) (revision f964c1062c52d93be83ace1aa252b45deff63d5e) +++ lams_learning/.classpath (.../.classpath) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -17,5 +17,6 @@ + Index: lams_learning/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_learning/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -15,6 +15,8 @@ message.activity.loading =The next task is loading. Please wait.... message.lesson.finished =Congratulations, {0}, you have finished. message.lesson.finishedCont =You have now completed the {0} lesson. You can return at anytime to this lesson and revisit and review activities by double clicking on the blue icons in the left hand progress bar. You can now close this window. +message.lesson.restart =You can give the lesson another an attempt by pressing this button: +message.lesson.restart.button =Restart exit.heading =You have exited from this Lesson. exit.message =You can resume this lesson using the Resume button. label.next.button =Next Index: lams_learning/src/java/org/lamsfoundation/lams/learning/service/ICoreLearnerService.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/src/java/org/lamsfoundation/lams/learning/service/ICoreLearnerService.java (.../ICoreLearnerService.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/service/ICoreLearnerService.java (.../ICoreLearnerService.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -21,7 +21,6 @@ * **************************************************************** */ - package org.lamsfoundation.lams.learning.service; import java.util.Set; @@ -51,21 +50,21 @@ public interface ICoreLearnerService extends ILearnerService { /** Get the I18N service. Used by actions for internationalising errors that go back to Flash */ - public MessageService getMessageService(); + MessageService getMessageService(); /** Get the user service. Used when the action needs the real user object, not just the userId */ - public IUserManagementService getUserManagementService(); + IUserManagementService getUserManagementService(); /** * Gets the lesson object for the given key. * */ - public Lesson getLesson(Long lessonID); + Lesson getLesson(Long lessonID); /** * Get the lesson data for a particular lesson. In a DTO format suitable for sending to the client. */ - public LessonDTO getLessonData(Long lessonId); + LessonDTO getLessonData(Long lessonId); /** * Joins a User to a a new lesson as a learner @@ -77,7 +76,7 @@ * @throws LearnerServiceException * in case of problems. */ - public LearnerProgress joinLesson(Integer learnerId, Long lessonID); + LearnerProgress joinLesson(Integer learnerId, Long lessonID); /** * This method navigate through all the tool activities for the given activity. For each tool activity, we look up @@ -88,7 +87,7 @@ * the learner progress we are processing. * @throws LamsToolServiceException */ - public void createToolSessionsIfNecessary(Activity activity, LearnerProgress learnerProgress); + void createToolSessionsIfNecessary(Activity activity, LearnerProgress learnerProgress); /** * Returns the current progress data of the User. @@ -101,9 +100,14 @@ * @throws LearnerServiceException * in case of problems. */ - public LearnerProgress getProgress(Integer learnerId, Long lessonId); + LearnerProgress getProgress(Integer learnerId, Long lessonId); /** + * Get the last attempt ID for the given learner and lesson. + */ + Integer getProgressArchiveMaxAttemptID(Integer userId, Long lessonId); + + /** * Returns the current progress data, in the DTO format required by the jsp progress screen, of the User. * * @param learnerId @@ -114,15 +118,15 @@ * @throws LearnerServiceException * in case of problems. */ - public Object[] getStructuredActivityURLs(Integer learnerId, Long lessonId); + Object[] getStructuredActivityURLs(Integer learnerId, Long lessonId); /** * Return the current progress data against progress id. * * @param progressId * @return */ - public LearnerProgress getProgressById(Long progressId); + LearnerProgress getProgressById(Long progressId); /** * Return the current progress data for a user for a lesson Returns a DTO suitable to send to Flash. @@ -133,7 +137,7 @@ * id * @return */ - public LearnerProgressDTO getProgressDTOByLessonId(Long lessonId, Integer learnerId); + LearnerProgressDTO getProgressDTOByLessonId(Long lessonId, Integer learnerId); /** * Marks an activity as attempted. Called when a user selects an OptionsActivity. @@ -149,8 +153,7 @@ * for branching and optional sequences for skipped sequences (e.g. force completed branching) * @return LearnerProgress */ - public LearnerProgress chooseActivity(Integer learnerId, Long lessonId, Activity activity, - Boolean clearCompletedFlag); + LearnerProgress chooseActivity(Integer learnerId, Long lessonId, Activity activity, Boolean clearCompletedFlag); /** * Calculates learner progress and returns the data required to be displayed to the learner (including URL(s)). This @@ -164,7 +167,7 @@ * @throws LearnerServiceException * in case of problems. */ - public LearnerProgress calculateProgress(Activity completedActivity, Integer learnerId, + LearnerProgress calculateProgress(Activity completedActivity, Integer learnerId, LearnerProgress currentLearnerProgress); /** @@ -180,7 +183,7 @@ * lesson id * @return the updated learner progress */ - public LearnerProgress completeActivity(Integer learnerId, Activity activity, LearnerProgress progress); + LearnerProgress completeActivity(Integer learnerId, Activity activity, LearnerProgress progress); /** * If specified activity is set to produce ToolOutput, calculates and stores mark to gradebook. @@ -204,7 +207,7 @@ * lesson id * @return the updated learner progress */ - public LearnerProgress completeActivity(Integer learnerId, Activity activity, Long lessonId); + LearnerProgress completeActivity(Integer learnerId, Activity activity, Long lessonId); /** * Retrieve all lessons that has been started, suspended or finished. All finished but archived lesson should not be @@ -214,7 +217,7 @@ * the user who intend to start a lesson * @return a list of active lessons. */ - public LessonDTO[] getActiveLessonsFor(Integer learnerId); + LessonDTO[] getActiveLessonsFor(Integer learnerId); /** * Mark the learner progress as restarting to indicate the current learner has exit the lesson. Doesn't use the @@ -223,7 +226,7 @@ * @param userId * @param lessonId */ - public void exitLesson(Integer learnerId, Long lessonId); + void exitLesson(Integer learnerId, Long lessonId); /** * Returns an activity according to the activity id. @@ -232,7 +235,7 @@ * the activity id. * @return the activity requested. */ - public Activity getActivity(Long activityId); + Activity getActivity(Long activityId); /** * Perform grouping for the learners who have started the lesson, based on the grouping activity. @@ -248,7 +251,7 @@ * of the grouping type * @return true if grouping done, false if waiting for grouping to occur */ - public boolean performGrouping(Long lessonId, Long groupingActivityId, Integer learnerId, boolean forceGrouping); + boolean performGrouping(Long lessonId, Long groupingActivityId, Integer learnerId, boolean forceGrouping); /** * Perform grouping for the learner, depending on his/hers choice. @@ -264,7 +267,7 @@ * @return true if the learner was successfully added to the group; false if the group was empty * @throws LearnerServiceException */ - public boolean learnerChooseGroup(Long lessonId, Long groupingActivityId, Long groupId, Integer learnerId) + boolean learnerChooseGroup(Long lessonId, Long groupingActivityId, Long groupId, Integer learnerId) throws LearnerServiceException; /** @@ -277,9 +280,9 @@ * @return the maximum number of learners per group;null if the requirement for equal number of * learners in groups was not set */ - public Integer calculateMaxNumberOfLearnersPerGroup(Long lessonId, Grouping grouping); + Integer calculateMaxNumberOfLearnersPerGroup(Long lessonId, Grouping grouping); - public Grouping getGrouping(Long groupingId); + Grouping getGrouping(Long groupingId); /** * Check up the gate status to go through the gate. This also updates the gate. This method should be used when we @@ -293,7 +296,7 @@ * if forceGate==true and the lesson is a preview lesson then the gate is opened straight away. * @return Updated gate details */ - public GateActivityDTO knockGate(Long gateActivityId, User knocker, boolean forceGate); + GateActivityDTO knockGate(Long gateActivityId, User knocker, boolean forceGate); /** * Check up the gate status to go through the gate. This also updates the gate. This method should be used when we @@ -309,23 +312,23 @@ * if forceGate==true and the lesson is a preview lesson then the gate is opened straight away. * @return Updated gate details */ - public GateActivityDTO knockGate(GateActivity gateActivity, User knocker, boolean forceGate); + GateActivityDTO knockGate(GateActivity gateActivity, User knocker, boolean forceGate); - public Set getGroupsForGate(GateActivity gate); + Set getGroupsForGate(GateActivity gate); /** * Get the learner url for a particular activity. * * @param learnerId * @param activityId */ - public String getLearnerActivityURL(Integer learnerId, Long activityId); + String getLearnerActivityURL(Integer learnerId, Long activityId); /** * Get the lesson for this activity. If the activity is not part of a lesson (ie is from an authoring design then it * will return null. */ - public Lesson getLessonByActivity(Activity activity); + Lesson getLessonByActivity(Activity activity); /** * @@ -339,7 +342,7 @@ * Activity moving to (being run) * @return updated Learner Progress */ - public LearnerProgress moveToActivity(Integer learnerId, Long lessonId, Activity fromActivity, Activity toActivity); + LearnerProgress moveToActivity(Integer learnerId, Long lessonId, Activity fromActivity, Activity toActivity); /** * Work out which branch to which a user should go. If the current lesson is a preview lesson, it will force the @@ -353,7 +356,7 @@ * the learner who triggers the grouping. * @throws LearnerServiceException */ - public SequenceActivity determineBranch(Lesson lesson, BranchingActivity branchingActivity, Integer learnerId) + SequenceActivity determineBranch(Lesson lesson, BranchingActivity branchingActivity, Integer learnerId) throws LearnerServiceException; /** @@ -368,6 +371,6 @@ * @return branchId of the desired branch * @throws LearnerServiceException */ - public SequenceActivity selectBranch(Lesson lesson, BranchingActivity branchingActivity, Integer learnerId, - Long branchId) throws LearnerServiceException; -} + SequenceActivity selectBranch(Lesson lesson, BranchingActivity branchingActivity, Integer learnerId, Long branchId) + throws LearnerServiceException; +} \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java (.../LearnerService.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java (.../LearnerService.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -21,7 +21,6 @@ * **************************************************************** */ - package org.lamsfoundation.lams.learning.service; import java.sql.Timestamp; @@ -410,6 +409,11 @@ public LearnerProgress getProgressById(Long progressId) { return learnerProgressDAO.getLearnerProgress(progressId); } + + @Override + public Integer getProgressArchiveMaxAttemptID(Integer userId, Long lessonId) { + return learnerProgressDAO.getLearnerProgressArchiveMaxAttemptID(userId, lessonId); + } /** * @see org.lamsfoundation.lams.learning.service.ICoreLearnerService#getProgressDTOByLessonId(java.lang.Long, @@ -418,11 +422,7 @@ @Override public LearnerProgressDTO getProgressDTOByLessonId(Long lessonId, Integer learnerId) { LearnerProgress progress = learnerProgressDAO.getLearnerProgressByLearner(learnerId, lessonId); - if (progress != null) { - return progress.getLearnerProgressData(); - } else { - return null; - } + return progress == null ? null : progress.getLearnerProgressData(); } /** Index: lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerServiceProxy.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerServiceProxy.java (.../LearnerServiceProxy.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerServiceProxy.java (.../LearnerServiceProxy.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -21,12 +21,12 @@ * **************************************************************** */ - package org.lamsfoundation.lams.learning.service; import javax.servlet.ServletContext; import org.lamsfoundation.lams.learning.web.util.ActivityMapping; +import org.lamsfoundation.lams.monitoring.service.IMonitoringService; import org.lamsfoundation.lams.tool.service.ILamsToolService; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.springframework.web.context.WebApplicationContext; @@ -47,7 +47,7 @@ /** * Return the learner domain service object. It will delegate to the Spring * helper method to retrieve the proper bean from Spring bean factory. - * + * * @param servletContext * the servletContext for current application * @return learner service object. @@ -60,7 +60,7 @@ * Return the user management domain service object. It will delegate to * the Spring helper method to retrieve the proper bean from Spring bean * factory - * + * * @param servletContext * the servletContext for current application * @return user management service object @@ -72,7 +72,7 @@ /** * Return the lams tool domain service object. It will delegate to the * Spring helper method to retrieve the proper bean from Spring bean factory. - * + * * @param serlvetContext * the servletContext for current application * @return tool service object @@ -81,9 +81,13 @@ return (ILamsToolService) LearnerServiceProxy.getDomainService(serlvetContext, "lamsToolService"); } + public static final IMonitoringService getMonitoringService(ServletContext servletContext) { + return (IMonitoringService) LearnerServiceProxy.getDomainService(servletContext, "monitoringService"); + } + /** * Return the activity mapping service object. - * + * * @param serlvetContext * the servletContext for current application * @return the activity mapping service object. @@ -94,7 +98,7 @@ /** * Retrieve the proper Spring bean from bean factory. - * + * * @param servletContext * the servletContext for current application * @return the Spring service bean. @@ -103,5 +107,4 @@ WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); return wac.getBean(serviceName); } - } \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/CompleteActivityAction.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/CompleteActivityAction.java (.../CompleteActivityAction.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/CompleteActivityAction.java (.../CompleteActivityAction.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -21,7 +21,6 @@ * **************************************************************** */ - package org.lamsfoundation.lams.learning.web.action; import java.io.IOException; @@ -80,7 +79,7 @@ // otherwise we may be using an old version of a lesson while a teacher is starting a // live edit, and then the lock flag can't be checked correctly. LearnerProgress progress = learnerService - .getProgressById(WebUtil.readLongParam(request, LearningWebUtil.PARAM_PROGRESS_ID, true)); + .getProgressById(WebUtil.readLongParam(request, LearningWebUtil.PARAM_PROGRESS_ID)); // if user has already completed the lesson - we need to let integrations servers know to come and pick up // updated marks (as it won't happen at lessoncomplete.jsp page) @@ -90,6 +89,7 @@ if (lessonFinishCallbackUrl != null) { request.setAttribute("lessonFinishUrl", lessonFinishCallbackUrl); } + request.setAttribute("lessonID", progress.getLesson().getLessonId()); } ActionForward forward = null; Index: lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/LearnerAction.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/LearnerAction.java (.../LearnerAction.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/LearnerAction.java (.../LearnerAction.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -21,12 +21,15 @@ * **************************************************************** */ - package org.lamsfoundation.lams.learning.web.action; import java.io.IOException; import java.io.PrintWriter; +import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -46,11 +49,16 @@ import org.lamsfoundation.lams.learning.web.util.LearningWebUtil; import org.lamsfoundation.lams.learningdesign.Activity; import org.lamsfoundation.lams.learningdesign.dto.ProgressActivityDTO; +import org.lamsfoundation.lams.lesson.CompletedActivityProgress; +import org.lamsfoundation.lams.lesson.CompletedActivityProgressArchive; import org.lamsfoundation.lams.lesson.LearnerProgress; +import org.lamsfoundation.lams.lesson.LearnerProgressArchive; import org.lamsfoundation.lams.lesson.Lesson; import org.lamsfoundation.lams.lesson.dto.LessonDTO; +import org.lamsfoundation.lams.monitoring.service.IMonitoringService; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; import org.lamsfoundation.lams.util.WebUtil; @@ -277,6 +285,53 @@ } /** + * Archives current learner progress and moves a learner back to the start of lesson. + */ + public ActionForward restartLesson(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + // fetch necessary parameters + long lessonID = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + ICoreLearnerService learnerService = LearnerServiceProxy.getLearnerService(getServlet().getServletContext()); + User user = LearningWebUtil.getUser(learnerService); + Integer userID = user.getUserId(); + + // find number of previous attempts + Integer attemptID = learnerService.getProgressArchiveMaxAttemptID(userID, lessonID); + if (attemptID == null) { + attemptID = 0; + } + attemptID++; + + // make a copy of attempted and completed activities + LearnerProgress learnerProgress = learnerService.getProgress(userID, lessonID); + Map attemptedActivities = new HashMap(learnerProgress.getAttemptedActivities()); + Map completedActivities = new HashMap(); + for (Entry entry : learnerProgress.getCompletedActivities().entrySet()) { + CompletedActivityProgressArchive activityArchive = new CompletedActivityProgressArchive(learnerProgress, + entry.getKey(), entry.getValue().getStartDate(), entry.getValue().getFinishDate()); + completedActivities.put(entry.getKey(), activityArchive); + } + + // save the historic attempt + LearnerProgressArchive learnerProgressArchive = new LearnerProgressArchive(user, learnerProgress.getLesson(), + attemptID, attemptedActivities, completedActivities, learnerProgress.getCurrentActivity(), + learnerProgress.getLessonComplete(), learnerProgress.getStartDate(), learnerProgress.getFinishDate()); + + // move learner to the beginning of lesson the same way Monitor can + IMonitoringService monitoringService = LearnerServiceProxy + .getMonitoringService(getServlet().getServletContext()); + monitoringService.forceCompleteActivitiesByUser(userID, userID, lessonID, + learnerProgress.getLesson().getLearningDesign().getFirstActivity().getActivityId(), true); + + IUserManagementService userManagementService = LearnerServiceProxy + .getUserManagementService(getServlet().getServletContext()); + userManagementService.save(learnerProgressArchive); + + // display Learner interface with updated data + return joinLesson(mapping, form, request, response); + } + + /** *

* Exit the current lesson that is running in the leaner window. It expects lesson id passed as parameter from flash * component. Index: lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/LessonCompleteActivityAction.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/LessonCompleteActivityAction.java (.../LessonCompleteActivityAction.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/web/action/LessonCompleteActivityAction.java (.../LessonCompleteActivityAction.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -21,7 +21,6 @@ * **************************************************************** */ - package org.lamsfoundation.lams.learning.web.action; import java.io.UnsupportedEncodingException; @@ -86,7 +85,9 @@ learnerProgress.getLesson()); if (lessonFinishCallbackUrl != null) { request.setAttribute("lessonFinishUrl", lessonFinishCallbackUrl); + } + request.setAttribute("lessonID", learnerProgress.getLesson().getLessonId()); return mapping.findForward("lessonComplete"); } Index: lams_learning/web/lessonComplete.jsp =================================================================== diff -u -r8612a46d3d15f0e990dfd75d9d077d43a15aee55 -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_learning/web/lessonComplete.jsp (.../lessonComplete.jsp) (revision 8612a46d3d15f0e990dfd75d9d077d43a15aee55) +++ lams_learning/web/lessonComplete.jsp (.../lessonComplete.jsp) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -54,6 +54,11 @@

+

+ + +

+

@@ -71,7 +76,6 @@

- Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r2592aad04b8de7be2f430cb66273f9bf78f75b11 -r65dc0e557544d65bf37488fbd11567599181ad84 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 2592aad04b8de7be2f430cb66273f9bf78f75b11) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 65dc0e557544d65bf37488fbd11567599181ad84) @@ -21,7 +21,6 @@ * **************************************************************** */ - package org.lamsfoundation.lams.monitoring.service; import java.io.IOException; @@ -1081,7 +1080,11 @@ @Override public String forceCompleteActivitiesByUser(Integer learnerId, Integer requesterId, long lessonId, Long activityId, boolean removeLearnerContent) { - securityService.isLessonMonitor(lessonId, requesterId, "force complete", true); + if (requesterId.equals(learnerId)) { + securityService.isLessonLearner(lessonId, requesterId, "force complete", true); + } else { + securityService.isLessonMonitor(lessonId, requesterId, "force complete", true); + } Lesson lesson = lessonDAO.getLesson(new Long(lessonId)); User learner = (User) baseDAO.find(User.class, learnerId);