/*==========================================================================*\ | $Id: NewAssignmentPage.java,v 1.2 2010/09/27 04:23:20 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2010 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT 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 Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.grader; import java.util.GregorianCalendar; import org.apache.log4j.Logger; import org.webcat.core.Course; import org.webcat.core.CourseOffering; import org.webcat.core.Semester; import org.webcat.ui.generators.JavascriptGenerator; import org.webcat.ui.util.ComponentIDGenerator; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WOResponse; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSTimestamp; import com.webobjects.foundation.NSValidation; import er.extensions.foundation.ERXArrayUtilities; //------------------------------------------------------------------------- /** * Allows the user to create a new assignment + offering. * * @author Stephen Edwards * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.2 $, $Date: 2010/09/27 04:23:20 $ */ public class NewAssignmentPage extends GraderCourseComponent { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Creates a new object. * * @param context The context to use */ public NewAssignmentPage(WOContext context) { super(context); } //~ KVC Attributes (must be public) ....................................... public String aName; public String title; public String targetCourse; public boolean reuseOpen = false; public ComponentIDGenerator idFor; public Semester toSemester; public CourseOffering toCourseOffering; public Assignment assignmentToReoffer; public Semester semesterInRepetition; public NSArray<CourseOffering> toCourseOfferings; public CourseOffering courseOfferingInRepetition; public NSArray<Assignment> assignments; public Assignment assignmentInRepetition; //~ Methods ............................................................... // ---------------------------------------------------------- /** * Adds to the response of the page * * @param response The response being built * @param context The context of the request */ protected void beforeAppendToResponse( WOResponse response, WOContext context) { idFor = new ComponentIDGenerator(this); if (coreSelections().course() == null && coreSelections().courseOffering() == null) { if (prefs().assignmentOffering() != null) { coreSelections().setCourseRelationship( prefs().assignmentOffering().courseOffering().course()); } else if (prefs().assignment() != null && prefs().assignment().offerings().count() > 0) { coreSelections().setCourseRelationship( prefs().assignment().offerings().objectAtIndex(0) .courseOffering().course()); } } if (coreSelections().course() == null && coreSelections().courseOffering() == null) { targetCourse = "Please select a course ->"; } else if (coreSelections().course() != null) { Semester semester = coreSelections().semester(); targetCourse = coreSelections().course().toString() + " (all offerings, " + ((semester == null) ? "all semesters" : semester.toString()) + ")"; forAllSections = Boolean.TRUE; } else { targetCourse = coreSelections().courseOffering().compactName(); if (forAllSections == null) { forAllSections = Boolean.TRUE; } } super.beforeAppendToResponse(response, context); } // ---------------------------------------------------------- public boolean allowsAllSemesters() { return false; } // ---------------------------------------------------------- public boolean allowsAllOfferingsForCourse() { return true; } // ---------------------------------------------------------- public boolean requiresAssignmentOffering() { // Want to show all offerings for this assignment. return false; } // ---------------------------------------------------------- public WOComponent reoffer() { AssignmentOffering newOffering = AssignmentOffering.create( localContext(), assignmentToReoffer, toCourseOffering); localContext().insertObject(newOffering); prefs().setAssignmentOfferingRelationship(newOffering); prefs().setAssignmentRelationship(assignmentToReoffer); configureNewAssignmentOffering(newOffering, null); applyLocalChanges(); return super.next(); } // ---------------------------------------------------------- /** * Create a new assignment object as well as its associated assignment * offering objects, and then move on to an edit page. * @see org.webcat.core.WCComponent#next() */ public WOComponent next() { if (aName == null || aName.length() == 0) { error("Please enter a name for your assignment."); return null; } Semester semester = coreSelections().semester(); Course course = coreSelections().course(); NSArray<CourseOffering> offerings = null; if (course == null && forAllSections() && coreSelections().courseOffering() != null) { course = coreSelections().courseOffering().course(); semester = coreSelections().courseOffering().semester(); } if (course != null) { offerings = course.offerings(); if (semester != null) { NSMutableArray<CourseOffering> fullOfferings = offerings.mutableClone(); for (int i = 0; i < fullOfferings.count(); i++) { if (fullOfferings.objectAtIndex(i).semester() != semester) { fullOfferings.remove(i); i--; } } offerings = fullOfferings; } } else { offerings = new NSArray<CourseOffering>( coreSelections().courseOffering()); } if (offerings == null || offerings.count() == 0) { error("Please select a course in which to create the assignment."); return null; } log.debug("creating new assignment"); Assignment newAssignment = Assignment.create(localContext(), false); newAssignment.setShortDescription(title); newAssignment.setAuthorRelationship(user()); // Make sure the name is set first, so that date guessing works // correctly. newAssignment.setName(aName); NSArray<AssignmentOffering> similar = AssignmentOffering .offeringsWithSimilarNames( localContext(), aName, course, 1); if (similar.count() > 0) { AssignmentOffering model = similar.objectAtIndex(0); newAssignment.setSubmissionProfile( model.assignment().submissionProfile()); newAssignment.setTrackOpinions( model.assignment().trackOpinions()); for (Step oldStep : model.assignment().steps()) { Step newStep = Step.create(localContext(), false); newStep.setAssignmentRelationship(newAssignment); newStep.setOrder(oldStep.order()); newStep.setTimeout(oldStep.timeout()); newStep.setConfigRelationship(oldStep.config()); newStep.setGradingPluginRelationship(oldStep.gradingPlugin()); } } NSTimestamp common = newAssignment.commonOfferingsDueDate(); AssignmentOffering firstOffering = null; for (CourseOffering offering: offerings) { log.debug("creating new assignment offering for " + offering); AssignmentOffering newOffering = AssignmentOffering.create( localContext(), newAssignment, offering); if (firstOffering == null) { firstOffering = newOffering; } prefs().setAssignmentOfferingRelationship(newOffering); configureNewAssignmentOffering(newOffering, common); } // Now clear the name so that the validation check will be performed newAssignment.setName(null); try { newAssignment.validateName(aName); } catch (NSValidation.ValidationException e) { error(e); cancelLocalChanges(); return null; } // Finally, put the name back newAssignment.setName(aName); applyLocalChanges(); prefs().setAssignmentRelationship(newAssignment); prefs().setAssignmentOfferingRelationship(firstOffering); applyLocalChanges(); return super.next(); } // ---------------------------------------------------------- public void configureNewAssignmentOffering( AssignmentOffering newOffering, NSTimestamp commonDueDate) { NSTimestamp ts = new NSTimestamp(); // first, look for any other assignments, and use their due date // as a default { AssignmentOffering other = null; System.out.println("ao = " + newOffering); System.out.println("assignment = " + newOffering.assignment()); System.out.println("offerings = " + newOffering.assignment().offerings()); NSArray<AssignmentOffering> others = newOffering.assignment().offerings(); for (AssignmentOffering ao : others) { if (ao != newOffering) { other = ao; break; } } if (other == null) { if (commonDueDate != null) { ts = commonDueDate; } else { GregorianCalendar dueDateTime = new GregorianCalendar(); dueDateTime.setTime(ts .timestampByAddingGregorianUnits(0, 0, 15, 18, 55, 00)); dueDateTime.set(GregorianCalendar.AM_PM, GregorianCalendar.PM ); dueDateTime.set(GregorianCalendar.HOUR , 11); dueDateTime.set(GregorianCalendar.MINUTE , 55); dueDateTime.set(GregorianCalendar.SECOND , 0); ts = new NSTimestamp(dueDateTime.getTime()); } } else { ts = other.dueDate(); } } // Next, look for assignments for this course with similar names, // and try to spot a trend String name1 = newOffering.assignment().name(); if (name1 != null) { NSMutableArray<AssignmentOffering> others = AssignmentOffering.offeringsWithSimilarNames( localContext(), name1, newOffering.courseOffering(), 2); if (others.count() > 1) { AssignmentOffering ao1 = others.objectAtIndex(0); GregorianCalendar ao1DateTime = new GregorianCalendar(); ao1DateTime.setTime(ao1.dueDate()); AssignmentOffering ao2 = others.objectAtIndex(1); GregorianCalendar ao2DateTime = new GregorianCalendar(); ao2DateTime.setTime(ao2.dueDate()); if (ao1DateTime.get(GregorianCalendar.HOUR_OF_DAY) == ao2DateTime.get(GregorianCalendar.HOUR_OF_DAY) && ao1DateTime.get(GregorianCalendar.MINUTE) == ao2DateTime.get(GregorianCalendar.MINUTE) ) { int days = ao1DateTime.get(GregorianCalendar.DAY_OF_YEAR) - ao2DateTime.get(GregorianCalendar.DAY_OF_YEAR); if (days < 0) { GregorianCalendar yearLen = new GregorianCalendar( ao1DateTime.get(GregorianCalendar.YEAR), 0, 1); yearLen.add(GregorianCalendar.DAY_OF_YEAR, -1); days += yearLen.get(GregorianCalendar.DAY_OF_YEAR); } log.debug("day gap: " + days); log.debug("old time: " + ao1DateTime); ao1DateTime.add(GregorianCalendar.DAY_OF_YEAR, days); GregorianCalendar today = new GregorianCalendar(); while (today.after(ao1DateTime)) { ao1DateTime.add(GregorianCalendar.DAY_OF_YEAR, 7); } log.debug("new time: " + ao1DateTime); ts = new NSTimestamp(ao1DateTime.getTime()); } else { ts = new NSTimestamp( adjustTimeLike(ts, ao1DateTime).getTime()); } } else if (others.count() > 0) { AssignmentOffering ao = others.objectAtIndex(0); GregorianCalendar aoDateTime = new GregorianCalendar(); aoDateTime.setTime(ao.dueDate()); ts = new NSTimestamp( adjustTimeLike(ts, aoDateTime).getTime()); } } newOffering.setDueDate(ts); } // ---------------------------------------------------------- public boolean forAllSections() { return forAllSections != null && forAllSections.booleanValue(); } // ---------------------------------------------------------- public void setForAllSections(boolean value) { forAllSections = Boolean.valueOf(value); } // ---------------------------------------------------------- public boolean hasMultipleSections() { Course course = coreSelections().courseOffering().course(); Semester semester = coreSelections().courseOffering().semester(); NSArray<CourseOffering> offerings = CourseOffering.offeringsForSemesterAndCourse( localContext(), course, semester); return offerings != null && offerings.count() > 1; } // ---------------------------------------------------------- public NSArray<Semester> semesters() { if ( semesters == null ) { semesters = Semester.allObjectsOrderedByStartDate( localContext() ); toSemester = coreSelections().semester(); updateReofferPane(); } return semesters; } // ---------------------------------------------------------- public JavascriptGenerator updateReofferPane() { if (myCourses == null) { myCourses = ERXArrayUtilities.sortedArraySortedWithKeys( user().teaching(), new NSArray<String>( new String[] {"course.number", "label", "crn" }), null); } if (savedToSemester != toSemester) { toCourseOfferings = null; savedToSemester = toSemester; } if (toCourseOfferings == null) { toCourseOffering = null; // TODO: collapse the next two statements and eliminate the // temporary, once it will compile without warnings against // WONDER. @SuppressWarnings("unchecked") NSArray<CourseOffering> newList = ERXArrayUtilities.filteredArrayWithQualifierEvaluation( myCourses, CourseOffering.semester.eq(toSemester)); toCourseOfferings = newList; } if (toCourseOffering == null) { assignments = null; Course selected = coreSelections().course(); if (selected == null && coreSelections().courseOffering() != null) { selected = coreSelections().courseOffering().course(); } if (selected != null) { toCourseOffering = (CourseOffering) ERXArrayUtilities.firstObjectWithValueForKeyPath( toCourseOfferings, selected, "course"); } } if (savedToCourseOffering != toCourseOffering) { assignments = null; savedToCourseOffering = toCourseOffering; } if (assignments == null && toCourseOffering != null) { assignmentToReoffer = null; // TODO: collapse the next two statements and eliminate the // temporary, once it will compile without warnings against // WONDER. @SuppressWarnings("unchecked") NSArray<Assignment> newList = ERXArrayUtilities.filteredArrayWithQualifierEvaluation( Assignment.assignmentsForReuseInCourse( localContext(), toCourseOffering.course(), toCourseOffering), new Assignment.NonDuplicateAssignmentNameQualifier( toCourseOffering)); assignments = newList; } if (assignmentToReoffer == null && assignments != null && assignments.count() > 0) { assignmentToReoffer = assignments.objectAtIndex(0); } reuseOpen = true; return new JavascriptGenerator() .refresh((String) idFor.valueForKey("reofferPane")); } //~ Private Methods ....................................................... // ---------------------------------------------------------- private GregorianCalendar adjustTimeLike( NSTimestamp starting, GregorianCalendar similarTo) { GregorianCalendar result = new GregorianCalendar(); result.setTime(starting); // First, copy the time and day of the week from the old // assignment result.set(GregorianCalendar.HOUR_OF_DAY, similarTo.get( GregorianCalendar.HOUR_OF_DAY)); result.set(GregorianCalendar.MINUTE, similarTo.get( GregorianCalendar.MINUTE)); result.set(GregorianCalendar.SECOND, similarTo.get( GregorianCalendar.SECOND)); result.set(GregorianCalendar.DAY_OF_WEEK, similarTo.get( GregorianCalendar.DAY_OF_WEEK)); // jump ahead by weeks until we're in the future GregorianCalendar today = new GregorianCalendar(); while (today.after(result)) { result.add(GregorianCalendar.DAY_OF_YEAR, 7); } return result; } //~ Instance/static variables ............................................. private NSArray<Semester> semesters; private Boolean forAllSections = null; private Semester savedToSemester; private CourseOffering savedToCourseOffering; private NSArray<CourseOffering> myCourses; static Logger log = Logger.getLogger(NewAssignmentPage.class); }