001: /**********************************************************************************
002: *
003: * $Id: GradebookFrameworkServiceImpl.java 20648 2007-01-24 21:29:41Z ray@media.berkeley.edu $
004: *
005: ***********************************************************************************
006: *
007: * Copyright (c) 2007 The Regents of the University of California
008: *
009: * Licensed under the Educational Community License, Version 1.0 (the "License");
010: * you may not use this file except in compliance with the License.
011: * You may obtain a copy of the License at
012: *
013: * http://www.opensource.org/licenses/ecl1.php
014: *
015: * Unless required by applicable law or agreed to in writing, software
016: * distributed under the License is distributed on an "AS IS" BASIS,
017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018: * See the License for the specific language governing permissions and
019: * limitations under the License.
020: *
021: **********************************************************************************/package org.sakaiproject.component.gradebook;
022:
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.HashMap;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Set;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.hibernate.HibernateException;
035: import org.hibernate.Query;
036: import org.hibernate.Session;
037: import org.sakaiproject.service.gradebook.shared.GradebookExistsException;
038: import org.sakaiproject.service.gradebook.shared.GradebookFrameworkService;
039: import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException;
040: import org.sakaiproject.service.gradebook.shared.GradingScaleDefinition;
041: import org.sakaiproject.tool.gradebook.CourseGrade;
042: import org.sakaiproject.tool.gradebook.GradeMapping;
043: import org.sakaiproject.tool.gradebook.Gradebook;
044: import org.sakaiproject.tool.gradebook.GradingScale;
045: import org.sakaiproject.tool.gradebook.LetterGradeMapping;
046: import org.sakaiproject.tool.gradebook.LetterGradePlusMinusMapping;
047: import org.sakaiproject.tool.gradebook.PassNotPassMapping;
048: import org.springframework.orm.hibernate3.HibernateCallback;
049: import org.springframework.orm.hibernate3.HibernateTemplate;
050:
051: public class GradebookFrameworkServiceImpl extends BaseHibernateManager
052: implements GradebookFrameworkService {
053: private static final Log log = LogFactory
054: .getLog(GradebookFrameworkServiceImpl.class);
055:
056: public static final String UID_OF_DEFAULT_GRADING_SCALE_PROPERTY = "uidOfDefaultGradingScale";
057:
058: public void addGradebook(final String uid, final String name) {
059: if (isGradebookDefined(uid)) {
060: log.warn("You can not add a gradebook with uid=" + uid
061: + ". That gradebook already exists.");
062: throw new GradebookExistsException(
063: "You can not add a gradebook with uid=" + uid
064: + ". That gradebook already exists.");
065: }
066: if (log.isInfoEnabled())
067: log.info("Adding gradebook uid=" + uid + " by userUid="
068: + getUserUid());
069:
070: getHibernateTemplate().execute(new HibernateCallback() {
071: public Object doInHibernate(Session session)
072: throws HibernateException {
073: // Get available grade mapping templates.
074: List gradingScales = session
075: .createQuery(
076: "from GradingScale as gradingScale where gradingScale.unavailable=false")
077: .list();
078:
079: // The application won't be able to run without grade mapping
080: // templates, so if for some reason none have been defined yet,
081: // do that now.
082: if (gradingScales.isEmpty()) {
083: if (log.isInfoEnabled())
084: log
085: .info("No Grading Scale defined yet. This is probably because you have upgraded or you are working with a new database. Default grading scales will be created. Any customized system-wide grade mappings you may have defined in previous versions will have to be reconfigured.");
086: gradingScales = GradebookFrameworkServiceImpl.this
087: .addDefaultGradingScales(session);
088: }
089:
090: // Create and save the gradebook
091: Gradebook gradebook = new Gradebook(name);
092: gradebook.setUid(uid);
093: session.save(gradebook);
094:
095: // Create the course grade for the gradebook
096: CourseGrade cg = new CourseGrade();
097: cg.setGradebook(gradebook);
098: session.save(cg);
099:
100: // According to the specification, Display Assignment Grades is
101: // on by default, and Display course grade is off.
102: gradebook.setAssignmentsDisplayed(true);
103: gradebook.setCourseGradeDisplayed(false);
104:
105: String defaultScaleUid = GradebookFrameworkServiceImpl.this
106: .getPropertyValue(UID_OF_DEFAULT_GRADING_SCALE_PROPERTY);
107:
108: // Add and save grade mappings based on the templates.
109: GradeMapping defaultGradeMapping = null;
110: Set gradeMappings = new HashSet();
111: for (Iterator iter = gradingScales.iterator(); iter
112: .hasNext();) {
113: GradingScale gradingScale = (GradingScale) iter
114: .next();
115: GradeMapping gradeMapping = new GradeMapping(
116: gradingScale);
117: gradeMapping.setGradebook(gradebook);
118: session.save(gradeMapping);
119: gradeMappings.add(gradeMapping);
120: if (gradingScale.getUid().equals(defaultScaleUid)) {
121: defaultGradeMapping = gradeMapping;
122: }
123: }
124:
125: // Check for null default.
126: if (defaultGradeMapping == null) {
127: defaultGradeMapping = (GradeMapping) gradeMappings
128: .iterator().next();
129: if (log.isWarnEnabled())
130: log
131: .warn("No default GradeMapping found for new Gradebook="
132: + gradebook.getUid()
133: + "; will set default to "
134: + defaultGradeMapping.getName());
135: }
136: gradebook.setSelectedGradeMapping(defaultGradeMapping);
137:
138: // The Hibernate mapping as of Sakai 2.2 makes this next
139: // call meaningless when it comes to persisting changes at
140: // the end of the transaction. It is, however, needed for
141: // the mappings to be seen while the transaction remains
142: // uncommitted.
143: gradebook.setGradeMappings(gradeMappings);
144:
145: // Update the gradebook with the new selected grade mapping
146: session.update(gradebook);
147:
148: return null;
149:
150: }
151: });
152: }
153:
154: private List addDefaultGradingScales(Session session)
155: throws HibernateException {
156: List gradingScales = new ArrayList();
157:
158: // Base the default set of templates on the old
159: // statically defined GradeMapping classes.
160: GradeMapping[] oldGradeMappings = { new LetterGradeMapping(),
161: new LetterGradePlusMinusMapping(),
162: new PassNotPassMapping() };
163:
164: for (int i = 0; i < oldGradeMappings.length; i++) {
165: GradeMapping sampleMapping = oldGradeMappings[i];
166: sampleMapping.setDefaultValues();
167: GradingScale gradingScale = new GradingScale();
168: String uid = sampleMapping.getClass().getName();
169: uid = uid.substring(uid.lastIndexOf('.') + 1);
170: gradingScale.setUid(uid);
171: gradingScale.setUnavailable(false);
172: gradingScale.setName(sampleMapping.getName());
173: gradingScale.setGrades(new ArrayList(sampleMapping
174: .getGrades()));
175: gradingScale.setDefaultBottomPercents(new HashMap(
176: sampleMapping.getGradeMap()));
177: session.save(gradingScale);
178: if (log.isInfoEnabled())
179: log
180: .info("Added Grade Mapping "
181: + gradingScale.getUid());
182: gradingScales.add(gradingScale);
183: }
184: setDefaultGradingScale("LetterGradePlusMinusMapping");
185: session.flush();
186: return gradingScales;
187: }
188:
189: public void setAvailableGradingScales(
190: final Collection gradingScaleDefinitions) {
191: getHibernateTemplate().execute(new HibernateCallback() {
192: public Object doInHibernate(Session session)
193: throws HibernateException {
194: mergeGradeMappings(gradingScaleDefinitions, session);
195: return null;
196: }
197: });
198: }
199:
200: public void setDefaultGradingScale(String uid) {
201: setPropertyValue(UID_OF_DEFAULT_GRADING_SCALE_PROPERTY, uid);
202: }
203:
204: private void copyDefinitionToScale(GradingScaleDefinition bean,
205: GradingScale gradingScale) {
206: gradingScale.setUnavailable(false);
207: gradingScale.setName(bean.getName());
208: gradingScale.setGrades(bean.getGrades());
209: Map defaultBottomPercents = new HashMap();
210: Iterator gradesIter = bean.getGrades().iterator();
211: Iterator defaultBottomPercentsIter = bean
212: .getDefaultBottomPercents().iterator();
213: while (gradesIter.hasNext()
214: && defaultBottomPercentsIter.hasNext()) {
215: String grade = (String) gradesIter.next();
216: Double value = (Double) defaultBottomPercentsIter.next();
217: defaultBottomPercents.put(grade, value);
218: }
219: gradingScale.setDefaultBottomPercents(defaultBottomPercents);
220: }
221:
222: private void mergeGradeMappings(Collection gradingScaleDefinitions,
223: Session session) throws HibernateException {
224: Map newMappingDefinitionsMap = new HashMap();
225: HashSet uidsToSet = new HashSet();
226: for (Iterator iter = gradingScaleDefinitions.iterator(); iter
227: .hasNext();) {
228: GradingScaleDefinition bean = (GradingScaleDefinition) iter
229: .next();
230: newMappingDefinitionsMap.put(bean.getUid(), bean);
231: uidsToSet.add(bean.getUid());
232: }
233:
234: // Until we move to Hibernate 3 syntax, we need to update one record at a time.
235: Query q;
236: List gmtList;
237:
238: // Toggle any scales that are no longer specified.
239: q = session
240: .createQuery("from GradingScale as gradingScale where gradingScale.uid not in (:uidList) and gradingScale.unavailable=false");
241: q.setParameterList("uidList", uidsToSet);
242: gmtList = q.list();
243: for (Iterator iter = gmtList.iterator(); iter.hasNext();) {
244: GradingScale gradingScale = (GradingScale) iter.next();
245: gradingScale.setUnavailable(true);
246: session.update(gradingScale);
247: if (log.isInfoEnabled())
248: log.info("Set Grading Scale " + gradingScale.getUid()
249: + " unavailable");
250: }
251:
252: // Modify any specified scales that already exist.
253: q = session
254: .createQuery("from GradingScale as gradingScale where gradingScale.uid in (:uidList)");
255: q.setParameterList("uidList", uidsToSet);
256: gmtList = q.list();
257: for (Iterator iter = gmtList.iterator(); iter.hasNext();) {
258: GradingScale gradingScale = (GradingScale) iter.next();
259: copyDefinitionToScale(
260: (GradingScaleDefinition) newMappingDefinitionsMap
261: .get(gradingScale.getUid()), gradingScale);
262: uidsToSet.remove(gradingScale.getUid());
263: session.update(gradingScale);
264: if (log.isInfoEnabled())
265: log.info("Updated Grading Scale "
266: + gradingScale.getUid());
267: }
268:
269: // Add any new scales.
270: for (Iterator iter = uidsToSet.iterator(); iter.hasNext();) {
271: String uid = (String) iter.next();
272: GradingScale gradingScale = new GradingScale();
273: gradingScale.setUid(uid);
274: GradingScaleDefinition bean = (GradingScaleDefinition) newMappingDefinitionsMap
275: .get(uid);
276: copyDefinitionToScale(bean, gradingScale);
277: session.save(gradingScale);
278: if (log.isInfoEnabled())
279: log
280: .info("Added Grading Scale "
281: + gradingScale.getUid());
282: }
283: session.flush();
284: }
285:
286: public void deleteGradebook(final String uid)
287: throws GradebookNotFoundException {
288: if (log.isInfoEnabled())
289: log.info("Deleting gradebook uid=" + uid + " by userUid="
290: + getUserUid());
291: final Long gradebookId = getGradebook(uid).getId();
292:
293: // Worse of both worlds code ahead. We've been quick-marched
294: // into Hibernate 3 sessions, but we're also having to use classic query
295: // parsing -- which keeps us from being able to use either Hibernate's new-style
296: // bulk delete queries or Hibernate's old-style session.delete method.
297: // Instead, we're stuck with going through the Spring template for each
298: // deletion one at a time.
299: HibernateTemplate hibTempl = getHibernateTemplate();
300: // int numberDeleted = hibTempl.bulkUpdate("delete GradingEvent as ge where ge.gradableObject.gradebook.id=?", gradebookId);
301: // log.warn("GradingEvent numberDeleted=" + numberDeleted);
302:
303: List toBeDeleted;
304: int numberDeleted;
305:
306: toBeDeleted = hibTempl
307: .find(
308: "from GradingEvent as ge where ge.gradableObject.gradebook.id=?",
309: gradebookId);
310: numberDeleted = toBeDeleted.size();
311: hibTempl.deleteAll(toBeDeleted);
312: if (log.isDebugEnabled())
313: log.debug("Deleted " + numberDeleted + " grading events");
314:
315: toBeDeleted = hibTempl
316: .find(
317: "from AbstractGradeRecord as gr where gr.gradableObject.gradebook.id=?",
318: gradebookId);
319: numberDeleted = toBeDeleted.size();
320: hibTempl.deleteAll(toBeDeleted);
321: if (log.isDebugEnabled())
322: log.debug("Deleted " + numberDeleted + " grade records");
323:
324: toBeDeleted = hibTempl.find(
325: "from GradableObject as go where go.gradebook.id=?",
326: gradebookId);
327: numberDeleted = toBeDeleted.size();
328: hibTempl.deleteAll(toBeDeleted);
329: if (log.isDebugEnabled())
330: log.debug("Deleted " + numberDeleted + " gradable objects");
331:
332: Gradebook gradebook = (Gradebook) hibTempl.load(
333: Gradebook.class, gradebookId);
334: gradebook.setSelectedGradeMapping(null);
335:
336: toBeDeleted = hibTempl.find(
337: "from GradeMapping as gm where gm.gradebook.id=?",
338: gradebookId);
339: numberDeleted = toBeDeleted.size();
340: hibTempl.deleteAll(toBeDeleted);
341: if (log.isDebugEnabled())
342: log.debug("Deleted " + numberDeleted + " grade mappings");
343:
344: hibTempl.delete(gradebook);
345: hibTempl.flush();
346: hibTempl.clear();
347: }
348:
349: }
|