/**********************************************************************************
*
* $Id: AssignmentBean.java $
*
***********************************************************************************
*
* Copyright (c) 2005, 2006, 2007, 2008, 2009 The Sakai Foundation, The MIT Corporation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.tool.gradebook.ui;
import java.io.Serializable;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.model.SelectItem;
import javax.faces.validator.ValidatorException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord;
import org.sakaiproject.section.api.facade.Role;
import org.sakaiproject.service.gradebook.shared.ConflictingAssignmentNameException;
import org.sakaiproject.service.gradebook.shared.StaleObjectModificationException;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.tool.gradebook.Assignment;
import org.sakaiproject.tool.gradebook.Category;
import org.sakaiproject.tool.gradebook.Gradebook;
import org.sakaiproject.tool.gradebook.jsf.FacesUtil;
import org.sakaiproject.util.ResourceLoader;
import org.sakaiproject.service.gradebook.shared.MultipleAssignmentSavingException;
import org.sakaiproject.service.gradebook.shared.GradebookService;
public class AssignmentBean extends GradebookDependentBean implements Serializable {
private static final Log logger = LogFactory.getLog(AssignmentBean.class);
private Long assignmentId;
private Assignment assignment;
private List categoriesSelectList;
private String assignmentCategory;
// added to support bulk gradebook item creation
public List newBulkItems;
public int numTotalItems = 1;
public List addItemSelectList;
public static final String UNASSIGNED_CATEGORY = "unassigned";
/**
* To add the proper number of blank gradebook item objects for bulk creation
*/
private static final int NUM_EXTRA_ASSIGNMENT_ENTRIES = 50;
protected void init() {
if (logger.isDebugEnabled()) logger.debug("init assignment=" + assignment);
if (assignment == null) {
if (assignmentId != null) {
assignment = getGradebookManager().getAssignment(assignmentId);
}
if (assignment == null) {
// it is a new assignment
assignment = new Assignment();
assignment.setReleased(true);
}
}
Category assignCategory = assignment.getCategory();
if (assignCategory != null) {
assignmentCategory = assignCategory.getId().toString();
}
else {
assignmentCategory = getLocalizedString("cat_unassigned");
}
categoriesSelectList = new ArrayList();
// The first choice is always "Unassigned"
categoriesSelectList.add(new SelectItem(UNASSIGNED_CATEGORY, FacesUtil.getLocalizedString("cat_unassigned")));
List gbCategories = getGradebookManager().getCategories(getGradebookId());
if (gbCategories != null && gbCategories.size() > 0)
{
Iterator catIter = gbCategories.iterator();
while (catIter.hasNext()) {
Category cat = (Category) catIter.next();
categoriesSelectList.add(new SelectItem(cat.getId().toString(), cat.getName()));
}
}
// To support bulk creation of assignments
if (newBulkItems == null) {
newBulkItems = new ArrayList();
}
// initialize the number of items to add dropdown
addItemSelectList = new ArrayList();
addItemSelectList.add(new SelectItem("0", ""));
for (int i = 1; i < NUM_EXTRA_ASSIGNMENT_ENTRIES; i++) {
addItemSelectList.add(new SelectItem(new Integer(i).toString(), new Integer(i).toString()));
}
for (int i = newBulkItems.size(); i < NUM_EXTRA_ASSIGNMENT_ENTRIES; i++) {
BulkAssignmentDecoratedBean item = getNewAssignment();
if (i == 0) {
item.setSaveThisItem("true");
}
newBulkItems.add(item);
}
}
private BulkAssignmentDecoratedBean getNewAssignment() {
Assignment assignment = new Assignment();
assignment.setReleased(true);
BulkAssignmentDecoratedBean bulkAssignmentDecoBean = new BulkAssignmentDecoratedBean(assignment, getItemCategoryString(assignment));
return bulkAssignmentDecoBean;
}
private String getItemCategoryString(Assignment assignment) {
String assignmentCategory;
Category assignCategory = assignment.getCategory();
if (assignCategory != null) {
assignmentCategory = assignCategory.getId().toString();
}
else {
assignmentCategory = getLocalizedString("cat_unassigned");
}
return assignmentCategory;
}
/**
* Used to check if all gradebook items are valid before saving. Due to the way JSF works, had to turn off
* validators for bulk assignments so had to perform checks here.
* This is an all-or-nothing save, ie, either all items are OK and we save them all, or return to add page
* and highlight errors.
*/
public String saveNewAssignment() {
String resultString = "overview";
boolean saveAll = true;
// keep list of new assignment names just in case
// duplicates entered on screen
List newAssignmentNameList = new ArrayList();
// used to hold assignments that are OK since we
// need to determine if all are correct before saving
List itemsToSave = new ArrayList();
Iterator assignIter = newBulkItems.iterator();
int i = 0;
while (i < numTotalItems && assignIter.hasNext()) {
BulkAssignmentDecoratedBean bulkAssignDecoBean = (BulkAssignmentDecoratedBean) assignIter.next();
if (bulkAssignDecoBean.getBlnSaveThisItem()) {
Assignment bulkAssignment = bulkAssignDecoBean.getAssignment();
// Check for blank entry else check if duplicate within items to be
// added or with item currently in gradebook.
if ("".equals(bulkAssignment.getName().toString().trim())) {
bulkAssignDecoBean.setBulkNoTitleError("blank");
saveAll = false;
resultString = "failure";
}
else if (newAssignmentNameList.contains(bulkAssignment.getName().trim()) ||
! getGradebookManager().checkValidName(getGradebookId(), bulkAssignment)){
bulkAssignDecoBean.setBulkNoTitleError("dup");
saveAll = false;
resultString = "failure";
}
else {
bulkAssignDecoBean.setBulkNoTitleError("OK");
newAssignmentNameList.add(bulkAssignment.getName().trim());
}
// Check if points possible is blank else convert to double. Exception at else point
// means non-numeric value entered.
if (bulkAssignDecoBean.getPointsPossible() == null || ("".equals(bulkAssignDecoBean.getPointsPossible().trim()))) {
bulkAssignDecoBean.setBulkNoPointsError("blank");
saveAll = false;
resultString = "failure";
}
else {
try {
// Check if PointsPossible uses local number format
String strPointsPossible = bulkAssignDecoBean.getPointsPossible();
NumberFormat nf = NumberFormat.getInstance(new ResourceLoader().getLocale());
double dblPointsPossible = new Double (nf.parse(strPointsPossible).doubleValue());
// Added per SAK-13459: did not validate if point value was valid (> zero)
if (dblPointsPossible > 0) {
// No more than 2 decimal places can be entered.
BigDecimal bd = new BigDecimal(dblPointsPossible);
bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP); // Two decimal places
double roundedVal = bd.doubleValue();
double diff = dblPointsPossible - roundedVal;
if(diff != 0) {
saveAll = false;
resultString = "failure";
bulkAssignDecoBean.setBulkNoPointsError("precision");
}
else {
bulkAssignDecoBean.setBulkNoPointsError("OK");
bulkAssignDecoBean.getAssignment().setPointsPossible(new Double(bulkAssignDecoBean.getPointsPossible()));
}
}
else {
saveAll = false;
resultString = "failure";
bulkAssignDecoBean.setBulkNoPointsError("invalid");
}
}
catch (Exception e) {
bulkAssignDecoBean.setBulkNoPointsError("NaN");
saveAll = false;
resultString = "failure";
}
}
if (saveAll) {
bulkAssignDecoBean.getAssignment().setCategory(retrieveSelectedCategory(bulkAssignDecoBean.getCategory()));
itemsToSave.add(bulkAssignDecoBean.getAssignment());
}
// Even if errors increment since we need to go back to add page
i++;
}
}
// Now ready to save, the only problem is due to duplicate names.
if (saveAll) {
try {
getGradebookManager().createAssignments(getGradebookId(), itemsToSave);
String authzLevel = (getGradebookBean().getAuthzService().isUserAbleToGradeAll(getGradebookUid())) ?"instructor" : "TA";
for (Iterator gbItemIter = itemsToSave.iterator(); gbItemIter.hasNext();) {
String itemName = ((Assignment) gbItemIter.next()).getName();
FacesUtil.addRedirectSafeMessage(getLocalizedString("add_assignment_save",
new String[] {itemName}));
getGradebookBean().getEventTrackingService().postEvent("gradebook.newItem","/gradebook/"+getGradebookId()+"/"+itemName+"/"+authzLevel);
}
}
catch (MultipleAssignmentSavingException e) {
FacesUtil.addErrorMessage(FacesUtil.getLocalizedString("validation_messages_present"));
resultString = "failure";
}
}
else {
// There are errors so need to put an error message at top
FacesUtil.addErrorMessage(FacesUtil.getLocalizedString("validation_messages_present"));
}
return resultString;
}
public String updateAssignment() {
try {
Category category = retrieveSelectedCategory();
assignment.setCategory(category);
Assignment originalAssignment = getGradebookManager().getAssignment(assignmentId);
Double origPointsPossible = originalAssignment.getPointsPossible();
Double newPointsPossible = assignment.getPointsPossible();
boolean scoresEnteredForAssignment = getGradebookManager().isEnteredAssignmentScores(assignmentId);
/* If grade entry by percentage or letter and the points possible has changed for this assignment,
* we need to convert all of the stored point values to retain the same value
*/
if ((getGradeEntryByPercent() || getGradeEntryByLetter()) && scoresEnteredForAssignment) {
if (!newPointsPossible.equals(origPointsPossible)) {
List enrollments = getSectionAwareness().getSiteMembersInRole(getGradebookUid(), Role.STUDENT);
List studentUids = new ArrayList();
for(Iterator iter = enrollments.iterator(); iter.hasNext();) {
studentUids.add(((EnrollmentRecord)iter.next()).getUser().getUserUid());
}
getGradebookManager().convertGradePointsForUpdatedTotalPoints(getGradebook(), originalAssignment, assignment.getPointsPossible(), studentUids);
}
}
getGradebookManager().updateAssignment(assignment);
long dueDateMillis = -1;
Date dueDate = assignment.getDueDate();
if (dueDate != null) dueDateMillis = dueDate.getTime();
getGradebookBean().getEventTrackingService().postEvent("gradebook.updateAssignment","/gradebook/"+getGradebookUid()+"/"+assignment.getName()+"/"+assignment.getPointsPossible()+"/"+dueDateMillis+"/"+assignment.isReleased()+"/"+assignment.isCounted()+"/"+getAuthzLevel());
if ((!origPointsPossible.equals(newPointsPossible)) && scoresEnteredForAssignment) {
if (getGradeEntryByPercent())
FacesUtil.addRedirectSafeMessage(getLocalizedString("edit_assignment_save_percentage", new String[] {assignment.getName()}));
else if (getGradeEntryByLetter())
FacesUtil.addRedirectSafeMessage(getLocalizedString("edit_assignment_save_converted", new String[] {assignment.getName()}));
else
FacesUtil.addRedirectSafeMessage(getLocalizedString("edit_assignment_save_scored", new String[] {assignment.getName()}));
} else {
FacesUtil.addRedirectSafeMessage(getLocalizedString("edit_assignment_save", new String[] {assignment.getName()}));
}
} catch (ConflictingAssignmentNameException e) {
logger.error(e);
FacesUtil.addErrorMessage(getLocalizedString("edit_assignment_name_conflict_failure"));
return "failure";
} catch (StaleObjectModificationException e) {
logger.error(e);
FacesUtil.addErrorMessage(getLocalizedString("edit_assignment_locking_failure"));
return "failure";
}
return navigateBack();
}
public String navigateToAssignmentDetails() {
return navigateBack();
}
/**
* Go to assignment details page. InstructorViewBean contains duplicate
* of this method, cannot migrate up to GradebookDependentBean since
* needs assignmentId, which is defined here.
*/
public String navigateBack() {
String breadcrumbPage = getBreadcrumbPage();
final Boolean middle = new Boolean((String) SessionManager.getCurrentToolSession().getAttribute("middle"));
if (breadcrumbPage == null || middle) {
AssignmentDetailsBean assignmentDetailsBean = (AssignmentDetailsBean)FacesUtil.resolveVariable("assignmentDetailsBean");
assignmentDetailsBean.setAssignmentId(assignmentId);
assignmentDetailsBean.setBreadcrumbPage(breadcrumbPage);
breadcrumbPage = "assignmentDetails";
}
// wherever we go, we're not editing, and middle section
// does not need to be shown.
setNav(null, "false", null, "false", null);
return breadcrumbPage;
}
/**
* View maintenance methods.
*/
public Long getAssignmentId() {
if (logger.isDebugEnabled()) logger.debug("getAssignmentId " + assignmentId);
return assignmentId;
}
public void setAssignmentId(Long assignmentId) {
if (logger.isDebugEnabled()) logger.debug("setAssignmentId " + assignmentId);
if (assignmentId != null) {
this.assignmentId = assignmentId;
}
}
public Assignment getAssignment() {
if (logger.isDebugEnabled()) logger.debug("getAssignment " + assignment);
if (assignment == null) {
if (assignmentId != null) {
assignment = getGradebookManager().getAssignment(assignmentId);
}
if (assignment == null) {
// it is a new assignment
assignment = new Assignment();
assignment.setReleased(true);
}
}
return assignment;
}
public List getCategoriesSelectList() {
return categoriesSelectList;
}
public List getAddItemSelectList() {
return addItemSelectList;
}
public String getAssignmentCategory() {
return assignmentCategory;
}
public void setAssignmentCategory(String assignmentCategory) {
this.assignmentCategory = assignmentCategory;
}
/**
* getNewBulkItems
*
* Generates and returns a List of blank Assignment objects.
* Used to support bulk gradebook item creation.
*/
public List getNewBulkItems() {
if (newBulkItems == null) {
newBulkItems = new ArrayList();
}
for (int i = newBulkItems.size(); i < NUM_EXTRA_ASSIGNMENT_ENTRIES; i++) {
newBulkItems.add(getNewAssignment());
}
return newBulkItems;
}
public void setNewBulkItems(List newBulkItems) {
this.newBulkItems = newBulkItems;
}
public int getNumTotalItems() {
return numTotalItems;
}
public void setNumTotalItems(int numTotalItems) {
this.numTotalItems = numTotalItems;
}
public Gradebook getLocalGradebook()
{
return getGradebook();
}
/**
* Returns the Category associated with assignmentCategory
* If unassigned or not found, returns null
*
* added parameterized version to support bulk gradebook item creation
*/
private Category retrieveSelectedCategory()
{
return retrieveSelectedCategory(assignmentCategory);
}
private Category retrieveSelectedCategory(String assignmentCategory)
{
Long catId = null;
Category category = null;
if (assignmentCategory != null && !assignmentCategory.equals(UNASSIGNED_CATEGORY)) {
try {
catId = new Long(assignmentCategory);
}
catch (Exception e) {
catId = null;
}
if (catId != null)
{
// check to make sure there is a corresponding category
category = getGradebookManager().getCategory(catId);
}
}
return category;
}
/**
* For bulk assignments, need to set the proper classes as a string
*/
public String getRowClasses()
{
StringBuffer rowClasses = new StringBuffer();
if (newBulkItems == null) {
newBulkItems = getNewBulkItems();
}
//if shown in UI, set class to 'bogus show' otherwise 'bogus hide'
for (int i=0; i< newBulkItems.size(); i++){
Object obj = newBulkItems.get(i);
if(obj instanceof BulkAssignmentDecoratedBean){
BulkAssignmentDecoratedBean assignment = (BulkAssignmentDecoratedBean) newBulkItems.get(i);
if (i != 0) rowClasses.append(",");
if (assignment.getBlnSaveThisItem() || i == 0) {
rowClasses.append("show bogus");
}
else {
rowClasses.append("hide bogus");
}
}
}
return rowClasses.toString();
}
}