001: /**********************************************************************************
002: *
003: * $Id: BaseHibernateManager.java 21942 2007-02-27 01:31:47Z louis@media.berkeley.edu $
004: *
005: ***********************************************************************************
006: *
007: * Copyright (c) 2005, 2006 The Regents of the University of California, The MIT Corporation
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.Date;
026: import java.util.HashMap;
027: import java.util.HashSet;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Map;
031: import java.util.Set;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.hibernate.HibernateException;
036: import org.hibernate.Query;
037: import org.hibernate.Session;
038: import org.hibernate.StaleObjectStateException;
039: import org.sakaiproject.section.api.SectionAwareness;
040: import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord;
041: import org.sakaiproject.section.api.facade.Role;
042: import org.sakaiproject.service.gradebook.shared.ConflictingAssignmentNameException;
043: import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException;
044: import org.sakaiproject.service.gradebook.shared.StaleObjectModificationException;
045: import org.sakaiproject.tool.gradebook.AbstractGradeRecord;
046: import org.sakaiproject.tool.gradebook.Assignment;
047: import org.sakaiproject.tool.gradebook.AssignmentGradeRecord;
048: import org.sakaiproject.tool.gradebook.CourseGrade;
049: import org.sakaiproject.tool.gradebook.CourseGradeRecord;
050: import org.sakaiproject.tool.gradebook.GradeMapping;
051: import org.sakaiproject.tool.gradebook.Gradebook;
052: import org.sakaiproject.tool.gradebook.GradebookProperty;
053: import org.sakaiproject.tool.gradebook.facades.Authn;
054: import org.sakaiproject.tool.gradebook.facades.EventTrackingService;
055: import org.springframework.orm.hibernate3.HibernateCallback;
056: import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
057:
058: /**
059: * Provides methods which are shared between service business logic and application business
060: * logic, but not exposed to external callers.
061: */
062: public abstract class BaseHibernateManager extends HibernateDaoSupport {
063: private static final Log log = LogFactory
064: .getLog(BaseHibernateManager.class);
065:
066: // Oracle will throw a SQLException if we put more than this into a
067: // "WHERE tbl.col IN (:paramList)" query.
068: public static int MAX_NUMBER_OF_SQL_PARAMETERS_IN_LIST = 1000;
069:
070: protected SectionAwareness sectionAwareness;
071: protected Authn authn;
072: protected EventTrackingService eventTrackingService;
073:
074: // Local cache of static-between-deployment properties.
075: protected Map propertiesMap = new HashMap();
076:
077: public Gradebook getGradebook(String uid)
078: throws GradebookNotFoundException {
079: List list = getHibernateTemplate().find(
080: "from Gradebook as gb where gb.uid=?", uid);
081: if (list.size() == 1) {
082: return (Gradebook) list.get(0);
083: } else {
084: throw new GradebookNotFoundException(
085: "Could not find gradebook uid=" + uid);
086: }
087: }
088:
089: public boolean isGradebookDefined(String gradebookUid) {
090: String hql = "from Gradebook as gb where gb.uid=?";
091: return getHibernateTemplate().find(hql, gradebookUid).size() == 1;
092: }
093:
094: protected List getAssignments(Long gradebookId, Session session)
095: throws HibernateException {
096: List assignments = session
097: .createQuery(
098: "from Assignment as asn where asn.gradebook.id=? and asn.removed=false")
099: .setLong(0, gradebookId.longValue()).list();
100: return assignments;
101: }
102:
103: protected List getCountedStudentGradeRecords(Long gradebookId,
104: String studentId, Session session)
105: throws HibernateException {
106: return session
107: .createQuery(
108: "select agr from AssignmentGradeRecord as agr, Assignment as asn where agr.studentId=? and agr.gradableObject=asn and asn.removed=false and asn.notCounted=false and asn.gradebook.id=?")
109: .setString(0, studentId).setLong(1,
110: gradebookId.longValue()).list();
111: }
112:
113: /**
114: */
115: public CourseGrade getCourseGrade(Long gradebookId) {
116: return (CourseGrade) getHibernateTemplate().find(
117: "from CourseGrade as cg where cg.gradebook.id=?",
118: gradebookId).get(0);
119: }
120:
121: /**
122: * Gets the course grade record for a student, or null if it does not yet exist.
123: *
124: * @param studentId The student ID
125: * @param session The hibernate session
126: * @return A List of grade records
127: *
128: * @throws HibernateException
129: */
130: protected CourseGradeRecord getCourseGradeRecord(
131: Gradebook gradebook, String studentId, Session session)
132: throws HibernateException {
133: return (CourseGradeRecord) session
134: .createQuery(
135: "from CourseGradeRecord as cgr where cgr.studentId=? and cgr.gradableObject.gradebook=?")
136: .setString(0, studentId).setEntity(1, gradebook)
137: .uniqueResult();
138: }
139:
140: public String getGradebookUid(Long id) {
141: return ((Gradebook) getHibernateTemplate().load(
142: Gradebook.class, id)).getUid();
143: }
144:
145: protected Set getAllStudentUids(String gradebookUid) {
146: List enrollments = getSectionAwareness().getSiteMembersInRole(
147: gradebookUid, Role.STUDENT);
148: Set studentUids = new HashSet();
149: for (Iterator iter = enrollments.iterator(); iter.hasNext();) {
150: studentUids.add(((EnrollmentRecord) iter.next()).getUser()
151: .getUserUid());
152: }
153: return studentUids;
154: }
155:
156: protected Map getPropertiesMap() {
157:
158: return propertiesMap;
159: }
160:
161: public String getPropertyValue(final String name) {
162: String value = (String) propertiesMap.get(name);
163: if (value == null) {
164: List list = getHibernateTemplate().find(
165: "from GradebookProperty as prop where prop.name=?",
166: name);
167: if (!list.isEmpty()) {
168: GradebookProperty property = (GradebookProperty) list
169: .get(0);
170: value = property.getValue();
171: propertiesMap.put(name, value);
172: }
173: }
174: return value;
175: }
176:
177: public void setPropertyValue(final String name, final String value) {
178: GradebookProperty property;
179: List list = getHibernateTemplate().find(
180: "from GradebookProperty as prop where prop.name=?",
181: name);
182: if (!list.isEmpty()) {
183: property = (GradebookProperty) list.get(0);
184: } else {
185: property = new GradebookProperty(name);
186: }
187: property.setValue(value);
188: getHibernateTemplate().saveOrUpdate(property);
189: propertiesMap.put(name, value);
190: }
191:
192: /**
193: * Oracle has a low limit on the maximum length of a parameter list
194: * in SQL queries of the form "WHERE tbl.col IN (:paramList)".
195: * Since enrollment lists can sometimes be very long, we've replaced
196: * such queries with full selects followed by filtering. This helper
197: * method filters out unwanted grade records. (Typically they're not
198: * wanted because they're either no longer officially enrolled in the
199: * course or they're not members of the selected section.)
200: */
201: protected List filterGradeRecordsByStudents(
202: Collection gradeRecords, Collection studentUids) {
203: List filteredRecords = new ArrayList();
204: for (Iterator iter = gradeRecords.iterator(); iter.hasNext();) {
205: AbstractGradeRecord agr = (AbstractGradeRecord) iter.next();
206: if (studentUids.contains(agr.getStudentId())) {
207: filteredRecords.add(agr);
208: }
209: }
210: return filteredRecords;
211: }
212:
213: protected Assignment getAssignmentWithoutStats(String gradebookUid,
214: String assignmentName, Session session)
215: throws HibernateException {
216: return (Assignment) session
217: .createQuery(
218: "from Assignment as asn where asn.name=? and asn.gradebook.uid=? and asn.removed=false")
219: .setString(0, assignmentName)
220: .setString(1, gradebookUid).uniqueResult();
221: }
222:
223: protected void updateAssignment(Assignment assignment,
224: Session session) throws ConflictingAssignmentNameException,
225: HibernateException {
226: // Ensure that we don't have the assignment in the session, since
227: // we need to compare the existing one in the db to our edited assignment
228: session.evict(assignment);
229:
230: Assignment asnFromDb = (Assignment) session.load(
231: Assignment.class, assignment.getId());
232: int numNameConflicts = ((Integer) session
233: .createQuery(
234: "select count(go) from GradableObject as go where go.name = ? and go.gradebook = ? and go.removed=false and go.id != ?")
235: .setString(0, assignment.getName()).setEntity(1,
236: assignment.getGradebook()).setLong(2,
237: assignment.getId().longValue()).uniqueResult())
238: .intValue();
239: if (numNameConflicts > 0) {
240: throw new ConflictingAssignmentNameException(
241: "You can not save multiple assignments in a gradebook with the same name");
242: }
243:
244: session.evict(asnFromDb);
245: session.update(assignment);
246: }
247:
248: protected AssignmentGradeRecord getAssignmentGradeRecord(
249: Assignment assignment, String studentUid, Session session)
250: throws HibernateException {
251: return (AssignmentGradeRecord) session
252: .createQuery(
253: "from AssignmentGradeRecord as agr where agr.studentId=? and agr.gradableObject.id=?")
254: .setString(0, studentUid).setLong(1,
255: assignment.getId().longValue()).uniqueResult();
256: }
257:
258: public Long createAssignment(final Long gradebookId,
259: final String name, final Double points, final Date dueDate,
260: final Boolean isNotCounted, final Boolean isReleased)
261: throws ConflictingAssignmentNameException,
262: StaleObjectModificationException {
263: HibernateCallback hc = new HibernateCallback() {
264: public Object doInHibernate(Session session)
265: throws HibernateException {
266: Gradebook gb = (Gradebook) session.load(
267: Gradebook.class, gradebookId);
268: int numNameConflicts = ((Integer) session
269: .createQuery(
270: "select count(go) from GradableObject as go where go.name = ? and go.gradebook = ? and go.removed=false")
271: .setString(0, name).setEntity(1, gb)
272: .uniqueResult()).intValue();
273: if (numNameConflicts > 0) {
274: throw new ConflictingAssignmentNameException(
275: "You can not save multiple assignments in a gradebook with the same name");
276: }
277:
278: Assignment asn = new Assignment();
279: asn.setGradebook(gb);
280: asn.setName(name);
281: asn.setPointsPossible(points);
282: asn.setDueDate(dueDate);
283: if (isNotCounted != null) {
284: asn.setNotCounted(isNotCounted.booleanValue());
285: }
286:
287: if (isReleased != null) {
288: asn.setReleased(isReleased.booleanValue());
289: }
290:
291: // Save the new assignment
292: Long id = (Long) session.save(asn);
293:
294: return id;
295: }
296: };
297:
298: return (Long) getHibernateTemplate().execute(hc);
299: }
300:
301: public void updateGradebook(final Gradebook gradebook)
302: throws StaleObjectModificationException {
303: HibernateCallback hc = new HibernateCallback() {
304: public Object doInHibernate(Session session)
305: throws HibernateException {
306: // Get the gradebook and selected mapping from persistence
307: Gradebook gradebookFromPersistence = (Gradebook) session
308: .load(gradebook.getClass(), gradebook.getId());
309: GradeMapping mappingFromPersistence = gradebookFromPersistence
310: .getSelectedGradeMapping();
311:
312: // If the mapping has changed, and there are explicitly entered
313: // course grade records, disallow this update.
314: if (!mappingFromPersistence.getId().equals(
315: gradebook.getSelectedGradeMapping().getId())) {
316: if (isExplicitlyEnteredCourseGradeRecords(gradebook
317: .getId())) {
318: throw new IllegalStateException(
319: "Selected grade mapping can not be changed, since explicit course grades exist.");
320: }
321: }
322:
323: // Evict the persisted objects from the session and update the gradebook
324: // so the new grade mapping is used in the sort column update
325: //session.evict(mappingFromPersistence);
326: for (Iterator iter = gradebookFromPersistence
327: .getGradeMappings().iterator(); iter.hasNext();) {
328: session.evict(iter.next());
329: }
330: session.evict(gradebookFromPersistence);
331: try {
332: session.update(gradebook);
333: session.flush();
334: } catch (StaleObjectStateException e) {
335: throw new StaleObjectModificationException(e);
336: }
337:
338: return null;
339: }
340: };
341: getHibernateTemplate().execute(hc);
342: }
343:
344: public boolean isExplicitlyEnteredCourseGradeRecords(
345: final Long gradebookId) {
346: final Set studentUids = getAllStudentUids(getGradebookUid(gradebookId));
347: if (studentUids.isEmpty()) {
348: return false;
349: }
350:
351: HibernateCallback hc = new HibernateCallback() {
352: public Object doInHibernate(Session session)
353: throws HibernateException {
354: Integer total;
355: if (studentUids.size() <= MAX_NUMBER_OF_SQL_PARAMETERS_IN_LIST) {
356: Query q = session
357: .createQuery("select count(cgr) from CourseGradeRecord as cgr where cgr.enteredGrade is not null and cgr.gradableObject.gradebook.id=:gradebookId and cgr.studentId in (:studentUids)");
358: q.setLong("gradebookId", gradebookId.longValue());
359: q.setParameterList("studentUids", studentUids);
360: total = (Integer) q.list().get(0);
361: if (log.isInfoEnabled())
362: log
363: .info("total number of explicitly entered course grade records = "
364: + total);
365: } else {
366: total = new Integer(0);
367: Query q = session
368: .createQuery("select cgr.studentId from CourseGradeRecord as cgr where cgr.enteredGrade is not null and cgr.gradableObject.gradebook.id=:gradebookId");
369: q.setLong("gradebookId", gradebookId.longValue());
370: for (Iterator iter = q.list().iterator(); iter
371: .hasNext();) {
372: String studentId = (String) iter.next();
373: if (studentUids.contains(studentId)) {
374: total = new Integer(1);
375: break;
376: }
377: }
378: }
379: return total;
380: }
381: };
382: return ((Integer) getHibernateTemplate().execute(hc))
383: .intValue() > 0;
384: }
385:
386: public Authn getAuthn() {
387: return authn;
388: }
389:
390: public void setAuthn(Authn authn) {
391: this .authn = authn;
392: }
393:
394: protected String getUserUid() {
395: return authn.getUserUid();
396: }
397:
398: protected SectionAwareness getSectionAwareness() {
399: return sectionAwareness;
400: }
401:
402: public void setSectionAwareness(SectionAwareness sectionAwareness) {
403: this .sectionAwareness = sectionAwareness;
404: }
405:
406: protected EventTrackingService getEventTrackingService() {
407: return eventTrackingService;
408: }
409:
410: public void setEventTrackingService(
411: EventTrackingService eventTrackingService) {
412: this .eventTrackingService = eventTrackingService;
413: }
414:
415: public void postEvent(String message, String objectReference) {
416: eventTrackingService.postEvent(message, objectReference);
417: }
418:
419: }
|