Index: lams_learning/src/java/org/lamsfoundation/lams/learning/progress/ProgressEngine.java =================================================================== RCS file: /usr/local/cvsroot/lams_learning/src/java/org/lamsfoundation/lams/learning/progress/ProgressEngine.java,v diff -u -r1.21.4.2 -r1.21.4.3 --- lams_learning/src/java/org/lamsfoundation/lams/learning/progress/ProgressEngine.java 28 Mar 2007 06:41:46 -0000 1.21.4.2 +++ lams_learning/src/java/org/lamsfoundation/lams/learning/progress/ProgressEngine.java 30 Mar 2007 05:02:17 -0000 1.21.4.3 @@ -28,6 +28,7 @@ import java.util.LinkedList; import java.util.List; +import org.apache.log4j.Logger; import org.lamsfoundation.lams.learningdesign.Activity; import org.lamsfoundation.lams.learningdesign.ComplexActivity; import org.lamsfoundation.lams.learningdesign.LearningDesign; @@ -36,7 +37,6 @@ import org.lamsfoundation.lams.learningdesign.Transition; import org.lamsfoundation.lams.learningdesign.dao.IActivityDAO; import org.lamsfoundation.lams.lesson.LearnerProgress; -import org.lamsfoundation.lams.lesson.Lesson; import org.lamsfoundation.lams.lesson.ParallelWaitActivity; import org.lamsfoundation.lams.usermanagement.User; @@ -48,7 +48,8 @@ */ public class ProgressEngine { - + protected Logger log = Logger.getLogger(ProgressEngine.class); + private IActivityDAO activityDAO; /** @@ -116,14 +117,51 @@ if ( progress.getCompletedActivities().contains(ld.getFirstActivity()) ) { // special case - recalculating the appropriate current activity. return calculateProgress(progress.getUser(), ld.getFirstActivity(), progress); - } else { - progress.setCurrentActivity(ld.getFirstActivity()); + } else if ( canDoActivity(ld, ld.getFirstActivity()) ) { + progress.setCurrentActivity(ld.getFirstActivity()); progress.setNextActivity(ld.getFirstActivity()); setActivityAttempted(progress, ld.getFirstActivity()); return progress; + } else { + return clearProgressNowhereToGoNotCompleted(progress,"setUpStartPoint"); } } + /** Is it okay for me to do this activity? Most of the time yes - but you can do it + * if the learning design is marked for edit (due to live edit) and the activity isn't read + * only. This case should only occur if you have snuck past the stop gates while live edit + * was being set up. Hopefully never! + * @param design + * @param activity + * @return + */ + private boolean canDoActivity(LearningDesign design, Activity activity) { + return ! design.getEditOverrideLock() || activity.isActivityReadOnly() ; + } + + /** + * Oh, dear - nowhere to go. Probably because I failed canDoActivity(). + * Set the current activity and next activity to null, and the progress + * engine should then show the "Sequence Broken" screen. + * + * Writes a warning to the log if callingMethod is not null. If it is null, we assume + * the calling code has written out a warning/error already. + */ + private LearnerProgress clearProgressNowhereToGoNotCompleted(LearnerProgress progress, String callingMethod) { + if ( callingMethod != null ) { + log.warn("Learner "+progress.getUser().getFullName()+"("+progress.getUser().getUserId() + +") has a problem with the progress for lesson " + +progress.getLesson().getLessonName()+"("+progress.getLesson().getLessonId() + +"). Completed activities so far was "+progress.getCurrentCompletedActivitiesList() + +". Setting current and next activity to null. Problem detected in method "+callingMethod+"."); + } + + progress.setCurrentActivity(null); + progress.setNextActivity(null); + progress.setLessonComplete(false); + return progress; + } + /** Set the current activity as attempted. If it is a parallel activity, mark its children as attempted too. */ private void setActivityAttempted(LearnerProgress progress, Activity activity) { progress.setProgressState(activity,LearnerProgress.ACTIVITY_ATTEMPTED); @@ -170,21 +208,28 @@ populateCurrentCompletedActivityList(learnerProgress); - learnerProgress.setCurrentActivity(nextActivity); - - //we set the next activity to be the first child activity if it - //is a sequence activity. - if(nextActivity.isSequenceActivity()) - { - Activity firstActivityInSequence = - ((SequenceActivity)nextActivity).getFirstActivityInSequenceActivity(); - learnerProgress.setNextActivity(firstActivityInSequence); + if ( canDoActivity(learnerProgress.getLesson().getLearningDesign(), nextActivity) ) { + + learnerProgress.setCurrentActivity(nextActivity); + + //we set the next activity to be the first child activity if it + //is a sequence activity. + if(nextActivity.isSequenceActivity()) + { + Activity firstActivityInSequence = + ((SequenceActivity)nextActivity).getFirstActivityInSequenceActivity(); + learnerProgress.setNextActivity(firstActivityInSequence); + } + //set next activity as the activity follows the transition. + else + learnerProgress.setNextActivity(nextActivity); + setActivityAttempted(learnerProgress, nextActivity); + learnerProgress.setParallelWaiting(false); + + } else { + return clearProgressNowhereToGoNotCompleted(learnerProgress,"progressCompletedActivity"); } - //set next activity as the activity follows the transition. - else - learnerProgress.setNextActivity(nextActivity); - setActivityAttempted(learnerProgress, nextActivity); - learnerProgress.setParallelWaiting(false); + return learnerProgress; } else { @@ -233,26 +278,30 @@ Activity nextActivity = complexParent.getNextActivityByParent(completedActivity); - if (!isNextActivityValid(nextActivity)) - throw new ProgressException("Error occurred in progress engine." + if (!isNextActivityValid(nextActivity)) { + log.error("Error occurred in progress engine." + " Unexpected Null activity received when progressing" + " to the next activity within a incomplete parent activity:" + " Parent activity id [" + parent.getActivityId() + "]"); - - if(isParallelWaitActivity(nextActivity)) + learnerProgress = clearProgressNowhereToGoNotCompleted(learnerProgress,null); + } + else if(isParallelWaitActivity(nextActivity)) { learnerProgress.setParallelWaiting(true); // learnerProgress.setNextActivity(null); populateCurrentCompletedActivityList(learnerProgress); } - else + else if ( canDoActivity(learnerProgress.getLesson().getLearningDesign(), nextActivity) ) { learnerProgress.setParallelWaiting(false); learnerProgress.setNextActivity(nextActivity); setActivityAttempted(learnerProgress, nextActivity); populateCurrentCompletedActivityList(learnerProgress); + } + else { + learnerProgress = clearProgressNowhereToGoNotCompleted(learnerProgress, "progressParentActivity"); } } //recurvisely call back to calculateProgress to calculate completed Index: lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java =================================================================== RCS file: /usr/local/cvsroot/lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java,v diff -u -r1.61.4.2 -r1.61.4.3 --- lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java 28 Mar 2007 06:41:55 -0000 1.61.4.2 +++ lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java 30 Mar 2007 05:02:21 -0000 1.61.4.3 @@ -423,7 +423,7 @@ if ( toolSession == null ) { // something has gone wrong - maybe due to Live Edit. The tool session supplied by the tool doesn't exist. // have to go to a "can't do anything" screen and the user will have to hit resume. - returnURL = ActivityMapping.getProgressBrokenURL(); + returnURL = activityMapping.getProgressBrokenURL(); } else { // in the future, we might want to see if the entire tool session is completed at this point. @@ -477,7 +477,7 @@ } else if (currentProgress.getCompletedActivities().contains(activity)) { // return close window url - returnURL = ActivityMapping.getCloseURL(); + returnURL = activityMapping.getCloseURL(); } else { Index: lams_learning/src/java/org/lamsfoundation/lams/learning/web/util/ActivityMapping.java =================================================================== RCS file: /usr/local/cvsroot/lams_learning/src/java/org/lamsfoundation/lams/learning/web/util/ActivityMapping.java,v diff -u -r1.31.2.1 -r1.31.2.2 --- lams_learning/src/java/org/lamsfoundation/lams/learning/web/util/ActivityMapping.java 28 Mar 2007 06:41:36 -0000 1.31.2.1 +++ lams_learning/src/java/org/lamsfoundation/lams/learning/web/util/ActivityMapping.java 30 Mar 2007 05:02:20 -0000 1.31.2.2 @@ -86,7 +86,7 @@ //String strutsAction = getActivityAction(activity, progress); String strutsAction = this.activityMappingStrategy.getActivityAction(activity); - if (activity.isToolActivity()) + if (activity != null && activity.isToolActivity()) { // always use redirect false for a ToolActivity as ToolDisplayActivity // does it's own redirect @@ -157,13 +157,15 @@ actionForward = getActivityForward(progress.getNextActivity(), progress, redirect); - //setup activity into request for display - Activity realActivity = learnerService.getActivity(progress.getNextActivity().getActivityId()); - request.setAttribute(ActivityAction.ACTIVITY_REQUEST_ATTRIBUTE, realActivity); + if ( progress.getNextActivity() != null ) { + //setup activity into request for display + Activity realActivity = learnerService.getActivity(progress.getNextActivity().getActivityId()); + request.setAttribute(ActivityAction.ACTIVITY_REQUEST_ATTRIBUTE, realActivity); - LearningWebUtil.putActivityInRequest(request, + LearningWebUtil.putActivityInRequest(request, progress.getNextActivity(), learnerService); + } } } } @@ -344,7 +346,7 @@ Activity activity) { - if (activity.isToolActivity() || activity.isSystemToolActivity()) + if (activity != null && activity.isToolActivity() || activity.isSystemToolActivity()) { return WebUtil.convertToFullURL(getLearnerToolURL(lesson, ((Activity) activity), learner)); } else { @@ -368,13 +370,11 @@ this.activityMappingStrategy = activityMappingStrategy; } - public static String getCloseURL() { - String lamsUrl = Configuration.get(ConfigurationKeys.SERVER_URL) + LEARNING; - String closeUrl = lamsUrl + "/close.do"; - return closeUrl; + public String getCloseURL() { + return strutsActionToURL(this.activityMappingStrategy.getCloseWindowAction(), null, true); } - public static String getProgressBrokenURL() { + public String getProgressBrokenURL() { String lamsUrl = Configuration.get(ConfigurationKeys.SERVER_URL) + LEARNING; String closeUrl = lamsUrl + "/progressBroken.do"; return closeUrl; Index: lams_learning/src/java/org/lamsfoundation/lams/learning/web/util/ActivityMappingStrategy.java =================================================================== RCS file: /usr/local/cvsroot/lams_learning/src/java/org/lamsfoundation/lams/learning/web/util/ActivityMappingStrategy.java,v diff -u -r1.12 -r1.12.4.1 --- lams_learning/src/java/org/lamsfoundation/lams/learning/web/util/ActivityMappingStrategy.java 17 Sep 2006 06:17:57 -0000 1.12 +++ lams_learning/src/java/org/lamsfoundation/lams/learning/web/util/ActivityMappingStrategy.java 30 Mar 2007 05:02:20 -0000 1.12.4.1 @@ -35,6 +35,7 @@ */ public class ActivityMappingStrategy implements Serializable { + private static final String PROGRESS_BROKEN_ACTION = "/progressBroken.do"; /** * Returns the struts action used to display the specified activity. * @param activity, Activity to be displayed @@ -43,7 +44,11 @@ */ protected String getActivityAction(Activity activity) { String strutsAction = null; - if ( activity.isComplexActivity() ) { + if ( activity == null ) { + strutsAction = PROGRESS_BROKEN_ACTION; + } + else if ( activity.isComplexActivity() ) + { if ( activity.isParallelActivity() ) strutsAction = "/DisplayParallelActivity.do"; else if (activity.isOptionsActivity()) @@ -70,4 +75,17 @@ return "/LessonComplete.do"; } + /** + * Returns the struts action for triggering the window to close. + */ + protected String getCloseWindowAction() { + return "/close.do"; + } + + /** + * Returns the struts action for the error message that appears when the learner progress is broken, say be live edit. + */ + protected String getProgressBrokenAction() { + return PROGRESS_BROKEN_ACTION; + } }