/**********************************************************************************
*
* $Id: OverviewBean.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $
*
***********************************************************************************
*
* Copyright (c) 2005, 2006, 2007, 2008 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.opensource.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.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.service.gradebook.shared.GradebookService;
import org.sakaiproject.tool.api.ToolSession;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.tool.gradebook.Assignment;
import org.sakaiproject.tool.gradebook.Category;
import org.sakaiproject.tool.gradebook.CourseGrade;
import org.sakaiproject.tool.gradebook.GradableObject;
import org.sakaiproject.tool.gradebook.Gradebook;
import org.sakaiproject.tool.gradebook.jsf.FacesUtil;
/**
* Backing bean for the visible list of assignments in the gradebook.
*/
public class OverviewBean extends GradebookDependentBean implements Serializable {
private static final Log logger = LogFactory.getLog(OverviewBean.class);
private static final Map columnSortMap;
private List gradebookItemList;
private CourseGrade courseGrade;
private boolean displayGradeEditorCol = false;
static {
columnSortMap = new HashMap();
columnSortMap.put(Assignment.SORT_BY_NAME, GradableObject.nameComparator);
columnSortMap.put(Assignment.SORT_BY_DATE, GradableObject.dateComparator);
columnSortMap.put(Assignment.SORT_BY_RELEASED,Assignment.releasedComparator);
columnSortMap.put(Assignment.SORT_BY_MEAN, GradableObject.meanComparator);
columnSortMap.put(Assignment.SORT_BY_POINTS, Assignment.pointsComparator);
columnSortMap.put(Assignment.SORT_BY_COUNTED, Assignment.countedComparator);
columnSortMap.put(Assignment.SORT_BY_EDITOR, Assignment.gradeEditorComparator);
columnSortMap.put(Assignment.SORT_BY_SORTING, GradableObject.sortingComparator);
columnSortMap.put("default", GradableObject.defaultComparator);
}
public List getGradebookItemList() {
return gradebookItemList;
}
public void setGradebookItemList(List gradebookItemList) {
this.gradebookItemList = gradebookItemList;
}
public CourseGrade getCourseGrade() {
return courseGrade;
}
public void setCourseGrade(CourseGrade courseGrade) {
this.courseGrade = courseGrade;
}
public String getAvgCourseGradeLetter() {
String letterGrade = "";
if (courseGrade != null) {
letterGrade = getGradebook().getSelectedGradeMapping().getGrade(courseGrade.getMean());
}
return letterGrade;
}
protected void init() {
gradebookItemList = new ArrayList();
courseGrade = new CourseGrade();
if (getCategoriesEnabled()) {
/* if categories are enabled, we need to display a table that includes
* categories, assignments, and the course grade.
*/
List categoryListWithCG = getGradebookManager().getCategoriesWithStats(getGradebookId(), getAssignmentSortColumn(), isAssignmentSortAscending(), getCategorySortColumn(), isCategorySortAscending());
List<Category> categoryList = new ArrayList<Category>();
// first, remove the CourseGrade from the Category list
for (Iterator catIter = categoryListWithCG.iterator(); catIter.hasNext();) {
Object catOrCourseGrade = catIter.next();
if (catOrCourseGrade instanceof Category) {
categoryList.add((Category)catOrCourseGrade);
} else if (catOrCourseGrade instanceof CourseGrade) {
courseGrade = (CourseGrade) catOrCourseGrade;
}
}
// then, we need to check for special grader permissions that may limit which categories may be viewed
if (!isUserAbleToGradeAll() && isUserHasGraderPermissions()) {
//SAK-19896, eduservice's can't share the same "Category" class, so just pass the ID's
List<Long> catIds = new ArrayList<Long>();
for (Category category : (List<Category>) categoryList) {
catIds.add(category.getId());
}
List<Long> viewableCats = getGradebookPermissionService().getCategoriesForUser(getGradebookId(), getUserUid(), catIds, getGradebook().getCategory_type());
List<Category> tmpCatList = new ArrayList<Category>();
for (Category category : (List<Category>) categoryList) {
if(viewableCats.contains(category.getId())){
tmpCatList.add(category);
}
}
categoryList = tmpCatList;
}
if (categoryList != null && !categoryList.isEmpty()) {
Comparator catComparator = null;
if(Assignment.SORT_BY_MEAN.equals(getAssignmentSortColumn())){
catComparator = Category.averageScoreComparator;
}else if(Category.SORT_BY_WEIGHT.equals(getAssignmentSortColumn())){
catComparator = Category.weightComparator;
}else if(Category.SORT_BY_NAME.equals(getAssignmentSortColumn())){
catComparator = Category.nameComparator;
}
if(catComparator != null){
Collections.sort(categoryList, catComparator);
if(!isAssignmentSortAscending()){
Collections.reverse(categoryList);
}
}
Iterator catIter = categoryList.iterator();
while (catIter.hasNext()) {
Category myCat = (Category)catIter.next();
gradebookItemList.add(myCat);
List assignmentList = myCat.getAssignmentList();
if (assignmentList != null && !assignmentList.isEmpty()) {
Iterator assignIter = assignmentList.iterator();
while (assignIter.hasNext()) {
Assignment assign = (Assignment) assignIter.next();
if (assign.isExternallyMaintained())
displayGradeEditorCol = true;
gradebookItemList.add(assign);
}
ensureAssignmentsSorted(assignmentList, GradableObject.sortingComparator, false);
}
}
}
if (!isUserAbleToGradeAll() && (isUserHasGraderPermissions() && !getGradebookPermissionService().getPermissionForUserForAllAssignment(getGradebookId(), getUserUid()))) {
// is not authorized to view the "Unassigned" Category
} else {
List unassignedList = getGradebookManager().getAssignmentsWithNoCategoryWithStats(getGradebookId(), getAssignmentSortColumn(), isAssignmentSortAscending());
if (unassignedList != null && !unassignedList.isEmpty()) {
Category unassignedCat = new Category();
unassignedCat.setGradebook(getGradebook());
unassignedCat.setAverageScore(new Double(0));
unassignedCat.setName(getLocalizedString("cat_unassigned"));
unassignedCat.setAssignmentList(unassignedList);
if (!getWeightingEnabled())
unassignedCat.calculateStatistics(unassignedList);
gradebookItemList.add(unassignedCat);
Iterator unassignedIter = unassignedList.iterator();
while (unassignedIter.hasNext()) {
Assignment assignWithNoCat = (Assignment) unassignedIter.next();
if (assignWithNoCat.isExternallyMaintained())
displayGradeEditorCol = true;
gradebookItemList.add(assignWithNoCat);
}
}
ensureAssignmentsSorted(unassignedList, GradableObject.sortingComparator, false);
}
} else {
// Get the list of assignments for this gradebook, sorted as defined in the overview page.
List goList = getGradebookManager().getAssignmentsAndCourseGradeWithStats(getGradebookId(),
getAssignmentSortColumn(), isAssignmentSortAscending());
if (goList != null && !goList.isEmpty()) {
Iterator goIter = goList.iterator();
while (goIter.hasNext()) {
GradableObject go = (GradableObject) goIter.next();
if (go.isCourseGrade()) {
courseGrade = (CourseGrade) go;
} else {
Assignment assign = (Assignment) go;
if (assign.isExternallyMaintained())
displayGradeEditorCol = true;
gradebookItemList.add(assign);
}
}
ensureAssignmentsSorted(goList, GradableObject.sortingComparator, false);
}
}
// Set up navigation
ToolSession session = SessionManager.getCurrentToolSession();
session.setAttribute("breadcrumbPage", "overview");
session.removeAttribute("adding");
session.removeAttribute("editing");
session.removeAttribute("middle");
}
// Delegated sort methods
public String getAssignmentSortColumn() {
return getPreferencesBean().getAssignmentSortColumn();
}
public void setAssignmentSortColumn(String assignmentSortColumn) {
getPreferencesBean().setAssignmentSortColumn(assignmentSortColumn);
}
public boolean isAssignmentSortAscending() {
return getPreferencesBean().isAssignmentSortAscending();
}
public void setAssignmentSortAscending(boolean sortAscending) {
getPreferencesBean().setAssignmentSortAscending(sortAscending);
}
public String getCategorySortColumn() {
return getPreferencesBean().getCategorySortColumn();
}
public void setCategorySortColumn(String categorySortColumn) {
getPreferencesBean().setCategorySortColumn(categorySortColumn);
}
public boolean isCategorySortAscending() {
return getPreferencesBean().isCategorySortAscending();
}
public void setCategorySortAscending(boolean sortAscending) {
getPreferencesBean().setCategorySortAscending(sortAscending);
}
/**
* @return The comma-separated list of css styles to use in displaying the rows
*/
public String getRowStyles() {
StringBuilder sb = new StringBuilder();
for(Iterator iter = gradebookItemList.iterator(); iter.hasNext();) {
Object gradebookItem = iter.next();
if (gradebookItem instanceof GradableObject) {
GradableObject go = (GradableObject)gradebookItem;
if(go.isCourseGrade()) {
sb.append("internal");
break;
} else {
Assignment asn = (Assignment)go;
if(asn.isExternallyMaintained()) {
sb.append("external,");
} else {
sb.append("internal,");
}
}
} else {
sb.append("internal,");
}
}
return sb.toString();
}
public String getGradeOptionSummary() {
String gradeOptionSummary;
Gradebook gradebook = getGradebook();
String gradeMappingName = gradebook.getSelectedGradeMapping().getName();
if (gradebook.isAssignmentsDisplayed()) {
if (gradebook.isCourseGradeDisplayed()) {
gradeOptionSummary = FacesUtil.getLocalizedString("overview_grade_option_all_viewable", new String[] {gradeMappingName});
} else {
gradeOptionSummary = FacesUtil.getLocalizedString("overview_grade_option_assignments_viewable");
}
} else if (gradebook.isCourseGradeDisplayed()) {
gradeOptionSummary = FacesUtil.getLocalizedString("overview_grade_option_course_grade_viewable", new String[] {gradeMappingName});
} else {
gradeOptionSummary = FacesUtil.getLocalizedString("overview_grade_option_none_viewable");
}
return gradeOptionSummary;
}
public boolean isDisplayGradeEditorCol() {
return displayGradeEditorCol;
}
/**
* Set state when navigating to edit page directly from overview page.
*/
public String navigateToEdit() {
setNav("overview","true","false","false", null);
return "editAssignment";
}
/**
* Set state when navigating to add page directly from overview page
*/
public String navigateToAddAssignment() {
setNav("overview", "false", "true", "false", null);
return "addAssignment";
}
/**
* Set state when navigating to assignment details page directly from overview page
*/
public String navigateToAssignmentDetails() {
setNav("overview", "false", "false", "false", null);
return "assignmentDetails";
}
/**
* Set state when navigating to spreadsheet dock page directly from overview page.
*/
public String navigateToSpreadsheet() {
setNav("overview", "false", "false", "false", "spreadsheetListing");
return "spreadsheetListing";
}
/**
* Since Gradebook Items (Overview) is the default page, to deal with the case where
* navigating from another tool/clicked refresh, reset the navigation to "overview"
*/
public void setBreadcrumbPageParam(String breadcrumbPageParam) {
if (SessionManager.getCurrentToolSession().getAttribute(BREADCRUMBPAGE) != null) {
if ((breadcrumbPageParam != null) && !breadcrumbPageParam.equals("null")) {
setBreadcrumbPage(breadcrumbPageParam);
if (!"".equals(breadcrumbPageParam)) SessionManager.getCurrentToolSession().setAttribute(BREADCRUMBPAGE, breadcrumbPageParam);
}
else {
setBreadcrumbPage("overview");
}
}
}
public boolean getIsLetterGrade()
{
if(isUserAbleToEditAssessments())
{
Gradebook gb = getGradebookManager().getGradebookWithGradeMappings(getGradebookId());
if(gb != null && gb.getGrade_type() == GradebookService.GRADE_TYPE_LETTER)
{
return true;
}
return false;
}
return false;
}
@SuppressWarnings("unchecked")
public void sortUp() {
Long assignmentId = getAssignmentIdFromParam();
if (logger.isDebugEnabled()) {
logger.debug("Sort: sorting up: " + assignmentId);
}
List<Assignment> assignments = getGradebookManager().getAssignments(getGradebookId());
if (assignments.size() > 1) {
ensureAssignmentsSorted(assignments, GradableObject.sortingComparator, true);
// now adjust the numbering
for (int i = 0; i < assignments.size(); i++) {
Assignment a1 = assignments.get(i);
if (a1.getId().equals(assignmentId)) {
if (i > 0) {
Assignment a2 = assignments.get(i-1);
// only swap items which are in the same category
if ( (a1.getCategory() == null && a2.getCategory() == null)
|| (a1.getCategory().equals(a2.getCategory())) ) {
// swap the ordering of this item and the item below it
Integer holder = a1.getSortOrder();
a1.setSortOrder(a2.getSortOrder());
a2.setSortOrder(holder);
logger.info("Sort: UP swapping: "+a1.getId()+" (to "+a1.getSortOrder()+" from "+holder+") with "+a2.getId());
// save the new orders
getGradebookManager().updateAssignment(a1);
getGradebookManager().updateAssignment(a2);
} else {
logger.info("Sort: UP: unable to swap items ("+a1.getId()+","+a2.getId()+") in different categories");
}
}
break;
}
}
}
}
@SuppressWarnings("unchecked")
public void sortDown() {
Long assignmentId = getAssignmentIdFromParam();
if (logger.isDebugEnabled()) {
logger.debug("Sort: sorting down: " + assignmentId);
}
List<Assignment> assignments = getGradebookManager().getAssignments(getGradebookId());
if (assignments.size() > 1) {
ensureAssignmentsSorted(assignments, GradableObject.sortingComparator, true);
// now adjust the numbering
for (int i = 0; i < assignments.size(); i++) {
Assignment a1 = assignments.get(i);
if (a1.getId().equals(assignmentId)) {
if (i < (assignments.size() - 1)) {
Assignment a2 = assignments.get(i+1);
// only swap items which are in the same category
if ( (a1.getCategory() == null && a2.getCategory() == null)
|| (a1.getCategory().equals(a2.getCategory())) ) {
// swap the ordering of this item and the item below it
Integer holder = a1.getSortOrder();
a1.setSortOrder(a2.getSortOrder());
a2.setSortOrder(holder);
logger.info("Sort: DOWN swapping: "+a1.getId()+" (to "+a1.getSortOrder()+" from "+holder+") with "+a2.getId());
// save the new orders
getGradebookManager().updateAssignment(a1);
getGradebookManager().updateAssignment(a2);
} else {
logger.info("Sort: DOWN: unable to swap items ("+a1.getId()+","+a2.getId()+") in different categories");
}
}
break;
}
}
}
}
@SuppressWarnings("unchecked")
public void saveCurrentSort() {
String sortColumn = getAssignmentSortColumn();
if (sortColumn == null) {
sortColumn = Assignment.DEFAULT_SORT;
}
boolean ascending = isAssignmentSortAscending();
if (logger.isDebugEnabled()) {
logger.debug("saveCurrentSort: saving current sort order ("+sortColumn+", "+ascending+") for gradebook: " + getGradebookId());
}
List<Assignment> assignments = getGradebookManager().getAssignmentsAndCourseGradeWithStats(getGradebookId(), sortColumn, ascending); //getAssignmentsWithNoCategoryWithStats(getGradebookId(), sortColumn, ascending);
if (logger.isDebugEnabled()) {
logger.debug("saveCurrentSort: current order ("+assignments.size()+"): " + Arrays.toString(assignments.toArray()));
}
// now ensure the numbering is set and correct
ensureAssignmentsSorted(assignments, new NoChangeMarkerComparator(), true);
if (logger.isDebugEnabled()) {
logger.debug("saveCurrentSort: final order ("+assignments.size()+"): " + Arrays.toString(assignments.toArray()));
}
logger.info("Sort: Saved current sort order ("+sortColumn+") [asc="+ascending+"] for gradebook ("+assignments.size()+" items): " + getGradebookId());
// force sorting back to defaults
setAssignmentSortColumn(Assignment.DEFAULT_SORT);
setAssignmentSortAscending(true);
}
@SuppressWarnings("unchecked")
public boolean getEnabledSaveSort() {
boolean enabled = false;
String sortColumn = getAssignmentSortColumn();
boolean ascending = isAssignmentSortAscending();
// if default sort is set, no save allowed
if (sortColumn != null
&& (! Assignment.DEFAULT_SORT.equals(sortColumn)
|| (Assignment.DEFAULT_SORT.equals(sortColumn) && !ascending) ) ) {
enabled = true;
}
if (enabled) {
// if allowed then check that there are enough assignments
List<Assignment> assignments = getGradebookManager().getAssignments(getGradebookId());
if (assignments.size() > 1) { // factor out the category
enabled = true;
} else {
enabled = false;
}
}
return enabled;
}
private Long getAssignmentIdFromParam() {
FacesContext context = FacesContext.getCurrentInstance();
String[] assignmentIds = (String[]) context.getExternalContext().getRequestParameterValuesMap().get("assignmentId");
if (assignmentIds == null || assignmentIds.length == 0) {
throw new IllegalArgumentException("assignmentId must be set");
}
Long assignmentId = Long.valueOf(assignmentIds[0]);
return assignmentId;
}
@SuppressWarnings("unchecked")
private void ensureAssignmentsSorted(List assignments, Comparator comparator, boolean save) {
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: comparator="+comparator+", save="+save+", assignments= "+Arrays.toString(assignments.toArray()));
}
// remove any non-assignments first
List gradeables = new ArrayList();
for (Iterator iterator = assignments.iterator(); iterator.hasNext();) {
GradableObject go = (GradableObject) iterator.next();
if (! (go instanceof Assignment)) { // ! go.isAssignment()) {
gradeables.add(go);
iterator.remove();
}
}
Collections.sort(gradeables, GradableObject.nameComparator);
// put everything in the established sort order first (if needed)
if (comparator == null) {
comparator = GradableObject.dateComparator;
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: setting default comparator="+comparator);
}
}
if (! NoChangeMarkerComparator.class.isAssignableFrom(comparator.getClass())) {
// only sort if this is not the no-sort marker
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: sorting with comparator="+comparator);
}
Collections.sort(assignments, comparator);
} else {
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: no sort, using NoChangeMarkerComparator="+comparator);
}
}
// always need to sort by category
Collections.sort(assignments, GradableObject.categoryComparator);
// now ensure the numbering is set and correct
int saveCount = 0;
int updateCount = 0;
for (int i = 0; i < assignments.size(); i++) {
Assignment assignment = (Assignment) assignments.get(i);
Integer curOrder = assignment.getSortOrder();
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: checking if current order ("+curOrder+") matches correct order ("+i+") for assignment: "+assignment);
}
if (curOrder == null || i != curOrder.intValue()) {
// no match so we need to update it (else it is already set correctly, only save if needed)
assignment.setSortOrder(i);
updateCount++;
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: setting sort order ("+i+") for assignment: "+assignment);
}
if (save) {
getGradebookManager().updateAssignment(assignment);
saveCount++;
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: saving assignment: "+assignment);
}
}
}
}
// set the ordering up in the assignment with support for categories
Map<String, List<Assignment>> categoryAssignments = new LinkedHashMap<String, List<Assignment>>();
for (Assignment assignment : (List<Assignment>) assignments) {
String category = "NULL";
if (assignment.getCategory() != null) {
category = assignment.getCategory().getName();
}
if (! categoryAssignments.containsKey(category)) {
categoryAssignments.put(category, new ArrayList<Assignment>());
}
categoryAssignments.get(category).add(assignment);
//assignment.assignSorting(assignments.size(), i);
}
for (Entry<String, List<Assignment>> entry : categoryAssignments.entrySet()) {
List<Assignment> l = entry.getValue();
for (int i = 0; i < l.size(); i++) {
Assignment assignment = l.get(i);
// assign the counter for ordering
assignment.assignSorting(l.size(), i);
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: ordered: "+i+" : "+assignment);
}
}
}
// add back in the gradeables to the end
for (Object gradeable : gradeables) {
assignments.add(gradeable);
}
if (logger.isDebugEnabled()) {
logger.debug("ensureAssignmentsSorted: sorted assignments (updated="+updateCount+", saved="+saveCount+"): "+Arrays.toString(assignments.toArray()));
}
}
/**
* Special marker class to preserve the order when saving the sort order
*/
public static class NoChangeMarkerComparator implements Comparator<GradableObject> {
public int compare(GradableObject o1, GradableObject o2) {
return -1; // preserve the existing order
}
@Override
public String toString() {
return "NoChangeMarkerComparator";
}
}
}