/*==========================================================================*\
| $Id: UploadSubmissionPage.java,v 1.9 2011/06/08 02:21:32 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2008 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 com.webobjects.appserver.*;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.foundation.*;
import er.extensions.appserver.ERXDisplayGroup;
import er.extensions.eof.ERXQ;
import org.apache.log4j.Logger;
import org.webcat.archives.IArchiveEntry;
import org.webcat.core.*;
import org.webcat.ui.generators.JavascriptGenerator;
// -------------------------------------------------------------------------
/**
* This wizard page summarizes past submissions and allows a student
* to upload a program file for the current (new) submission.
*
* @author Stephen Edwards
* @author Last changed by $Author: stedwar2 $
* @version $Revision: 1.9 $, $Date: 2011/06/08 02:21:32 $
*/
public class UploadSubmissionPage
extends GraderSubmissionUploadComponent
{
//~ Constructors ..........................................................
// ----------------------------------------------------------
/**
* This is the default constructor
*
* @param context The page's context
*/
public UploadSubmissionPage( WOContext context )
{
super( context );
}
//~ KVC Attributes (must be public) .......................................
public AssignmentOffering offering;
public ERXDisplayGroup<Submission> submissionDisplayGroup;
/** submission item in the repetition */
public Submission aSubmission;
/** index in repetition */
public int index;
/** true if there are previous submissions */
public boolean hasPreviousSubmissions;
public Boolean showStudentSelector;
public ERXDisplayGroup<User> studentDisplayGroup;
public User student;
public User submitAsStudent;
public NSMutableArray<User> previousPartners;
public NSMutableArray<User> partnersForEditing;
public User aPartner;
public int partnerIndex;
public int extraColumnCount;
//~ 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)
{
log.debug("primeUser = " + wcSession().primeUser()
+ ", localUser = " + user());
if (showStudentSelector == null)
{
NSDictionary<?, ?> config =
wcSession().tabs.selectedDescendant().config();
showStudentSelector = Boolean.valueOf(
config != null
&& config.containsKey("showStudentSelector"));
}
if (offering == null)
{
offering = prefs().assignmentOffering();
}
if (showStudentSelector)
{
studentDisplayGroup.setMasterObject(offering.courseOffering());
if (submitAsStudent == null
&& studentDisplayGroup.displayedObjects().count() > 0)
{
submitAsStudent =
studentDisplayGroup.displayedObjects().objectAtIndex(0);
}
}
int currentSubNo = fillDisplayGroup(user());
hasPreviousSubmissions = submissionDisplayGroup.displayedObjects()
.count() > 0;
Submission latestSub =
Submission.latestSubmissionForAssignmentOfferingAndUser(
localContext(), offering, user());
if (previousPartners == null)
{
if (submissionInProcess().partners() != null)
{
previousPartners =
submissionInProcess().partners().mutableClone();
}
else
{
previousPartners = new NSMutableArray<User>();
if (latestSub != null)
{
for (Submission partneredSub :
latestSub.partneredSubmissions())
{
previousPartners.addObject(partneredSub.user());
}
}
}
}
if (partnersForEditing == null)
{
partnersForEditing = previousPartners.mutableClone();
}
Number maxSubmissions = offering
.assignment().submissionProfile().maxSubmissionsRaw();
okayToSubmit = (maxSubmissions == null
|| currentSubNo <= maxSubmissions.intValue());
if (okayToSubmit)
{
startSubmission(currentSubNo, user());
}
if (offering.dueDate() != null)
{
log.debug("due = " + offering.dueDate().getTime());
log.debug("grace = "
+ offering.assignment().submissionProfile().deadTimeDelta());
NSTimestamp deadline = new NSTimestamp(
offering.dueDate().getTime()
+ offering.assignment().submissionProfile().deadTimeDelta());
log.debug("time = " + deadline);
}
extraColumnCount = 0;
if (offering != null)
{
Assignment a = offering.assignment();
if (a.usesTAScore())
{
extraColumnCount++;
}
if (a.usesTestingScore())
{
extraColumnCount++;
}
if (a.usesToolCheckScore())
{
extraColumnCount++;
}
if (a.usesBonusesOrPenalties())
{
extraColumnCount++;
}
}
super.beforeAppendToResponse(response, context);
}
// ----------------------------------------------------------
@Override
public void flushNavigatorDerivedData()
{
offering = null;
super.flushNavigatorDerivedData();
}
// ----------------------------------------------------------
protected void afterAppendToResponse(
WOResponse response, WOContext context)
{
super.afterAppendToResponse(response, context);
oldBatchSize = submissionDisplayGroup.numberOfObjectsPerBatch();
oldBatchIndex = submissionDisplayGroup.currentBatchIndex();
cachedUploadedFile = submissionInProcess().uploadedFile();
cachedUploadedFileName = submissionInProcess().uploadedFileName();
cachedUploadedFileList = submissionInProcess().uploadedFileList();
}
// ----------------------------------------------------------
/**
* This method determines whether any embedded navigator will
* automatically pop up to force a selection and page reload.
* @return True if the navigator should start out by opening automatically.
*/
public boolean forceNavigatorSelection()
{
boolean result = super.forceNavigatorSelection();
if (!result)
{
// If the assignment is closed and the user is not allowed to
// submit to it (i.e., not a grader or instructor), then force the
// assignment offering selection to be null and pop open the
// navigator.
if (offering == null)
{
offering = prefs().assignmentOffering();
}
AssignmentOffering assnOff = offering;
if (!user().hasAdminPrivileges() &&
(assnOff == null || !assnOff.userCanSubmit(user())))
{
prefs().setAssignmentOfferingRelationship(null);
offering = null;
result = true;
}
}
return result;
}
// ----------------------------------------------------------
/**
* A predicate that indicates whether the user can proceed.
* As a side-effect, it sets the error message if the user cannot
* proceed.
*
* @return true if the user can proceed
*/
public boolean okayToSubmit()
{
if (!okayToSubmit)
{
error("You have already made the maximum allowed "
+ "number of submissions for this assignment.");
}
return okayToSubmit;
}
// ----------------------------------------------------------
public boolean nextEnabled()
{
return okayToSubmit();
}
// ----------------------------------------------------------
public WOComponent next()
{
if (log.isDebugEnabled())
{
log.debug("next():");
log.debug(" request = " + context().request());
log.debug(" form values = " + context().request().formValues());
log.debug(" multipart = "
+ context().request().isMultipartFormData());
}
if (showStudentSelector && submitAsStudent == null)
{
error("Please select a student for this submission.");
return null;
}
if (okayToSubmit)
{
NSTimestamp deadline = new NSTimestamp(
offering.dueDate().getTime()
+ offering.assignment().submissionProfile().deadTimeDelta());
log.debug("deadline = " + deadline);
log.debug("now = " + new NSTimestamp());
CourseOffering course = offering.courseOffering();
User primeUser =
wcSession().primeUser().localInstance(localContext());
if (deadline.before(new NSTimestamp())
&& !course.isInstructor(primeUser)
&& !course.isGrader(primeUser))
{
error("Unfortunately, the final deadline for this assignment "
+ "has passed. No more submissions are being accepted.");
return null;
}
boolean clearFileList = true;
if (!submissionInProcess().hasValidFileUpload())
{
submissionInProcess().setUploadedFile(cachedUploadedFile);
submissionInProcess().setUploadedFileName(
cachedUploadedFileName);
submissionInProcess().setUploadedFileList(
cachedUploadedFileList);
clearFileList = false;
}
if (!submissionInProcess().hasValidFileUpload())
{
error("Please select a file to upload.");
return null;
}
if (clearFileList)
{
submissionInProcess().setUploadedFileList(null);
}
if (submissionInProcess().uploadedFile().length() >
offering.assignment().submissionProfile()
.effectiveMaxFileUploadSize())
{
error("You file exceeds the file size limit for this "
+ "assignment ("
+ offering.assignment().submissionProfile()
.effectiveMaxFileUploadSize()
+ "). Please choose a smaller file.");
return null;
}
if (showStudentSelector)
{
setLocalUser(submitAsStudent);
int currentSubNo = fillDisplayGroup(user());
// restart the submission with a new user/number
submissionInProcess().startSubmission(user(), currentSubNo);
}
submissionInProcess().setPartners(partnersForEditing);
return super.next();
}
else
{
// If we get here, an error message has already been set
// in okayToSubmit(), so just refresh the page to show it.
return null;
}
}
// ----------------------------------------------------------
/**
* A boolean predicate that indicates that this assignment
* has instructor-provided instructions to show to the student.
*
* @return true if there are instructions to show
*/
public boolean hasInstructions()
{
String instructions = offering.assignment().fileUploadMessage();
return instructions != null && !instructions.equals("");
}
// ----------------------------------------------------------
/**
* Returns the file size for the currently uploaded file.
*
* @return the file size
*/
public long uploadedFileSize()
{
long size = 0L;
NSData file = submissionInProcess().uploadedFile();
if (file != null)
{
size = file.length();
}
return size;
}
// ----------------------------------------------------------
public WOComponent defaultAction()
{
log.debug("defaultAction()");
if (oldBatchSize != submissionDisplayGroup.numberOfObjectsPerBatch()
|| oldBatchIndex != submissionDisplayGroup.currentBatchIndex())
{
return null;
}
else
{
return super.defaultAction();
}
}
// ----------------------------------------------------------
public void cancelLocalChanges()
{
clearSubmission();
resetPrimeUser();
super.cancelLocalChanges();
}
// ----------------------------------------------------------
public boolean allowsAllOfferingsForCourse()
{
return false;
}
// ----------------------------------------------------------
public void takeValuesFromRequest(WORequest request, WOContext context)
{
try
{
super.takeValuesFromRequest(request, context);
}
catch (Exception e)
{
// Ignore it
}
}
// ----------------------------------------------------------
public String permalink()
{
if (offering != null)
{
return offering.permalink();
}
else
{
return null;
}
}
// ----------------------------------------------------------
public void setPermalink(String value)
{
// Do nothing.
}
// ----------------------------------------------------------
private int fillDisplayGroup(User user)
{
NSArray<Submission> submissions =
Submission.submissionsForAssignmentOfferingAndUser(
localContext(), offering, user);
submissionDisplayGroup.setObjectArray(submissions);
int currentSubNo = submissions.count() + 1;
for (int i = 0; i < submissions.count(); i++)
{
int sno = submissions.objectAtIndex(i).submitNumber();
if (sno >= currentSubNo)
{
currentSubNo = sno + 1;
}
}
return currentSubNo;
}
// ----------------------------------------------------------
public String showEditPartnersDialogScript()
{
return "dijit.byId('editPartnersDialog').show();";
}
// ----------------------------------------------------------
public JavascriptGenerator partnersChanged()
{
previousPartners = partnersForEditing.mutableClone();
JavascriptGenerator script = new JavascriptGenerator();
script.refresh("partnersPane").
dijit("editPartnersDialog").call("hide");
return script;
}
// ----------------------------------------------------------
public EOQualifier qualifierForStudentsInCourse()
{
CourseOffering courseOffering = offering.courseOffering();
NSArray<CourseOffering> offerings =
CourseOffering.offeringsForSemesterAndCourse(localContext(),
courseOffering.course(),
courseOffering.semester());
EOQualifier[] enrollmentQuals = new EOQualifier[offerings.count()];
int i = 0;
for (CourseOffering co : offerings)
{
enrollmentQuals[i++] = User.enrolledIn.is(co);
}
return ERXQ.or(enrollmentQuals);
}
//~ Instance/static variables .............................................
/** Saves the state of the batch navigator to detect setting changes */
protected int oldBatchSize;
/** Saves the state of the batch navigator to detect setting changes */
protected int oldBatchIndex;
protected NSData cachedUploadedFile;
protected String cachedUploadedFileName;
protected NSArray<IArchiveEntry> cachedUploadedFileList;
/** True if the user has not reached the maximum allowable submissions
* for this assignment and its okay to submit another time.
*/
protected boolean okayToSubmit;
static Logger log = Logger.getLogger( UploadSubmissionPage.class );
}