/********************************************************************************** * * $Id: GradebookCalculationImpl.java 63658 2009-06-15 13:49:40Z wagnermr@iupui.edu $ * *********************************************************************************** * * Copyright (c) 2005, 2006 The Regents of the University of California, The MIT Corporation * * Licensed under the Educational Community License, Version 1.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/ecl1.php * * 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.business.impl; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.sakaiproject.service.gradebook.shared.GradebookService; 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.Gradebook; import org.sakaiproject.tool.gradebook.business.GradebookManager; import org.springframework.orm.hibernate3.HibernateCallback; public class GradebookCalculationImpl extends GradebookManagerHibernateImpl implements GradebookManager { private static final Log log = LogFactory.getLog(GradebookCalculationImpl.class); public List getPointsEarnedCourseGradeRecords(final CourseGrade courseGrade, final Collection studentUids) { HibernateCallback hc = new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException { if(studentUids == null || studentUids.size() == 0) { if(log.isDebugEnabled()) log.debug("Returning no grade records for an empty collection of student UIDs in GradebookCalculationImpl.getPointsEarnedCourseGradeRecords(CourseGrade, Collection)."); return new ArrayList(); } Long gradebookId = courseGrade.getGradebook().getId(); // get all of the AssignmentGradeRecords here to avoid repeated db calls Map<String, List<AssignmentGradeRecord>> studentIdGradeRecordsMap = getGradeRecordMapForStudents(session, gradebookId, studentUids); // get all of the counted assignments List<Assignment> countedAssigns = getCountedAssignments(session, gradebookId); return getPointsEarnedCourseGradeRecords(session, courseGrade, studentUids, countedAssigns, studentIdGradeRecordsMap); } }; return (List)getHibernateTemplate().execute(hc); } public List getPointsEarnedCourseGradeRecords(final CourseGrade courseGrade, final Collection studentUids, final Collection assignments, final Map gradeRecordMap) { HibernateCallback hc = new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException { if(studentUids == null || studentUids.size() == 0) { if(log.isDebugEnabled()) log.debug("Returning no grade records for an empty collection of student UIDs in GradebookCalculationImpl.getPointsEarnedCourseGradeRecords"); return new ArrayList(); } // let's make the grade map more manageable here. it starts out as // Map of studentId --> Map of assignment id --> corresponding AssignmentGradeRecord Map<String, List<AssignmentGradeRecord>> studentIdGradeRecordsMap = new HashMap<String, List<AssignmentGradeRecord>>(); if (gradeRecordMap != null) { for (Iterator stIter = studentUids.iterator(); stIter.hasNext();) { String studentUid = (String)stIter.next(); Map<Long, AssignmentGradeRecord> studentMap = (Map<Long, AssignmentGradeRecord>)gradeRecordMap.get(studentUid); if (studentMap != null) { List<AssignmentGradeRecord> studentGradeRecs = new ArrayList<AssignmentGradeRecord>(studentMap.values()); studentIdGradeRecordsMap.put(studentUid, studentGradeRecs); } } } return getPointsEarnedCourseGradeRecords(session, courseGrade, studentUids, assignments, studentIdGradeRecordsMap); } }; return (List)getHibernateTemplate().execute(hc); } /** * * @param session * @param courseGrade * @param studentUids * @param studentIdGradeRecordsMap map of studentId --> List of that student's AssignmentGradeRecords * @return a list of the CourseGradeRecords with nonpersisted fields populated for the given students and * associated AssignmentGradeRecords */ private List<CourseGradeRecord> getPointsEarnedCourseGradeRecords(Session session, CourseGrade courseGrade, Collection<String> studentUids, Collection<Assignment> assignments, Map<String, List<AssignmentGradeRecord>> studentIdGradeRecordsMap) { List<CourseGradeRecord> courseGradeRecs = new ArrayList<CourseGradeRecord>(); if (studentUids != null && !studentUids.isEmpty()) { int gbGradeType = getGradebook(courseGrade.getGradebook().getId()).getGrade_type(); Query q = session.createQuery("from CourseGradeRecord as cgr where cgr.gradableObject.id=:gradableObjectId"); q.setLong("gradableObjectId", courseGrade.getId().longValue()); courseGradeRecs = filterAndPopulateCourseGradeRecordsByStudents(courseGrade, q.list(), studentUids); Long gradebookId = courseGrade.getGradebook().getId(); Gradebook gradebook = getGradebook(gradebookId); List cates = getCategories(gradebookId); List<Assignment> countedAssigns = new ArrayList<Assignment>(); // let's filter the passed assignments to make sure they are all counted if (assignments != null) { for (Assignment assign : assignments) { if (assign.isIncludedInCalculations()) { countedAssigns.add(assign); } } } // no need to calculate anything for non-calculating gradebook if (gbGradeType != GradebookService.GRADE_TYPE_LETTER) { Map<String, Set<Assignment>> visibleExternals = getVisibleExternalAssignments(courseGrade.getGradebook(), studentUids, countedAssigns); for(CourseGradeRecord cgr : courseGradeRecs) { // Filter out external activities that are not visible to this student, considering them "uncounted" List<Assignment> studentCountedAssigns = new ArrayList<Assignment>(); String studentId = cgr.getStudentId(); for (Assignment a : countedAssigns) { if (!a.isExternallyMaintained() || (visibleExternals.containsKey(studentId) && visibleExternals.get(studentId).contains(a))) { studentCountedAssigns.add(a); } } //double totalPointsEarned = getTotalPointsEarnedInternal(gradebookId, cgr.getStudentId(), session); List<AssignmentGradeRecord> studentGradeRecs; if (studentIdGradeRecordsMap == null) { studentGradeRecs = new ArrayList<AssignmentGradeRecord>(); } else { studentGradeRecs = studentIdGradeRecordsMap.get(cgr.getStudentId()); } applyDropScores(studentGradeRecs); List totalEarned = getTotalPointsEarnedInternal(cgr.getStudentId(), gradebook, cates, studentGradeRecs, studentCountedAssigns); double totalPointsEarned = ((Double)totalEarned.get(0)).doubleValue(); double literalTotalPointsEarned = ((Double)totalEarned.get(1)).doubleValue(); double totalPointsPossible = getTotalPointsInternal(gradebook, cates, cgr.getStudentId(), studentGradeRecs, studentCountedAssigns, false); cgr.initNonpersistentFields(totalPointsPossible, totalPointsEarned, literalTotalPointsEarned); if(log.isDebugEnabled()) log.debug("Points earned = " + cgr.getPointsEarned()); } } } return courseGradeRecs; } List getTotalPointsEarnedInternal(final String studentId, final Gradebook gradebook, final List categories, final List<AssignmentGradeRecord> gradeRecs, List<Assignment> countedAssigns) { int gbGradeType = gradebook.getGrade_type(); if( gbGradeType != GradebookService.GRADE_TYPE_POINTS && gbGradeType != GradebookService.GRADE_TYPE_PERCENTAGE) { if(log.isInfoEnabled()) log.error("Wrong grade type in GradebookCalculationImpl.getTotalPointsEarnedInternal"); return new ArrayList(); } if (gradeRecs == null || countedAssigns == null) { if (log.isDebugEnabled()) log.debug("getTotalPointsEarnedInternal for " + "studentId=" + studentId + " returning 0 because null gradeRecs or countedAssigns"); List returnList = new ArrayList(); returnList.add(new Double(0)); returnList.add(new Double(0)); returnList.add(new Double(0)); // 3rd one is for the pre-adjusted course grade return returnList; } double totalPointsEarned = 0; BigDecimal literalTotalPointsEarned = new BigDecimal(0d); Map cateScoreMap = new HashMap(); Map cateTotalScoreMap = new HashMap(); Set assignmentsTaken = new HashSet(); for (AssignmentGradeRecord gradeRec : gradeRecs) { if(gradeRec.getPointsEarned() != null && !gradeRec.getPointsEarned().equals("") && !gradeRec.getDroppedFromGrade()) { Assignment go = gradeRec.getAssignment(); if (go.isIncludedInCalculations()) { Double pointsEarned = new Double(gradeRec.getPointsEarned()); //if(gbGradeType == GradebookService.GRADE_TYPE_POINTS) //{ if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY) { totalPointsEarned += pointsEarned.doubleValue(); literalTotalPointsEarned = (new BigDecimal(pointsEarned.doubleValue())).add(literalTotalPointsEarned); assignmentsTaken.add(go.getId()); } else if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_ONLY_CATEGORY && go != null) { totalPointsEarned += pointsEarned.doubleValue(); literalTotalPointsEarned = (new BigDecimal(pointsEarned.doubleValue())).add(literalTotalPointsEarned); assignmentsTaken.add(go.getId()); } else if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY && go != null && categories != null) { for(int i=0; i<categories.size(); i++) { Category cate = (Category) categories.get(i); if(cate != null && !cate.isRemoved() && go.getCategory() != null && cate.getId().equals(go.getCategory().getId())) { assignmentsTaken.add(go.getId()); literalTotalPointsEarned = (new BigDecimal(pointsEarned.doubleValue())).add(literalTotalPointsEarned); if(cateScoreMap.get(cate.getId()) != null) { cateScoreMap.put(cate.getId(), new Double(((Double)cateScoreMap.get(cate.getId())).doubleValue() + pointsEarned.doubleValue())); } else { cateScoreMap.put(cate.getId(), new Double(pointsEarned)); } break; } } } } } } if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY && categories != null) { Iterator assgnsIter = countedAssigns.iterator(); while (assgnsIter.hasNext()) { Assignment asgn = (Assignment)assgnsIter.next(); if(assignmentsTaken.contains(asgn.getId())) { for(int i=0; i<categories.size(); i++) { Category cate = (Category) categories.get(i); if(cate != null && !cate.isRemoved() && asgn.getCategory() != null && cate.getId().equals(asgn.getCategory().getId()) && !asgn.isExtraCredit()) { if(cateTotalScoreMap.get(cate.getId()) == null) { cateTotalScoreMap.put(cate.getId(), asgn.getPointsPossible()); } else { cateTotalScoreMap.put(cate.getId(), new Double(((Double)cateTotalScoreMap.get(cate.getId())).doubleValue() + asgn.getPointsPossible().doubleValue())); } } } } } } if(assignmentsTaken.isEmpty()) totalPointsEarned = -1; if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY) { for(int i=0; i<categories.size(); i++) { Category cate = (Category) categories.get(i); if(cate != null && !cate.isRemoved() && cateScoreMap.get(cate.getId()) != null && cateTotalScoreMap.get(cate.getId()) != null) { totalPointsEarned += ((Double)cateScoreMap.get(cate.getId())).doubleValue() * cate.getWeight().doubleValue() / ((Double)cateTotalScoreMap.get(cate.getId())).doubleValue(); } } } if (log.isDebugEnabled()) log.debug("getTotalPointsEarnedInternal for studentId=" + studentId + " returning " + totalPointsEarned); List returnList = new ArrayList(); returnList.add(new Double(totalPointsEarned)); returnList.add(new Double((new BigDecimal(literalTotalPointsEarned.doubleValue(), GradebookService.MATH_CONTEXT)).doubleValue())); return returnList; } public double getTotalPointsInternal(final Gradebook gradebook, final List categories, final String studentId, List<AssignmentGradeRecord> studentGradeRecs, List<Assignment> countedAssigns, boolean literalTotal) { int gbGradeType = gradebook.getGrade_type(); if( gbGradeType != GradebookService.GRADE_TYPE_POINTS && gbGradeType != GradebookService.GRADE_TYPE_PERCENTAGE) { if(log.isInfoEnabled()) log.error("Wrong grade type in GradebookCalculationImpl.getTotalPointsInternal"); return -1; } if (studentGradeRecs == null || countedAssigns == null) { if (log.isDebugEnabled()) log.debug("Returning 0 from getTotalPointsInternal " + "since studentGradeRecs or countedAssigns was null"); return 0; } double totalPointsPossible = 0; HashSet<Assignment> countedSet = new HashSet<Assignment>(countedAssigns); // we need to filter this list to identify only "counted" grade recs List<AssignmentGradeRecord> countedGradeRecs = new ArrayList<AssignmentGradeRecord>(); for (AssignmentGradeRecord gradeRec : studentGradeRecs) { Assignment assign = gradeRec.getAssignment(); boolean extraCredit = assign.isExtraCredit(); if(gradebook.getCategory_type() != GradebookService.CATEGORY_TYPE_NO_CATEGORY && assign.getCategory() != null && assign.getCategory().isExtraCredit()) extraCredit = true; if (assign.isCounted() && !assign.getUngraded() && !assign.isRemoved() && countedSet.contains(assign) && assign.getPointsPossible() != null && assign.getPointsPossible() > 0 && !gradeRec.getDroppedFromGrade() && !extraCredit) { countedGradeRecs.add(gradeRec); } } Set assignmentsTaken = new HashSet(); Set categoryTaken = new HashSet(); for (AssignmentGradeRecord gradeRec : countedGradeRecs) { if (gradeRec.getPointsEarned() != null && !gradeRec.getPointsEarned().equals("")) { Double pointsEarned = new Double(gradeRec.getPointsEarned()); Assignment go = gradeRec.getAssignment(); if (pointsEarned != null) { if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY) { assignmentsTaken.add(go.getId()); } else if ((gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_ONLY_CATEGORY || gradebook .getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY) && go != null && categories != null) { // assignmentsTaken.add(go.getId()); // } // else if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY && go != null && categories != null) // { for(int i=0; i<categories.size(); i++) { Category cate = (Category) categories.get(i); if(cate != null && !cate.isRemoved() && go.getCategory() != null && cate.getId().equals(go.getCategory().getId()) && ((cate.isExtraCredit()!=null && !cate.isExtraCredit()) || cate.isExtraCredit()==null)) { assignmentsTaken.add(go.getId()); categoryTaken.add(cate.getId()); break; } } } } } } if(!assignmentsTaken.isEmpty()) { if(!literalTotal && gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY) { for(int i=0; i<categories.size(); i++) { Category cate = (Category) categories.get(i); if(cate != null && !cate.isRemoved() && categoryTaken.contains(cate.getId()) ) { totalPointsPossible += cate.getWeight().doubleValue(); } } return totalPointsPossible; } Iterator assignmentIter = countedAssigns.iterator(); while (assignmentIter.hasNext()) { Assignment asn = (Assignment) assignmentIter.next(); if(asn != null) { Double pointsPossible = asn.getPointsPossible(); if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY && assignmentsTaken.contains(asn.getId())) { totalPointsPossible += pointsPossible.doubleValue(); } else if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_ONLY_CATEGORY && assignmentsTaken.contains(asn.getId())) { totalPointsPossible += pointsPossible.doubleValue(); }else if(literalTotal && gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY && assignmentsTaken.contains(asn.getId())) { totalPointsPossible += pointsPossible.doubleValue(); } } } } else totalPointsPossible = -1; return totalPointsPossible; } public void applyDropScores(Collection<AssignmentGradeRecord> gradeRecords) { super.applyDropScores(gradeRecords); } }