/********************************************************************************** * * $Id: RosterBean.java 108030 2012-05-11 15:38:31Z holladay@longsight.com $ * *********************************************************************************** * * 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.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.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.text.NumberFormat; import javax.faces.application.Application; import javax.faces.component.UIColumn; import javax.faces.component.html.HtmlOutputText; import javax.faces.component.UIComponent; import javax.faces.component.html.HtmlPanelGroup; import javax.faces.component.html.HtmlCommandLink; import javax.faces.component.UIParameter; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.faces.event.ValueChangeEvent; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.myfaces.component.html.ext.HtmlDataTable; import org.apache.myfaces.custom.sortheader.HtmlCommandSortHeader; import org.sakaiproject.jsf.spreadsheet.SpreadsheetDataFileWriterCsv; import org.sakaiproject.jsf.spreadsheet.SpreadsheetDataFileWriterXls; import org.sakaiproject.jsf.spreadsheet.SpreadsheetDataFileWriterPdf; import org.sakaiproject.jsf.spreadsheet.SpreadsheetUtil; import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; import org.sakaiproject.section.api.coursemanagement.User; import org.sakaiproject.tool.api.ToolSession; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.tool.gradebook.AbstractGradeRecord; import org.sakaiproject.tool.gradebook.Assignment; import org.sakaiproject.tool.gradebook.AssignmentGradeRecord; import org.sakaiproject.tool.gradebook.Category; import org.sakaiproject.tool.gradebook.CourseGrade; import org.sakaiproject.tool.gradebook.CourseGradeRecord; import org.sakaiproject.tool.gradebook.GradableObject; import org.sakaiproject.tool.gradebook.Gradebook; import org.sakaiproject.tool.gradebook.jsf.AssignmentPointsConverter; import org.sakaiproject.tool.gradebook.jsf.CategoryPointsConverter; import org.sakaiproject.tool.gradebook.jsf.FacesUtil; import org.sakaiproject.util.ResourceLoader; import org.sakaiproject.service.gradebook.shared.GradebookService; import org.sakaiproject.component.cover.ServerConfigurationService; /** * Backing bean for the visible list of assignments in the gradebook. */ public class RosterBean extends EnrollmentTableBean implements Serializable, Paging { private static final Log logger = LogFactory.getLog(RosterBean.class); // Used to generate IDs for the dynamically created assignment columns. private static final String ASSIGNMENT_COLUMN_PREFIX = "asg_"; private static final String CATEGORY_COLUMN_PREFIX = "_categoryCol_"; // View maintenance fields - serializable. private List gradableObjectColumns; // Needed to build table columns private List workingEnrollments; private Map enrollmentMap; private CourseGrade avgCourseGrade; private HtmlDataTable originalRosterDataTable = null; private boolean selectedCategoryDropsScores; public class GradableObjectColumn implements Serializable { private Long id; private String name; private Boolean categoryColumn = false; private Boolean assignmentColumn = false; private Long assignmentId; private Boolean inactive = false; private Boolean hideInAllGradesTable = false; private Boolean hiddenChanged = false; public GradableObjectColumn() { } public GradableObjectColumn(GradableObject gradableObject) { id = gradableObject.getId(); name = getColumnHeader(gradableObject); categoryColumn = false; assignmentId = getColumnHeaderAssignmentId(gradableObject); assignmentColumn = !gradableObject.isCourseGrade(); inactive = (!gradableObject.isCourseGrade() && !((Assignment)gradableObject).isReleased() ? true : false); hideInAllGradesTable = assignmentColumn ? ((Assignment) gradableObject).isHideInAllGradesTable() : false; hiddenChanged = hideInAllGradesTable; } @Override public String toString() { return name+":("+id+"):"+assignmentId; // AZ - better debugging } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Boolean getCategoryColumn() { return categoryColumn; } public void setCategoryColumn(Boolean categoryColumn){ this.categoryColumn = categoryColumn; } public Long getAssignmentId() { return assignmentId; } public void setAssignmentId(Long assignmentId) { this.assignmentId = assignmentId; } public Boolean getAssignmentColumn() { return assignmentColumn; } public void setAssignmentColumn(Boolean assignmentColumn) { this.assignmentColumn = assignmentColumn; } public Boolean getInactive() { return this.inactive; } public void setInactive(Boolean inactive) { this.inactive = inactive; } public Boolean getHideInAllGradesTable() { return hideInAllGradesTable; } public void setHideInAllGradesTable(Boolean hideInAllGradesTable) { this.hideInAllGradesTable = hideInAllGradesTable; } public boolean hasHiddenChanged(){ return hiddenChanged != hideInAllGradesTable; } } // Controller fields - transient. private transient List studentRows; private transient Map gradeRecordMap; private transient Map categoryResultMap; public class StudentRow implements Serializable { private EnrollmentRecord enrollment; public StudentRow() { } public StudentRow(EnrollmentRecord enrollment) { this.enrollment = enrollment; } public String getStudentUid() { return enrollment.getUser().getUserUid(); } public String getSortName() { return enrollment.getUser().getSortName(); } public String getDisplayId() { return enrollment.getUser().getDisplayId(); } public Map getScores() { return (Map)gradeRecordMap.get(enrollment.getUser().getUserUid()); } public Map getCategoryResults() { return (Map)categoryResultMap.get(enrollment.getUser().getUserUid()); } } protected void init() { // set the roster filter super.setSelectedSectionFilterValue(this.getSelectedSectionFilterValue()); super.init(); //get array to hold columns gradableObjectColumns = new ArrayList(); avgCourseGrade = new CourseGrade(); //get the selected categoryUID String selectedCategoryUid = getSelectedCategoryUid(); if(selectedCategoryUid != null) { Category selectedCategory = getSelectedCategory(); selectedCategoryDropsScores = selectedCategory.isDropScores(); } CourseGrade courseGrade = null; if (isUserAbleToGradeAll()) { courseGrade = getGradebookManager().getCourseGrade(getGradebookId()); // first add Cumulative if not a selected category if(selectedCategoryUid == null){ gradableObjectColumns.add(new GradableObjectColumn(courseGrade)); } } // make sure the numeric display is locale-aware NumberFormat nf = NumberFormat.getInstance(new ResourceLoader().getLocale()); List<Category> categories = new ArrayList<Category>(); List<Assignment> allAssignments = new ArrayList<Assignment>(); // get all of the assignments and categories List assignCategoryCGList = getGradebookManager().getAssignmentsCategoriesAndCourseGradeWithStats(getGradebookId(),Assignment.DEFAULT_SORT, true, Category.SORT_BY_NAME, true); // let's filter these into assignment list, category list, and course grade for (Iterator listIter = assignCategoryCGList.iterator(); listIter.hasNext();) { Object assignCatOrCourseGrade = listIter.next(); if (assignCatOrCourseGrade instanceof Category) { categories.add((Category)assignCatOrCourseGrade); } else if (assignCatOrCourseGrade instanceof CourseGrade) { avgCourseGrade = (CourseGrade)assignCatOrCourseGrade; } else if (assignCatOrCourseGrade instanceof Assignment) { allAssignments.add((Assignment)assignCatOrCourseGrade); } } if (getCategoriesEnabled()) { 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 : categories) { catIds.add(category.getId()); } List<Long> viewableCats = getGradebookPermissionService().getCategoriesForUser(getGradebookId(), getUserUid(), catIds, getGradebook().getCategory_type()); List<Category> tmpCatList = new ArrayList<Category>(); for (Category category : categories) { if(viewableCats.contains(category.getId())){ tmpCatList.add(category); } } categories = tmpCatList; } int categoryCount = categories.size(); for (Iterator iter = categories.iterator(); iter.hasNext(); ){ Category cat = (Category) iter.next(); if(selectedCategoryUid == null || selectedCategoryUid.equals(cat.getId().toString())){ //get the category column GradableObjectColumn categoryColumn = new GradableObjectColumn(); String name = cat.getName(); if(getWeightingEnabled()){ //if weighting is enabled, then add "(weight)" to column Double value = (Double) ((Number)cat.getWeight()); name = name + " (" + nf.format(value * 100.0) + "%)"; //name = name + " (" + Integer.toString(cat.getWeight() * 100) + "%)"; } categoryColumn.setName(name); categoryColumn.setId(cat.getId()); categoryColumn.setCategoryColumn(true); //if selectedCategoryUID, then we want the category first, otherwise after if(selectedCategoryUid != null) { gradableObjectColumns.add(categoryColumn); } //add assignments //List assignments = getGradebookManager().getAssignmentsForCategory(cat.getId()); List assignments = cat.getAssignmentList(); if (assignments != null && !assignments.isEmpty()){ for (Iterator assignmentsIter = assignments.iterator(); assignmentsIter.hasNext();){ gradableObjectColumns.add(new GradableObjectColumn((GradableObject)assignmentsIter.next())); } //if not selectedCategoryUID, then add category field after if(selectedCategoryUid == null) { gradableObjectColumns.add(categoryColumn); } } } } if(selectedCategoryUid == null){ if (!isUserAbleToGradeAll() && (isUserHasGraderPermissions() && !getGradebookPermissionService().getPermissionForUserForAllAssignment(getGradebookId(), getUserUid()))) { // not allowed to view the unassigned category } else { //get Assignments with no category List unassignedAssignments = getGradebookManager().getAssignmentsWithNoCategory(getGradebookId(), Assignment.DEFAULT_SORT, true); int unassignedAssignmentCount = unassignedAssignments.size(); for (Iterator assignmentsIter = unassignedAssignments.iterator(); assignmentsIter.hasNext(); ){ gradableObjectColumns.add(new GradableObjectColumn((GradableObject) assignmentsIter.next())); } //If there are categories and there are unassigned assignments, then display Unassigned Category column if (getCategoriesEnabled() && unassignedAssignmentCount > 0){ //add Unassigned column GradableObjectColumn unassignedCategoryColumn = new GradableObjectColumn(); unassignedCategoryColumn.setName(FacesUtil.getLocalizedString("cat_unassigned")); unassignedCategoryColumn.setCategoryColumn(true); gradableObjectColumns.add(unassignedCategoryColumn); } } } } if (isRefreshRoster()) { enrollmentMap = getOrderedEnrollmentMapForAllItems(); // Map of EnrollmentRecord --> Map of Item --> function (grade/view) setRefreshRoster(false); } Map studentIdEnrRecMap = new HashMap(); Map studentIdItemIdFunctionMap = new HashMap(); // get all of the items included in the item --> function map for each viewable enrollee List viewableAssignmentIds = new ArrayList(); for (Iterator enrIter = enrollmentMap.keySet().iterator(); enrIter.hasNext();) { EnrollmentRecord enr = (EnrollmentRecord) enrIter.next(); if (enr != null) { String studentId = enr.getUser().getUserUid(); studentIdEnrRecMap.put(studentId, enr); Map itemFunctionMap = (Map)enrollmentMap.get(enr); studentIdItemIdFunctionMap.put(studentId, itemFunctionMap); if (itemFunctionMap != null) { for (Iterator itemIter = itemFunctionMap.keySet().iterator(); itemIter.hasNext();) { Long itemId = (Long) itemIter.next(); if (itemId != null) { viewableAssignmentIds.add(itemId); } } } } } List viewableAssignmentList = new ArrayList(); //Map viewableAssignmentMap = new HashMap(); if (!allAssignments.isEmpty()) { for (Iterator assignIter = allAssignments.iterator(); assignIter.hasNext();) { Object obj = assignIter.next(); if (obj instanceof Assignment){ Assignment assignment = (Assignment) obj; if (assignment != null) { Long assignId = assignment.getId(); if (viewableAssignmentIds.contains(assignId)) { viewableAssignmentList.add(assignment); //viewableAssignmentMap.put(assignId, assignment); } } } } } List assignments = viewableAssignmentList; List gradeRecords = getGradebookManager().getAllAssignmentGradeRecordsConverted(getGradebookId(), new ArrayList(studentIdEnrRecMap.keySet())); if (!getCategoriesEnabled()) { int unassignedAssignmentCount = assignments.size(); for (Iterator assignmentsIter = assignments.iterator(); assignmentsIter.hasNext(); ){ gradableObjectColumns.add(new GradableObjectColumn((GradableObject) assignmentsIter.next())); } } workingEnrollments = new ArrayList(enrollmentMap.keySet()); gradeRecordMap = new HashMap(); if (!isUserAbleToGradeAll() && isUserHasGraderPermissions()) { // first, add dummy grade records for all of the missing "unviewable" grade records // this will allow us to display grade entries that are not viewable differently // than null grade records for (Iterator studentIter = studentIdItemIdFunctionMap.keySet().iterator(); studentIter.hasNext();) { String studentId = (String)studentIter.next(); if (studentId != null) { Map itemIdFunctionMap = (Map)studentIdItemIdFunctionMap.get(studentId); for (Iterator itemIter = allAssignments.iterator(); itemIter.hasNext();) { Object obj = itemIter.next(); if (obj instanceof Assignment){ Assignment assignment = (Assignment) obj; if (assignment != null) { Long itemId = assignment.getId(); if (itemIdFunctionMap == null || itemIdFunctionMap.get(itemId) == null){ AssignmentGradeRecord agr = new AssignmentGradeRecord(assignment, studentId, null); gradeRecords.add(agr); } } } } } } getGradebookManager().addToGradeRecordMap(gradeRecordMap, gradeRecords, studentIdItemIdFunctionMap); } else { getGradebookManager().addToGradeRecordMap(gradeRecordMap, gradeRecords); } if (logger.isDebugEnabled()) logger.debug("init - gradeRecordMap.keySet().size() = " + gradeRecordMap.keySet().size()); if (!isEnrollmentSort() && !isUserAbleToGradeAll() && isUserHasGraderPermissions()) { // we need to re-sort these records b/c some may actually be null based upon permissions. // retrieve updated grade recs from gradeRecordMap List updatedGradeRecs = new ArrayList(); for (Iterator iter = gradeRecordMap.keySet().iterator(); iter.hasNext();) { String studentId = (String)iter.next(); Map itemIdGradeRecMap = (Map)gradeRecordMap.get(studentId); if (!itemIdGradeRecMap.isEmpty()) { updatedGradeRecs.addAll(itemIdGradeRecMap.values()); } } Collections.sort(updatedGradeRecs, AssignmentGradeRecord.calcComparator); gradeRecords = updatedGradeRecs; } // only display course grade if user has "grade all" perm if (isUserAbleToGradeAll()) { List courseGradeRecords = getGradebookManager().getPointsEarnedCourseGradeRecords(courseGrade, studentIdEnrRecMap.keySet(), assignments, gradeRecordMap); Collections.sort(courseGradeRecords, CourseGradeRecord.calcComparator); getGradebookManager().addToGradeRecordMap(gradeRecordMap, courseGradeRecords); gradeRecords.addAll(courseGradeRecords); } //do category results categoryResultMap = new HashMap(); getGradebookManager().addToCategoryResultMap(categoryResultMap, categories, gradeRecordMap, studentIdEnrRecMap); if (logger.isDebugEnabled()) logger.debug("init - categoryResultMap.keySet().size() = " + categoryResultMap.keySet().size()); if (!isEnrollmentSort()) { // Need to sort and page based on a scores column. String sortColumn = getSortColumn(); List scoreSortedEnrollments = new ArrayList(); for(Iterator iter = gradeRecords.iterator(); iter.hasNext();) { AbstractGradeRecord agr = (AbstractGradeRecord)iter.next(); if(getColumnHeader(agr.getGradableObject()).equals(sortColumn)) { scoreSortedEnrollments.add(studentIdEnrRecMap.get(agr.getStudentId())); } } //In order to order by category score, first create 2 lists: students who have no score (null) //and a map of student Id's and their category score. //Next, sort the existing score then and put the sorted students in the scoreSortedEnrollments list if(sortColumn.startsWith(CATEGORY_COLUMN_PREFIX) && sortColumn.length() > CATEGORY_COLUMN_PREFIX.length()){ Map<String, Double> studentCatScore = new HashMap<String, Double>(); String sortColumnIdStr = sortColumn.substring(CATEGORY_COLUMN_PREFIX.length()); Long sortColumnId = null; try{ sortColumnId = Long.parseLong(sortColumnIdStr); }catch (NumberFormatException e) { } List emptyCatList = new ArrayList(); if(sortColumnId != null){ for(Iterator iterator = categoryResultMap.entrySet().iterator(); iterator.hasNext();){ Entry entry = (Entry) iterator.next(); Map catMap = (Map) entry.getValue(); String studentId = (String) entry.getKey(); if(catMap.containsKey(sortColumnId)){ Map sortCat = (Map) catMap.get(sortColumnId); //break up the students into two categories: scores and no score if(sortCat.containsKey("studentMean") && sortCat.get("studentMean") != null){ studentCatScore.put(studentId, (Double) sortCat.get("studentMean")); }else{ emptyCatList.add(studentIdEnrRecMap.get(studentId)); } } } } //sort category scores: List studentCatEntrySet = new LinkedList(studentCatScore.entrySet()); Collections.sort(studentCatEntrySet, new Comparator() { public int compare(Object o1, Object o2) { return ((Comparable) ((Map.Entry) (o1)).getValue()) .compareTo(((Map.Entry) (o2)).getValue()); } }); //add it to the scoreSortedEnrollments list now that it has been ordered for (Iterator it = studentCatEntrySet.iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry)it.next(); scoreSortedEnrollments.add(studentIdEnrRecMap.get(entry.getKey())); } // remove and re-add the empty score users workingEnrollments.removeAll(emptyCatList); // by adding it back in, they will be in a group together (in order) workingEnrollments.addAll(emptyCatList); } // Put enrollments with no scores at the beginning of the final list. workingEnrollments.removeAll(scoreSortedEnrollments); // Add all sorted enrollments with scores into the final list workingEnrollments.addAll(scoreSortedEnrollments); workingEnrollments = finalizeSortingAndPaging(workingEnrollments); } studentRows = new ArrayList(workingEnrollments.size()); for (Iterator iter = workingEnrollments.iterator(); iter.hasNext(); ) { EnrollmentRecord enrollment = (EnrollmentRecord)iter.next(); studentRows.add(new StudentRow(enrollment)); } // set breadcrumb page for navigation // SessionManager.getCurrentToolSession().setAttribute("breadcrumbPage", "roster"); } private String getColumnHeader(GradableObject gradableObject) { if (gradableObject.isCourseGrade()) { return getLocalizedString("roster_course_grade_column_name"); } else { return ((Assignment)gradableObject).getName(); } } private Long getColumnHeaderAssignmentId(GradableObject gradableObject) { if (gradableObject.isCourseGrade()) { return new Long(-1); } else { return ((Assignment)gradableObject).getId(); } } // The roster table uses assignments as columns, and therefore the component // model needs to have those columns added dynamically, based on the current // state of the gradebook. // In JSF 1.1, dynamic data table columns are managed by binding the component // tag to a bean property. // It's not exactly intuitive, but the convention is for the bean to return // null, so that JSF can create and manage the UIData component itself. public HtmlDataTable getRosterDataTable() { if (logger.isDebugEnabled()) logger.debug("getRosterDataTable"); return null; } public void setRosterDataTable(HtmlDataTable rosterDataTable) { if (logger.isDebugEnabled()) { logger.debug("setRosterDataTable gradableObjectColumns=" + gradableObjectColumns + ", rosterDataTable=" + rosterDataTable); if (rosterDataTable != null) { logger.debug(" data children=" + rosterDataTable.getChildren()); } } if (rosterDataTable == null) throw new IllegalArgumentException( "HtmlDataTable rosterDataTable == null!"); //check if columns of changed due to categories ExternalContext context = FacesContext.getCurrentInstance().getExternalContext(); Map paramMap = context.getRequestParameterMap(); String catId = (String) paramMap.get("gbForm:selectCategoryFilter"); //due to this set method getting called before all others, including the setSelectCategoryFilterValue, // we have to manually set the value, then call init to get the new gradableObjectColumns array if(catId != null && !catId.equals(getSelectedCategoryUid())) { this.setSelectedCategoryFilterValue(new Integer(catId)); init(); //now destroy all of the columns to be readded rosterDataTable.getChildren().removeAll( rosterDataTable.getChildren().subList(2, rosterDataTable.getChildren().size())); } // Set the columnClasses on the data table StringBuilder colClasses = new StringBuilder("left,left,"); for(Iterator iter = gradableObjectColumns.iterator(); iter.hasNext();) { iter.next(); colClasses.append("center"); if(iter.hasNext()) { colClasses.append(","); } } rosterDataTable.setColumnClasses(colClasses.toString()); if (rosterDataTable.findComponent(ASSIGNMENT_COLUMN_PREFIX + "0") == null) { Application app = FacesContext.getCurrentInstance().getApplication(); // Add columns for each assignment. Be sure to create unique IDs // for all child components. int colpos = 0; for (Iterator iter = gradableObjectColumns.iterator(); iter.hasNext(); colpos++) { GradableObjectColumn columnData = (GradableObjectColumn)iter.next(); UIColumn col = new UIColumn(); col.setId(ASSIGNMENT_COLUMN_PREFIX + colpos); if(columnData.getHideInAllGradesTable()){ col.setRendered(false); } if(!columnData.getCategoryColumn()){ HtmlCommandSortHeader sortHeader = new HtmlCommandSortHeader(); sortHeader.setId(ASSIGNMENT_COLUMN_PREFIX + "sorthdr_" + colpos); sortHeader.setRendererType("org.apache.myfaces.SortHeader"); // Yes, this is necessary. sortHeader.setArrow(true); sortHeader.setColumnName(columnData.getName()); sortHeader.setActionListener(app.createMethodBinding("#{rosterBean.sort}", new Class[] {ActionEvent.class})); // Allow word-wrapping on assignment name columns. if(columnData.getInactive()){ sortHeader.setStyleClass("inactive-column allowWrap"); } else { sortHeader.setStyleClass("allowWrap"); } HtmlOutputText headerText = new HtmlOutputText(); headerText.setId(ASSIGNMENT_COLUMN_PREFIX + "hdr_" + colpos); // Try straight setValue rather than setValueBinding. headerText.setValue(columnData.getName()); sortHeader.getChildren().add(headerText); if(columnData.getAssignmentColumn()){ //get details link HtmlCommandLink detailsLink = new HtmlCommandLink(); detailsLink.setAction(app.createMethodBinding("#{rosterBean.navigateToAssignmentDetails}", new Class[] {})); detailsLink.setId(ASSIGNMENT_COLUMN_PREFIX + "hdr_link_" + colpos); HtmlOutputText detailsText = new HtmlOutputText(); detailsText.setId(ASSIGNMENT_COLUMN_PREFIX + "hdr_details_" + colpos); detailsText.setValue("<em>" + getLocalizedString("roster_details") + "</em>"); detailsText.setEscape(false); detailsText.setStyle("font-size: 80%"); detailsLink.getChildren().add(detailsText); UIParameter param = new UIParameter(); param.setName("assignmentId"); param.setValue(columnData.getAssignmentId()); detailsLink.getChildren().add(param); /* UIParameter param2 = new UIParameter(); param2.setName("breadcrumbPage"); param2.setValue("roster"); detailsLink.getChildren().add(param2); */ HtmlOutputText br = new HtmlOutputText(); br.setValue("<br />"); br.setEscape(false); //make a panel group to add link HtmlPanelGroup pg = new HtmlPanelGroup(); pg.getChildren().add(sortHeader); pg.getChildren().add(br); pg.getChildren().add(detailsLink); col.setHeader(pg); } else { col.setHeader(sortHeader); } } else { //if we are dealing with a category String categoryId = CATEGORY_COLUMN_PREFIX; if(columnData.getId() != null){ categoryId += columnData.getId().toString(); HtmlCommandSortHeader sortHeader = new HtmlCommandSortHeader(); sortHeader.setId(ASSIGNMENT_COLUMN_PREFIX + "sorthdr_" + colpos); sortHeader.setRendererType("org.apache.myfaces.SortHeader"); // Yes, this is necessary. sortHeader.setArrow(true); sortHeader.setColumnName(categoryId); sortHeader.setActionListener(app.createMethodBinding("#{rosterBean.sort}", new Class[] {ActionEvent.class})); // Allow word-wrapping on assignment name columns. if(columnData.getInactive()){ sortHeader.setStyleClass("inactive-column allowWrap"); } else { sortHeader.setStyleClass("allowWrap"); } HtmlOutputText headerText = new HtmlOutputText(); headerText.setId(ASSIGNMENT_COLUMN_PREFIX + "hdr_" + colpos); // Try straight setValue rather than setValueBinding. headerText.setValue(columnData.getName()); sortHeader.getChildren().add(headerText); col.setHeader(sortHeader); }else{ //Unassigned Category HtmlOutputText headerText = new HtmlOutputText(); headerText.setId(ASSIGNMENT_COLUMN_PREFIX + "hrd_" + colpos); headerText.setValue(columnData.getName()); col.setHeader(headerText); } } HtmlOutputText contents = new HtmlOutputText(); contents.setEscape(false); contents.setId(ASSIGNMENT_COLUMN_PREFIX + "cell_" + colpos); if(!columnData.getCategoryColumn()){ contents.setValueBinding("value", app.createValueBinding("#{row.scores[rosterBean.gradableObjectColumns[" + colpos + "].id]}")); contents.setConverter(new AssignmentPointsConverter()); } else { contents.setValueBinding("value", app.createValueBinding("#{row.categoryResults[rosterBean.gradableObjectColumns[" + colpos + "].id]}")); contents.setConverter(new CategoryPointsConverter()); } // Distinguish the "Cumulative" score for the course, which, by convention, // is always the first column. Only viewable if user has Grade All perm if (colpos == 0 && (isUserAbleToGradeAll() || getSelectedCategoryUid() != null)) { contents.setStyleClass("courseGrade center"); } col.getChildren().add(contents); rosterDataTable.getChildren().add(col); } } } public List getGradableObjectColumns() { return gradableObjectColumns; } public void setGradableObjectColumns(List gradableObjectColumns) { this.gradableObjectColumns = gradableObjectColumns; } public List getStudentRows() { return studentRows; } public String getColLock() { if (isUserAbleToGradeAll() || getSelectedCategoryUid() != null) return "3"; else return "2"; } // Sorting public boolean isSortAscending() { return getPreferencesBean().isRosterTableSortAscending(); } public void setSortAscending(boolean sortAscending) { getPreferencesBean().setRosterTableSortAscending(sortAscending); } public String getSortColumn() { return getPreferencesBean().getRosterTableSortColumn(); } public void setSortColumn(String sortColumn) { getPreferencesBean().setRosterTableSortColumn(sortColumn); } public Category getSelectedCategory() { String selectedUid = getSelectedCategoryUid(); Category selectedCat = null; //if selectedUid is not null (not All Categories) then proceed if (selectedUid != null){ //get a list of all the categories with the stats List categories = getGradebookManager().getCategoriesWithStats(getGradebookId(),Assignment.DEFAULT_SORT, true, Category.SORT_BY_NAME, true); for (Iterator iter = categories.iterator(); iter.hasNext(); ){ Object obj = iter.next(); //last item of list is the CourseGrade, so ignore if (!(obj instanceof Category)) { continue; } Category cat = (Category) obj; if (cat.getId() == Long.parseLong(selectedUid)){ selectedCat = cat; } } } return selectedCat; } // Filtering public Integer getSelectedSectionFilterValue() { return getPreferencesBean().getRosterTableSectionFilter(); } public void setSelectedSectionFilterValue(Integer rosterTableSectionFilter) { getPreferencesBean().setRosterTableSectionFilter(rosterTableSectionFilter); super.setSelectedSectionFilterValue(rosterTableSectionFilter); } public CourseGrade getAvgCourseGrade() { return avgCourseGrade; } public void setAvgCourseGrade(CourseGrade courseGrade) { this.avgCourseGrade = courseGrade; } public String getAvgCourseGradeLetter() { String letterGrade = ""; if (avgCourseGrade != null) { letterGrade = getGradebook().getSelectedGradeMapping().getGrade(avgCourseGrade.getMean()); } return letterGrade; } public void setSelectedCategoryDropsScores(boolean selectedCategoryDropsScores) { this.selectedCategoryDropsScores = selectedCategoryDropsScores; } public boolean isSelectedCategoryDropsScores() { return selectedCategoryDropsScores; } public void exportXlsNoCourseGrade(ActionEvent event){ if(logger.isInfoEnabled()) logger.info("exporting gradebook " + getGradebookUid() + " as Excel"); getGradebookBean().getEventTrackingService().postEvent("gradebook.downloadRoster","/gradebook/"+getGradebookId()+"/"+getAuthzLevel()); SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(false, false), getDownloadFileName(getLocalizedString("export_gradebook_prefix")), new SpreadsheetDataFileWriterXls()); } public void exportCsvNoCourseGrade(ActionEvent event){ if(logger.isInfoEnabled()) logger.info("exporting gradebook " + getGradebookUid() + " as CSV"); getGradebookBean().getEventTrackingService().postEvent("gradebook.downloadRoster","/gradebook/"+getGradebookId()+"/"+getAuthzLevel()); SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(false, true), getDownloadFileName(getLocalizedString("export_gradebook_prefix")), new SpreadsheetDataFileWriterCsv()); } public void exportCsv(ActionEvent event){ if(logger.isInfoEnabled()) logger.info("exporting roster as CSV for gradebook " + getGradebookUid()); getGradebookBean().getEventTrackingService().postEvent("gradebook.downloadRoster","/gradebook/"+getGradebookId()+"/"+getAuthzLevel()); if (isUserAbleToGradeAll()) { SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(true, true), getDownloadFileName(getLocalizedString("export_gradebook_prefix")), new SpreadsheetDataFileWriterCsv()); } else { SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(false, true), getDownloadFileName(getLocalizedString("export_gradebook_prefix")), new SpreadsheetDataFileWriterCsv()); } } public void exportExcel(ActionEvent event){ if(logger.isInfoEnabled()) logger.info("exporting roster as Excel for gradebook " + getGradebookUid()); String authzLevel = (getGradebookBean().getAuthzService().isUserAbleToGradeAll(getGradebookUid())) ?"instructor" : "TA"; getGradebookBean().getEventTrackingService().postEvent("gradebook.downloadRoster","/gradebook/"+getGradebookId()+"/"+getAuthzLevel()); if (isUserAbleToGradeAll()) { SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(true, false), getDownloadFileName(getLocalizedString("export_gradebook_prefix")), new SpreadsheetDataFileWriterXls()); } else { SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(false, false), getDownloadFileName(getLocalizedString("export_gradebook_prefix")), new SpreadsheetDataFileWriterXls()); } } public void exportPdf(ActionEvent event){ if(logger.isInfoEnabled()) logger.info("exporting roster as Pdf for gradebook " + getGradebookUid()); String authzLevel = (getGradebookBean().getAuthzService().isUserAbleToGradeAll(getGradebookUid())) ?"instructor" : "TA"; getGradebookBean().getEventTrackingService().postEvent("gradebook.downloadRoster","/gradebook/"+getGradebookId()+"/"+getAuthzLevel()); if (isUserAbleToGradeAll()) { SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(true, true), getDownloadFileName(getLocalizedString("export_gradebook_prefix")), new SpreadsheetDataFileWriterPdf()); } else { SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(false, true), getDownloadFileName(getLocalizedString("export_gradebook_prefix")), new SpreadsheetDataFileWriterPdf()); } } private List<List<Object>> getSpreadsheetData(boolean includeCourseGrade, boolean localizeScores) { // Get the full list of filtered enrollments and scores (not just the current page's worth). Map enrRecItemIdFunctionMap = getWorkingEnrollmentsForAllItems(); List filteredEnrollments = new ArrayList(enrRecItemIdFunctionMap.keySet()); Collections.sort(filteredEnrollments, ENROLLMENT_NAME_COMPARATOR); Set<String> studentUids = new HashSet<String>(); Map studentIdItemIdFunctionMap = new HashMap(); List availableItems = new ArrayList(); for (Iterator iter = filteredEnrollments.iterator(); iter.hasNext(); ) { EnrollmentRecord enrollment = (EnrollmentRecord)iter.next(); String studentUid = enrollment.getUser().getUserUid(); studentUids.add(studentUid); Map itemIdFunctionMap = (Map)enrRecItemIdFunctionMap.get(enrollment); studentIdItemIdFunctionMap.put(studentUid, itemIdFunctionMap); // get the actual items to determine the gradable objects if (!itemIdFunctionMap.isEmpty()) { availableItems.addAll(itemIdFunctionMap.keySet()); } } Map filteredGradesMap = new HashMap(); List gradeRecords = getGradebookManager().getAllAssignmentGradeRecordsConverted(getGradebookId(), studentUids); if (!isUserAbleToGradeAll() && isUserHasGraderPermissions()) { getGradebookManager().addToGradeRecordMap(filteredGradesMap, gradeRecords, studentIdItemIdFunctionMap); } else { getGradebookManager().addToGradeRecordMap(filteredGradesMap, gradeRecords); } Category selCategoryView = getSelectedCategory(); List gradableObjects = new ArrayList(); List allAssignments = new ArrayList(); List categoriesFilter = new ArrayList(); if (getCategoriesEnabled()) { List categoryList = getGradebookManager().getCategoriesWithStats(getGradebookId(), getPreferencesBean().getAssignmentSortColumn(), getPreferencesBean().isAssignmentSortAscending(), getPreferencesBean().getCategorySortColumn(), getPreferencesBean().isCategorySortAscending()); // filter out the CourseGrade from the Category list to prevent errors for (Iterator catIter = categoryList.iterator(); catIter.hasNext();) { Object catOrCourseGrade = catIter.next(); if (catOrCourseGrade instanceof Category) { categoriesFilter.add((Category)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>) categoriesFilter) { 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>) categoriesFilter) { if(viewableCats.contains(category.getId())){ tmpCatList.add(category); } } categoryList = tmpCatList; } if (categoryList != null && !categoryList.isEmpty()) { Iterator catIter = categoryList.iterator(); while (catIter.hasNext()) { Object myCat = catIter.next(); if (myCat instanceof Category) { List assignmentList = ((Category)myCat).getAssignmentList(); if (assignmentList != null && !assignmentList.isEmpty()) { Iterator assignIter = assignmentList.iterator(); while (assignIter.hasNext()) { Assignment assign = (Assignment) assignIter.next(); allAssignments.add(assign); } } } } } if (!isUserAbleToGradeAll() && (isUserHasGraderPermissions() && !getGradebookPermissionService().getPermissionForUserForAllAssignment(getGradebookId(), getUserUid()))) { // is not authorized to view the "Unassigned" Category } else { List unassignedList = getGradebookManager().getAssignmentsWithNoCategory(getGradebookId(), getPreferencesBean().getAssignmentSortColumn(), getPreferencesBean().isAssignmentSortAscending()); if (unassignedList != null && !unassignedList.isEmpty()) { Iterator unassignedIter = unassignedList.iterator(); while (unassignedIter.hasNext()) { Assignment assignWithNoCat = (Assignment) unassignedIter.next(); allAssignments.add(assignWithNoCat); } } } } else { allAssignments = getGradebookManager().getAssignments(getGradebookId()); } if (!allAssignments.isEmpty()) { for (Iterator assignIter = allAssignments.iterator(); assignIter.hasNext();) { Assignment assign = (Assignment) assignIter.next(); if (availableItems.contains(assign.getId()) && (selCategoryView == null || (assign.getCategory() != null && (assign.getCategory()).getId().equals(selCategoryView.getId())))) { gradableObjects.add(assign); } } } // don't include the course grade column if the user doesn't have grade all perm // or if the view is filtered by category if (!isUserAbleToGradeAll() || selCategoryView != null) { includeCourseGrade = false; } if (includeCourseGrade) { CourseGrade courseGrade = getGradebookManager().getCourseGrade(getGradebookId()); List courseGradeRecords = getGradebookManager().getPointsEarnedCourseGradeRecords(courseGrade, studentUids, gradableObjects, filteredGradesMap); getGradebookManager().addToGradeRecordMap(filteredGradesMap, courseGradeRecords); gradableObjects.add(courseGrade); } return getSpreadsheetData(filteredEnrollments, filteredGradesMap, gradableObjects, includeCourseGrade, localizeScores); } /** * Creates the actual 'spreadsheet' List needed from gradebook objects * Modified to export without Course Grade column if desired * Format: * Header Row: Student id, Student Name, Assignment(s) (with [points possible] after title) * Student Rows * * @param enrollments * @param gradesMap * @param gradableObjects * @param includeCourseGrade * @return */ private List<List<Object>> getSpreadsheetData(List enrollments, Map gradesMap, List gradableObjects, boolean includeCourseGrade, boolean localizeScores) { List<List<Object>> spreadsheetData = new ArrayList<List<Object>>(); NumberFormat nf = NumberFormat.getInstance(new ResourceLoader().getLocale()); // Build column headers and points possible rows. List<Object> headerRow = new ArrayList<Object>(); List<Object> pointsPossibleRow = new ArrayList<Object>(); headerRow.add(getLocalizedString("export_student_id")); headerRow.add(getLocalizedString("export_student_name")); for (Object gradableObject : gradableObjects) { String colName = null; Double ptsPossible = 0.0; if (gradableObject instanceof Assignment) { ptsPossible = new Double(((Assignment) gradableObject).getPointsPossible()); colName = ((Assignment)gradableObject).getName() + " [" + nf.format(ptsPossible) + "]"; } else if (gradableObject instanceof CourseGrade && includeCourseGrade) { colName = getLocalizedString("roster_course_grade_column_name"); if(ServerConfigurationService.getBoolean("gradebook.roster.showCourseGradePoints", false) && ((CourseGrade) gradableObject).getGradebook().getGrade_type() == GradebookService.GRADE_TYPE_POINTS){ colName += " " + getLocalizedString("roster_export_percentage"); //add total points header headerRow.add(getLocalizedString("roster_course_grade_column_name") + " " + getLocalizedString("roster_export_points")); } } headerRow.add(colName); } spreadsheetData.add(headerRow); // Build student score rows. for (Object enrollment : enrollments) { User student = ((EnrollmentRecord)enrollment).getUser(); String studentUid = student.getUserUid(); Map studentMap = (Map)gradesMap.get(studentUid); List<Object> row = new ArrayList<Object>(); row.add(student.getDisplayId()); row.add(student.getSortName()); for (Object gradableObject : gradableObjects) { Object score = null; String letterScore = null; boolean droppedScore = false; if (studentMap != null) { Long gradableObjectId = ((GradableObject)gradableObject).getId(); AbstractGradeRecord gradeRecord = (AbstractGradeRecord)studentMap.get(gradableObjectId); if (gradeRecord != null) { if (gradeRecord.isCourseGradeRecord()) { if (includeCourseGrade) { score = gradeRecord.getGradeAsPercentage(); if(((CourseGradeRecord)gradeRecord).getEnteredGrade() != null){ score = "*" + score; } if(ServerConfigurationService.getBoolean("gradebook.roster.showCourseGradePoints", false) && ((CourseGrade) gradableObject).getGradebook().getGrade_type() == GradebookService.GRADE_TYPE_POINTS){ //add total points row.add(gradeRecord.getPointsEarned()); } } } else { if(gradeRecord instanceof AssignmentGradeRecord){ droppedScore = ((AssignmentGradeRecord)gradeRecord).getDroppedFromGrade(); } if (getGradeEntryByPoints()) { score = gradeRecord.getPointsEarned(); } else if (getGradeEntryByPercent()) { score = ((AssignmentGradeRecord)gradeRecord).getPercentEarned(); } else if (getGradeEntryByLetter()) { score = ((AssignmentGradeRecord)gradeRecord).getLetterEarned(); } } } } if (score != null && score instanceof Double) { score = new Double(FacesUtil.getRoundDown(((Double)score).doubleValue(), 2)); // SAK-19849: do NOT localize the score if exporting to Excel. Let Excel localize it! if (localizeScores) { score = nf.format(score); } } if(droppedScore){ score = score.toString() + " (" + getLocalizedString("export_dropped") + ")"; } row.add(score); } spreadsheetData.add(row); } return spreadsheetData; } public String assignmentDetails(){ return "assignmentDetails"; } /** * Set Tool session navigation values when navigating * to assignment details page. */ public String navigateToAssignmentDetails() { setNav("roster", "false", "false", "false", null); return "assignmentDetails"; } public String saveHidden(){ for (Iterator listIter = gradableObjectColumns.iterator(); listIter.hasNext();) { GradableObjectColumn col = (GradableObjectColumn) listIter.next(); if(col.hasHiddenChanged()){ //save Assignment assignment = getGradebookManager().getAssignment(col.getAssignmentId()); assignment.setHideInAllGradesTable(col.getHideInAllGradesTable()); getGradebookManager().updateAssignment(assignment); } } return null; } }