/**********************************************************************************
*
* $Id: Assignment.java 105077 2012-02-24 22:54:29Z 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;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import org.sakaiproject.service.gradebook.shared.GradebookService;
/**
* An Assignment is the basic unit that composes a gradebook. It represents a
* single unit that, when aggregated in a gradebook, can be used as the
* denomenator in calculating a CourseGradeRecord.
*
* @author <a href="mailto:jholtzman@berkeley.edu">Josh Holtzman</a>
*/
public class Assignment extends GradableObject {
public static String SORT_BY_DATE = "dueDate";
public static String SORT_BY_NAME = "name";
public static String SORT_BY_MEAN = "mean";
public static String SORT_BY_POINTS = "pointsPossible";
public static String SORT_BY_RELEASED ="released";
public static String SORT_BY_COUNTED = "counted";
public static String SORT_BY_EDITOR = "gradeEditor";
public static String SORT_BY_SORTING = "sorting";
public static String DEFAULT_SORT = SORT_BY_SORTING;
public static String item_type_points = "Points";
public static String item_type_percentage = "Percentage";
public static String item_type_letter = "Letter Grade";
public static String item_type_nonCalc = "Non-calculating";
public static String item_type_adjustment = "Adjustment";
public static Comparator dateComparator;
public static Comparator nameComparator;
public static Comparator pointsComparator;
public static Comparator meanComparator;
public static Comparator releasedComparator;
public static Comparator countedComparator;
public static Comparator gradeEditorComparator;
private Double pointsPossible;
private Date dueDate;
private boolean notCounted;
private boolean externallyMaintained;
private String externalStudentLink;
private String externalInstructorLink;
private String externalId;
private String externalAppName;
private boolean released;
private Category category;
private Double averageTotal;
private boolean ungraded;
private Boolean extraCredit = Boolean.FALSE;
private Double assignmentWeighting;
private Boolean countNullsAsZeros;
private String itemType;
public String selectedGradeEntryValue;
private boolean hideInAllGradesTable = false;
static {
dateComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if(log.isDebugEnabled()) log.debug("Comparing assignment + " + o1 + " to " + o2 + " by date");
Assignment one = (Assignment)o1;
Assignment two = (Assignment)o2;
// Sort by name if no date on either
if(one.getDueDate() == null && two.getDueDate() == null) {
return one.getName().toLowerCase().compareTo(two.getName().toLowerCase());
}
// Null dates are last
if(one.getDueDate() == null) {
return 1;
}
if(two.getDueDate() == null) {
return -1;
}
// Sort by name if both assignments have the same date
int comp = (one.getDueDate().compareTo(two.getDueDate()));
if(comp == 0) {
return one.getName().toLowerCase().compareTo(two.getName().toLowerCase());
} else {
return comp;
}
}
@Override
public String toString() {
return "Assignment.dateComparator";
}
};
nameComparator = new Comparator() {
public int compare(Object o1, Object o2) {
Assignment one = (Assignment)o1;
Assignment two = (Assignment)o2;
return one.getName().toLowerCase().compareTo(two.getName().toLowerCase());
}
@Override
public String toString() {
return "Assignment.nameComparator";
}
};
pointsComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if(log.isDebugEnabled()) log.debug("Comparing assignment + " + o1 + " to " + o2 + " by points");
Assignment one = (Assignment)o1;
Assignment two = (Assignment)o2;
int comp = one.getPointsPossible().compareTo(two.getPointsPossible());
if(comp == 0) {
return one.getName().toLowerCase().compareTo(two.getName().toLowerCase());
} else {
return comp;
}
}
@Override
public String toString() {
return "Assignment.pointsComparator";
}
};
meanComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if(log.isDebugEnabled()) log.debug("Comparing assignment + " + o1 + " to " + o2 + " by mean");
Assignment one = (Assignment)o1;
Assignment two = (Assignment)o2;
Double mean1 = one.getMean();
Double mean2 = two.getMean();
if(mean1 == null && mean2 == null) {
return one.getName().toLowerCase().compareTo(two.getName().toLowerCase());
}
if(mean1 != null && mean2 == null) {
return 1;
}
if(mean1 == null && mean2 != null) {
return -1;
}
int comp = mean1.compareTo(mean2);
if(comp == 0) {
return one.getName().toLowerCase().compareTo(two.getName().toLowerCase());
} else {
return comp;
}
}
@Override
public String toString() {
return "Assignment.meanComparator";
}
};
releasedComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if(log.isDebugEnabled()) log.debug("Comparing assignment + " + o1 + " to " + o2 + " by release");
Assignment one = (Assignment)o1;
Assignment two = (Assignment)o2;
int comp = String.valueOf(one.isReleased()).compareTo(String.valueOf(two.isReleased()));
if(comp == 0) {
return one.getName().compareTo(two.getName());
} else {
return comp;
}
}
@Override
public String toString() {
return "Assignment.releasedComparator";
}
};
countedComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if(log.isDebugEnabled()) log.debug("Comparing assignment + " + o1 + " to " + o2 + " by counted");
Assignment one = (Assignment)o1;
Assignment two = (Assignment)o2;
int comp = String.valueOf(one.isCounted()).compareTo(String.valueOf(two.isCounted()));
if(comp == 0) {
return one.getName().compareTo(two.getName());
} else {
return comp;
}
}
@Override
public String toString() {
return "Assignment.countedComparator";
}
};
gradeEditorComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if(log.isDebugEnabled()) log.debug("Comparing assignment + " + o1 + " to " + o2 + " by grade editor");
Assignment one = (Assignment)o1;
Assignment two = (Assignment)o2;
int comp = String.valueOf(one.getExternalAppName()).compareTo(String.valueOf(two.getExternalAppName()));
if(comp == 0) {
return one.getName().compareTo(two.getName());
} else {
return comp;
}
}
@Override
public String toString() {
return "Assignment.gradeEditorComparator";
}
};
}
public Assignment(Gradebook gradebook, String name, Double pointsPossible, Date dueDate) {
this.gradebook = gradebook;
this.name = name;
this.pointsPossible = pointsPossible;
this.dueDate = dueDate;
this.released = true;
this.extraCredit = Boolean.FALSE;
this.hideInAllGradesTable = false;
}
/**
* constructor to support selective release
* @param gradebook
* @param name
* @param pointsPossible
* @param dueDate
* @param released
*/
public Assignment(Gradebook gradebook, String name, Double pointsPossible, Date dueDate, boolean released) {
this.gradebook = gradebook;
this.name = name;
this.pointsPossible = pointsPossible;
this.dueDate = dueDate;
this.released = released;
this.extraCredit = Boolean.FALSE;
this.hideInAllGradesTable = false;
}
public Assignment() {
super();
this.extraCredit = Boolean.FALSE;
this.hideInAllGradesTable = false;
}
/**
*/
public boolean isCourseGrade() {
return false;
}
/**
* @see org.sakaiproject.tool.gradebook.GradableObject#isAssignment()
*/
public boolean isAssignment() {
return true;
}
/**
* @see org.sakaiproject.tool.gradebook.GradableObject#isCategory()
*/
public boolean getIsCategory() {
return false;
}
/**
*/
public Date getDateForDisplay() {
return dueDate;
}
/**
* @return Returns the dueDate.
*/
public Date getDueDate() {
return dueDate;
}
/**
* @param dueDate The dueDate to set.
*/
public void setDueDate(Date dueDate) {
this.dueDate = dueDate;
}
/**
*/
public boolean isNotCounted() {
return notCounted;
}
/**
*/
public void setNotCounted(boolean notCounted) {
this.notCounted = notCounted;
}
/**
*/
public boolean isCounted() {
return !isNotCounted();
}
/**
* This cover is for the benefit of JSF checkboxes.
*/
public void setCounted(boolean counted) {
setNotCounted(!counted);
}
/**
* @return Returns the externalInstructorLink.
*/
public String getExternalInstructorLink() {
return externalInstructorLink;
}
/**
* @param externalInstructorLink The externalInstructorLink to set.
*/
public void setExternalInstructorLink(String externalInstructorLink) {
this.externalInstructorLink = externalInstructorLink;
}
/**
* @return Returns the externallyMaintained.
*/
public boolean isExternallyMaintained() {
return externallyMaintained;
}
/**
* @param externallyMaintained The externallyMaintained to set.
*/
public void setExternallyMaintained(boolean externallyMaintained) {
this.externallyMaintained = externallyMaintained;
}
/**
* @return Returns the externalStudentLink.
*/
public String getExternalStudentLink() {
return externalStudentLink;
}
/**
* @param externalStudentLink The externalStudentLink to set.
*/
public void setExternalStudentLink(String externalStudentLink) {
this.externalStudentLink = externalStudentLink;
}
/**
* @return Returns the pointsPossible.
*/
public Double getPointsPossible() {
return pointsPossible;
}
/**
* @param pointsPossible The pointsPossible to set.
*/
public void setPointsPossible(Double pointsPossible) {
this.pointsPossible = pointsPossible;
}
/**
* @return Returns the externalId.
*/
public String getExternalId() {
return externalId;
}
/**
* @param externalId The externalId to set.
*/
public void setExternalId(String externalId) {
this.externalId = externalId;
}
/**
* @return Returns the externalAppName.
*/
public String getExternalAppName() {
return externalAppName;
}
/**
* @param externalAppName The externalAppName to set.
*/
public void setExternalAppName(String externalAppName) {
this.externalAppName = externalAppName;
}
/**
*
* @return selective release true or false
*/
public boolean isReleased() {
return released;
}
/**
*
* @param released returns wther the assignment has been released to users
*/
public void setReleased(boolean released) {
this.released = released;
}
/**
* Calculate the mean score for students with entered grades.
*/
public void calculateStatistics(Collection<AssignmentGradeRecord> gradeRecords) {
int numScored = 0;
BigDecimal total = new BigDecimal("0");
BigDecimal pointsTotal = new BigDecimal("0");
for (AssignmentGradeRecord record : gradeRecords) {
// Skip grade records that don't apply to this gradable object
if(!record.getGradableObject().equals(this)) {
continue;
}
if(record.getDroppedFromGrade() == null) {
throw new RuntimeException("record.droppedFromGrade cannot be null");
}
Double score = null;
if(!ungraded && pointsPossible > 0)
score = record.getGradeAsPercentage();
Double points = record.getPointsEarned();
if (score == null && points == null || record.getDroppedFromGrade()) {
continue;
}
else if (score == null)
{
pointsTotal = pointsTotal.add(new BigDecimal(points.toString()));
numScored++;
}
else
{
total = total.add(new BigDecimal(score.toString()));
pointsTotal = pointsTotal.add(new BigDecimal(points.toString()));
numScored++;
}
}
if (numScored == 0) {
mean = null;
averageTotal = null;
} else {
BigDecimal bdNumScored = new BigDecimal(numScored);
if(!ungraded && pointsPossible > 0)
{
mean = Double.valueOf(total.divide(bdNumScored, GradebookService.MATH_CONTEXT).doubleValue());
}
else
{
mean = null;
}
averageTotal = Double.valueOf(pointsTotal.divide(bdNumScored, GradebookService.MATH_CONTEXT).doubleValue());
}
}
public Category getCategory()
{
return category;
}
public void setCategory(Category category)
{
this.category = category;
}
public Double getAverageTotal()
{
return averageTotal;
}
public void setAverageTotal(Double averageTotal)
{
this.averageTotal = averageTotal;
}
public boolean getUngraded()
{
return ungraded;
}
public void setUngraded(boolean ungraded)
{
this.ungraded = ungraded;
}
//these two functions are needed to keep the old API and help JSF and RSF play nicely together. Since isExtraCredit already exists and we can't remove it
//and JSF expects Boolean values to be "getExtraCredit", this had to be added for JSF. Also, since the external GB create item page is in
//RSF, you can't name it getExtraCredit and keep isExtraCredit b/c of SAK-14589
public Boolean getIsExtraCredit(){
return isExtraCredit();
}
public void setIsExtraCredit(Boolean isExtraCredit){
this.setExtraCredit(isExtraCredit);
}
public Boolean isExtraCredit() {
if(extraCredit == null){
return Boolean.FALSE;
}
return extraCredit;
}
public void setExtraCredit(Boolean isExtraCredit) {
this.extraCredit = isExtraCredit;
}
public Double getAssignmentWeighting() {
return assignmentWeighting;
}
public void setAssignmentWeighting(Double assignmentWeighting) {
this.assignmentWeighting = assignmentWeighting;
}
public String getItemType() {
Gradebook gb = getGradebook();
if (gb!=null)
{
if (isExtraCredit()!=null)
{
if (isExtraCredit())
{
// if we made it in here, go ahead and return since adjustment item takes priority over the rest
itemType = item_type_adjustment;
return itemType;
}
}
if (getUngraded())
{
// if we made it in here, go ahead and return since non-calc item takes priority over the rest
itemType = item_type_nonCalc;
return itemType;
}
if(gb.getGrade_type() == GradebookService.GRADE_TYPE_POINTS)
{
itemType = item_type_points;
}
else if(gb.getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE)
{
itemType = item_type_percentage;
}
else if(gb.getGrade_type() == GradebookService.GRADE_TYPE_LETTER)
{
itemType = item_type_letter;
}
}
return itemType;
}
public void setItemType(String itemType) {
this.itemType = itemType;
}
public Boolean getCountNullsAsZeros() {
return countNullsAsZeros;
}
public void setCountNullsAsZeros(Boolean countNullsAsZeros) {
this.countNullsAsZeros = countNullsAsZeros;
}
public String getSelectedGradeEntryValue() {
return selectedGradeEntryValue;
}
public void setSelectedGradeEntryValue(String selectedGradeEntryValue) {
this.selectedGradeEntryValue = selectedGradeEntryValue;
}
/**
* Convenience method for checking if the grade for the assignment should be included in calculations.
* This is different from just the {@link #isCounted()} method for an assignment. This method does a more thorough check
* using other values, such as if removed, isExtraCredit, ungraded, etc in addition to the assignment's notCounted property.
* @return true if grades for this assignment should be included in various calculations.
*/
public boolean isIncludedInCalculations() {
boolean isIncludedInCalculations = false;
boolean extraCredit = isExtraCredit()!=null && isExtraCredit();
if (!removed && !ungraded && !notCounted && (extraCredit || (pointsPossible != null && pointsPossible>0)))
{
isIncludedInCalculations = true;
}
return isIncludedInCalculations;
}
public boolean isHideInAllGradesTable() {
return hideInAllGradesTable;
}
public void setHideInAllGradesTable(boolean hideInAllGradesTable) {
this.hideInAllGradesTable = hideInAllGradesTable;
}
}