/**
* Copyright 2013 Apereo Foundation 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://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.gradebook.logic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.authz.api.AuthzGroupService;
import org.sakaiproject.authz.api.Member;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.gradebook.entity.Course;
import org.sakaiproject.gradebook.entity.Gradebook;
import org.sakaiproject.gradebook.entity.GradebookItem;
import org.sakaiproject.gradebook.entity.GradebookItemScore;
import org.sakaiproject.gradebook.entity.Student;
import org.sakaiproject.javax.PagingPosition;
import org.sakaiproject.service.gradebook.shared.Assignment;
import org.sakaiproject.service.gradebook.shared.CommentDefinition;
import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService;
import org.sakaiproject.service.gradebook.shared.GradebookService;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.site.api.SiteService.SelectionType;
import org.sakaiproject.site.api.SiteService.SortType;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.api.SessionManager;
import org.sakaiproject.tool.api.ToolManager;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService;
import org.sakaiproject.user.api.UserNotDefinedException;
import org.sakaiproject.util.ResourceLoader;
/**
* This is the common parts of the logic which is external to our app logic, this provides isolation
* of the Sakai system from the app so that the integration can be adjusted for future versions or
* even other systems without requiring rewriting large parts of the code
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public class ExternalLogic {
public final static String EXTERNAL_DATASOURCE = "GB_REST";
public final static boolean EXTERNAL_ONLY = true;
public String serverId = "UNKNOWN_SERVER_ID";
public final static String NO_LOCATION = "noLocationAvailable";
private final static Log log = LogFactory.getLog(ExternalLogic.class);
protected AuthzGroupService authzGroupService;
public void setAuthzGroupService(AuthzGroupService authzGroupService) {
this.authzGroupService = authzGroupService;
}
private GradebookService gradebookService;
public void setGradebookService(GradebookService gradebookService) {
this.gradebookService = gradebookService;
}
protected GradebookExternalAssessmentService gradebookExternalAssessmentService;
public void setGradebookExternalAssessmentService(GradebookExternalAssessmentService gradebookExternalAssessmentService) {
this.gradebookExternalAssessmentService = gradebookExternalAssessmentService;
}
private ToolManager toolManager;
public void setToolManager(ToolManager toolManager) {
this.toolManager = toolManager;
}
private SecurityService securityService;
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
private ServerConfigurationService serverConfigurationService;
public void setServerConfigurationService(ServerConfigurationService serverConfigurationService) {
this.serverConfigurationService = serverConfigurationService;
}
private SessionManager sessionManager;
public void setSessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
private SiteService siteService;
public void setSiteService(SiteService siteService) {
this.siteService = siteService;
}
private UserDirectoryService userDirectoryService;
public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
this.userDirectoryService = userDirectoryService;
}
public void init() {
log.info("INIT");
}
/**
* @return the current location id of the current user
*/
public String getCurrentLocationId() {
String location = null;
try {
String context = toolManager.getCurrentPlacement().getContext();
location = context;
// Site s = siteService.getSite( context );
// location = s.getReference(); // get the entity reference to the site
} catch (Exception e) {
// sakai failed to get us a location so we can assume we are not inside the portal
return NO_LOCATION;
}
if (location == null) {
location = NO_LOCATION;
}
return location;
}
/**
* @param locationId
* a unique id which represents the current location of the user (entity reference)
* @return the title for the context or "--------" (8 hyphens) if none found
*/
public String getLocationTitle(String locationId) {
String title = null;
try {
Site site = siteService.getSite(locationId);
title = site.getTitle();
} catch (IdUnusedException e) {
log.warn("Cannot get the info about locationId: " + locationId);
title = "----------";
}
return title;
}
/**
* Get the user id from the user login name
* @param loginname the eid for the user
* @return the id IF the user exists OR null if they do not
*/
public String getUserIdFromLoginName(String loginname) {
String userId;
try {
User u = userDirectoryService.getUserByEid(loginname);
userId = u.getId();
} catch (UserNotDefinedException e) {
userId = null;
}
return userId;
}
/**
* Validate the session id given and optionally make it the current one
* @param sessionId a sakai session id
* @param makeCurrent if true and the session id is valid then it is made the current one
* @return true if the session id is valid OR false if not
*/
public boolean validateSessionId(String sessionId, boolean makeCurrent) {
try {
// this also protects us from null pointer where session service is not set or working
Session s = sessionManager.getSession(sessionId);
if (s != null && s.getUserId() != null) {
if (makeCurrent) {
s.setActive();
sessionManager.setCurrentSession(s);
authzGroupService.refreshUser(s.getUserId());
}
} else {
return false;
}
} catch (Exception e) {
throw new IllegalArgumentException("Failure attempting to set sakai session id ("+sessionId+"): " + e.getMessage());
}
return true;
}
/**
* @return the current sakai user session id OR null if none
*/
public String getCurrentSessionId() {
String sessionId = null;
Session s = sessionManager.getCurrentSession();
if (s != null) {
sessionId = s.getId();
}
return sessionId;
}
/**
* @return the current sakai user id (not username)
*/
public String getCurrentUserId() {
return sessionManager.getCurrentSessionUserId();
}
/**
* @return the current Locale as Sakai understands it
*/
public Locale getCurrentLocale() {
return new ResourceLoader().getLocale();
}
/**
* Get the display name for a user by their unique id
*
* @param userId
* the current sakai user id (not username)
* @return display name (probably firstname lastname) or "----------" (10 hyphens) if none found
*/
public String getUserDisplayName(String userId) {
String name = null;
try {
name = userDirectoryService.getUser(userId).getDisplayName();
} catch (UserNotDefinedException e) {
log.warn("Cannot get user displayname for id: " + userId);
name = "--------";
}
return name;
}
/**
* Get a user by their unique id
* @param userId user id
* @return the populated User or null if none found
*/
public org.sakaiproject.gradebook.entity.User getUser(String userId) {
org.sakaiproject.gradebook.entity.User user = null;
User u = null;
try {
u = userDirectoryService.getUser(userId);
} catch (UserNotDefinedException e) {
try {
u = userDirectoryService.getUserByEid(userId);
} catch (UserNotDefinedException e1) {
log.warn("Cannot get user for id: " + userId);
}
}
if (u != null) {
user = new org.sakaiproject.gradebook.entity.User(u.getId(),
u.getEid(), u.getDisplayName(), u.getSortName(), u.getEmail());
user.fname = u.getFirstName();
user.lname = u.getLastName();
}
return user;
}
/**
* @return the system email address or null if none available
*/
public String getNotificationEmail() {
// attempt to get the email address, if it is not there then we will not send an email
String emailAddr = serverConfigurationService.getString("portal.error.email",
serverConfigurationService.getString("mail.support") );
if ("".equals(emailAddr)) {
emailAddr = null;
}
return emailAddr;
}
/**
* Check if this user has super admin access
*
* @param userId
* the internal user id (not username)
* @return true if the user has admin access, false otherwise
*/
public boolean isUserAdmin(String userId) {
return securityService.isSuperUser(userId);
}
/**
* Check if a user has a specified permission within a context, primarily a convenience method
* and passthrough
*
* @param userId
* the internal user id (not username)
* @param permission
* a permission string constant
* @param locationId
* a unique id which represents the current location of the user (entity reference)
* @return true if allowed, false otherwise
*/
public boolean isUserAllowedInLocation(String userId, String permission, String locationId) {
if (securityService.unlock(userId, permission, locationId)) {
return true;
}
return false;
}
/**
* Get all the courses for the current user, note that this needs to be limited from
* outside this method for security
*
* @param siteId
* [OPTIONAL] limit the return to just this one site
* @return the sites (up to 100 of them) which the user has instructor access in
*/
public List<Course> getCoursesForInstructor(String siteId) {
List<Course> courses = new Vector<Course>();
if (siteId == null || "".equals(siteId)) {
List<Site> sites = getInstructorSites();
for (Site site : sites) {
courses.add( makeCourseFromSite(site) );
}
} else {
// return a single site and enrollments
if (siteService.siteExists(siteId)) {
if (siteService.allowUpdateSite(siteId) || siteService.allowViewRoster(siteId)) {
Site site;
try {
site = siteService.getSite(siteId);
Course c = makeCourseFromSite(site);
courses.add(c);
} catch (IdUnusedException e) {
site = null;
}
}
}
}
return courses;
}
@SuppressWarnings("deprecation")
private Course makeCourseFromSite(Site site) {
long createdTime = System.currentTimeMillis() / 1000;
if (site.getCreatedTime() != null) {
createdTime = site.getCreatedTime().getTime() / 1000;
}
Course c = new Course(site.getId(),
site.getTitle(),
site.getShortDescription(),
createdTime,
site.isPublished() );
return c;
}
private List<Site> getInstructorSites() {
// return a max of 100 sites
List<Site> instSites = new ArrayList<Site>();
List<Site> sites = siteService.getSites(SelectionType.UPDATE, null, null, null,
SortType.TITLE_ASC, new PagingPosition(1, 100));
for (Site site : sites) {
// filter out admin sites
String sid = site.getId();
if (sid.startsWith("!") || sid.endsWith("Admin") || sid.equals("mercury")) {
if (log.isDebugEnabled()) {
log.debug("Skipping site (" + sid + ") for current user in instructor courses");
}
continue;
}
instSites.add(site);
}
return instSites;
}
/**
* Get the listing of students from the site gradebook, uses GB security so safe to call
*
* @param siteId
* the id of the site to get students from
* @return the list of Students
*/
public List<Student> getStudentsForCourse(String siteId) {
List<Student> students = new ArrayList<Student>();
Site site;
try {
site = siteService.getSite(siteId);
} catch (IdUnusedException e1) {
throw new IllegalArgumentException("No course found with id (" + siteId + ")");
}
String siteRef = site.getReference();
// use the method gradebook uses internally
List<User> studentUsers = securityService.unlockUsers("section.role.student", siteRef);
for (User user : studentUsers) {
Student s = new Student(user.getId(), user.getEid(), user.getDisplayName(), user.getSortName(), user.getEmail());
s.fname = user.getFirstName();
s.lname = user.getLastName();
students.add(s);
}
/*** this only works in the post-2.5 gradebook -AZ
// Let the gradebook tell use how it defines the students The gradebookUID is the siteId
String gbID = siteId;
if (!gradebookService.isGradebookDefined(gbID)) {
throw new IllegalArgumentException("No gradebook found for course (" + siteId
+ "), gradebook must be installed in each course to use with this");
}
Map<String, String> studentToPoints = gradebookService.getFixedPoint(gbID);
ArrayList<String> eids = new ArrayList<String>(studentToPoints.keySet());
Collections.sort(eids);
for (String eid : eids) {
try {
User user = userDirectoryService.getUserByEid(eid);
students.add(new Student(user.getId(), user.getEid(), user.getDisplayName()));
} catch (UserNotDefinedException e) {
log.warn("Undefined user eid (" + eid + ") from site/gb (" + siteId + "): " + e);
}
}
***/
return students;
}
/**
* @param userId
* the current sakai user id (not username)
* @return true if the user has update access in any sites
*/
public boolean isUserInstructor(String userId) {
boolean inst = false;
// admin never counts as in instructor
if (!isUserAdmin(userId)) {
int count = siteService.countSites(SelectionType.UPDATE, null, null, null);
inst = (count > 0);
}
return inst;
}
/**
* Check if the current user in an instructor for the given user id,
* this will return the first course found in alpha order,
* will only check the first 100 courses
*
* @param studentUserId the Sakai user id for the student
* @return the course ID of the course they are an instructor for the student OR null if they are not
*/
public String isInstructorOfUser(String studentUserId) {
if (studentUserId == null || "".equals(studentUserId)) {
throw new IllegalArgumentException("studentUserId must be set");
}
String courseId = null;
List<Site> sites = getInstructorSites();
if (sites != null && ! sites.isEmpty()) {
if (sites.size() >= 99) {
// if instructor of 99 or more sites then auto-approved
courseId = sites.get(0).getId();
} else {
for (Site site : sites) {
Member member = site.getMember(studentUserId);
if (member != null) {
courseId = site.getId();
break;
}
}
}
}
return courseId;
}
/**
* Gets the gradebook data for a given site,
* this uses the gradebook security so it is secure
*
* @param siteId a sakai siteId (cannot be group Id)
* @param gbItemName [OPTIONAL] an item name to fetch from this gradebook (limit to this item only),
* if null then all items are returned
* @throws IllegalArgumentException if no gradebook can be found
*/
@SuppressWarnings("unchecked")
public Gradebook getCourseGradebook(String siteId, String gbItemName) {
// The gradebookUID is the siteId, the gradebookID is a long
String gbID = siteId;
if (!gradebookService.isGradebookDefined(gbID)) {
throw new IllegalArgumentException("No gradebook found for site: " + siteId);
}
// verify permissions
String userId = getCurrentUserId();
if (userId == null
|| ! siteService.allowUpdateSite(siteId)
|| ! siteService.allowViewRoster(siteId) ) {
throw new SecurityException("User ("+userId+") cannot access gradebook in site ("+siteId+")");
}
Gradebook gb = new Gradebook(gbID);
gb.students = getStudentsForCourse(siteId);
Map<String, String> studentUserIds = new ConcurrentHashMap<String, String>();
for (Student student : gb.students) {
studentUserIds.put(student.userId, student.username);
}
ArrayList<String> studentIds = new ArrayList<String>(studentUserIds.keySet());
if (gbItemName == null) {
List<Assignment> gbitems = gradebookService.getAssignments(gbID);
for (Assignment assignment : gbitems) {
GradebookItem gbItem = makeGradebookItemFromAssignment(gbID, assignment, studentUserIds, studentIds);
gb.items.add(gbItem);
}
} else {
Assignment assignment = gradebookService.getAssignment(gbID, gbItemName);
if (assignment != null) {
GradebookItem gbItem = makeGradebookItemFromAssignment(gbID, assignment, studentUserIds, studentIds);
gb.items.add(gbItem);
} else {
throw new IllegalArgumentException("Invalid gradebook item name ("+gbItemName+"), no item with this name found in cource ("+siteId+")");
}
}
return gb;
}
private GradebookItem makeGradebookItemFromAssignment(String gbID, Assignment assignment,
Map<String, String> studentUserIds, ArrayList<String> studentIds) {
// build up the items listing
GradebookItem gbItem = new GradebookItem(gbID, assignment.getName(), assignment
.getPoints(), assignment.getDueDate(), assignment.getExternalAppName(),
assignment.isReleased());
gbItem.id = assignment.getId();
/*
* We have to iterate through each student and get the grades out...
* 2.6 gradebook service has some issues
*/
for (String studentId : studentIds) {
// too expensive: if (gradebookService.getGradeViewFunctionForUserForStudentForItem(gbID, assignment.getId(), studentId) == null) {
String grade = gradebookService.getAssignmentScoreString(gbID, assignment.getName(), studentId);
if (grade != null) {
GradebookItemScore score = new GradebookItemScore(assignment.getId().toString(),
studentId, grade );
score.username = studentUserIds.get(studentId);
CommentDefinition cd = gradebookService.getAssignmentScoreComment(gbID, assignment
.getName(), studentId);
if (cd != null) {
score.comment = cd.getCommentText();
score.recorded = cd.getDateRecorded();
score.graderUserId = cd.getGraderUid();
}
gbItem.scores.add(score);
}
}
/* This is another post 2.5 way
List<GradeDefinition> grades = gradebookService.getGradesForStudentsForItem(siteId,
assignment.getId(), studentIds);
for (GradeDefinition gd : grades) {
String studId = gd.getStudentUid();
String studEID = studentUserIds.get(studId);
GradebookItemScore score = new GradebookItemScore(assignment.getId().toString(),
studId, gd.getGrade(), studEID, gd.getGraderUid(), gd.getDateRecorded(),
gd.getGradeComment());
gbItem.scores.add(score);
}
**/
return gbItem;
}
/**
* Save a gradebook item and optionally the scores within <br/>
* Scores must have at least the studentId or username AND the grade set
*
* @param gbItem
* the gradebook item to save, must have at least the gradebookId and name set
* @return the updated gradebook item and scores, contains any errors that occurred
* @throws IllegalArgumentException if the assignment is invalid and cannot be saved
* @throws SecurityException if the current user does not have permissions to save
*/
public GradebookItem saveGradebookItem(GradebookItem gbItem) {
if (log.isDebugEnabled()) log.debug("saveGradebookItem("+gbItem+")");
if (gbItem == null) {
throw new IllegalArgumentException("gbItem cannot be null");
}
if (gbItem.gradebookId == null || "".equals(gbItem.gradebookId)) {
throw new IllegalArgumentException("gbItem must have the gradebookId set");
}
if (gbItem.name == null || "".equals(gbItem.name)) {
throw new IllegalArgumentException("gbItem must have the name set");
}
String gradebookId = gbItem.gradebookId;
boolean isExternal = false;
// try to find by eid first
Assignment assignment = findAssignmentByExternalId(gradebookId, gbItem.eid);
if (assignment != null) {
if (log.isDebugEnabled()) log.debug("find external("+assignment+"), eid="+gbItem.eid);
gbItem.id = assignment.getId();
isExternal = true;
}
if (!EXTERNAL_ONLY) {
// in the GB service we can only lookup by name or internal id
if (assignment == null) {
// try to get by internal id
if (gbItem.id != null) {
assignment = gradebookService.getAssignment(gradebookId, gbItem.id);
}
if (assignment == null) {
// try to find by name
if (gradebookService.isAssignmentDefined(gradebookId, gbItem.name)) {
assignment = gradebookService.getAssignment(gradebookId, gbItem.name);
if (log.isDebugEnabled()) log.debug("found internal assign by name ("+gbItem.name+"): "+assignment);
}
} else {
if (log.isDebugEnabled()) log.debug("found internal assign by id ("+gbItem.id+"): "+assignment);
}
}
}
if (log.isDebugEnabled()) log.debug("found assignment ("+assignment+") from gb item: "+gbItem);
// now we have the item if it exists
try {
// try to save or update it
if (assignment == null) {
// no item so create one
gbItem.name = makeSafeAssignmentName(gradebookId, gbItem.name);
if (EXTERNAL_ONLY) {
gradebookExternalAssessmentService.addExternalAssessment(gradebookId, gbItem.eid,
null, gbItem.name, gbItem.pointsPossible, gbItem.dueDate, EXTERNAL_DATASOURCE, false);
// GradebookNotFoundException, ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException
// fetch the newly created assignment object so we can get the id from it
isExternal = true;
assignment = gradebookService.getAssignment(gradebookId, gbItem.name);
if (assignment != null) {
if (log.isDebugEnabled()) log.debug("found new external assignment ("+assignment+") from name: "+gbItem.name);
} else {
assignment = findAssignmentByExternalId(gradebookId, gbItem.eid);
if (assignment != null) {
if (log.isDebugEnabled()) log.debug("found new external assignment ("+assignment+") from eid: "+gbItem.eid);
} else {
log.warn("FATAL error finding new external assignment from item: "+gbItem);
throw new IllegalArgumentException("Unable to find the newly saved item: "+gbItem);
}
}
if (log.isDebugEnabled()) log.debug("created external assignment ("+assignment+") from item: "+gbItem);
} else {
// create internal assignment
assignment = new Assignment();
/* setting External fields has no effect
assignment.setExternallyMaintained(false); // cannot modify it later if true
if (gbItem.eid != null) {
assignment.setExternalId(gbItem.eid);
}
assignment.setExternalAppName(gbItem.type);
*/
// assign values
assignment.setDueDate(gbItem.dueDate);
assignment.setName(gbItem.name);
assignment.setPoints(gbItem.pointsPossible);
assignment.setReleased(gbItem.released);
gradebookService.addAssignment(gradebookId, assignment);
// SecurityException, AssignmentHasIllegalPointsException, RuntimeException
if (log.isDebugEnabled()) log.debug("created internal assignment ("+assignment+") from item: "+gbItem);
}
gbItem.id = assignment.getId();
} else {
if (gbItem.dueDate != null) {
assignment.setDueDate(gbItem.dueDate);
}
if (gbItem.pointsPossible != null && gbItem.pointsPossible >= 0d) {
assignment.setPoints(gbItem.pointsPossible);
}
String originalName = assignment.getName();
if (gbItem.name != null && "".equals(gbItem.name)
&& !gbItem.name.equals(assignment.getName()) ) {
assignment.setName( makeSafeAssignmentName(gradebookId, gbItem.name) );
}
if (isExternal) {
gradebookExternalAssessmentService.updateExternalAssessment(gradebookId, gbItem.eid,
null, assignment.getName(), assignment.getPoints(), assignment.getDueDate(), false);
if (log.isDebugEnabled()) log.debug("updated external assignment ("+assignment+") from item: "+gbItem);
} else {
// assign new values to existing assignment
/* setting External fields has no effect
if (gbItem.type != null) {
assignment.setExternalAppName(gbItem.type);
//assignment.setExternalId(gbItem.type);
}
*/
// assignment.setReleased(gbItem.released); // no mod released setting from here
gradebookService.updateAssignment(gradebookId, originalName, assignment);
// SecurityException, RuntimeException
if (log.isDebugEnabled()) log.debug("updated internal assignment ("+assignment+") from item: "+gbItem);
}
}
} catch (SecurityException e) {
log.warn("security failure (gb="+gradebookId+", item="+gbItem+", asgn="+assignment+"): cannot create: " + e, e);
throw e; // rethrow
} catch (RuntimeException e) {
String msg = "Invalid assignment (gb="+gradebookId+", item="+gbItem+", asgn="+assignment+"): cannot create: " + e;
log.warn(msg, e);
throw new IllegalArgumentException(msg, e);
}
int errorsCount = 0;
if (gbItem.scores != null && !gbItem.scores.isEmpty()) {
// now update scores if there are any to update,
// this will not remove scores and will only add new ones
for (GradebookItemScore score : gbItem.scores) {
if (isBlank(score.username) && isBlank(score.userId)) {
score.error = "USER_INVALID: User ID and Name are both blank";
continue;
}
// find out if the student id is internal or external, and set the score vals and then studentId to the internal id
String studentId = score.userId;
if (isBlank(score.userId)) {
studentId = score.username;
}
try {
// try assuming it is the internal id first
score.username = userDirectoryService.getUserEid(studentId);
score.userId = studentId;
} catch (UserNotDefinedException e) {
try {
// now try it as the external id
score.userId = userDirectoryService.getUserId(studentId);
score.username = studentId;
studentId = score.userId;
} catch (UserNotDefinedException ex) {
score.error = "USER_NOT_EXISTS: User (id="+score.userId+", name="+score.username+") could not be found";
errorsCount++;
continue;
}
}
score.assignId(gbItem.id, studentId);
// null/blank scores are not allowed
if (isBlank(score.grade)) {
score.error = "NO_SCORE_ERROR: score is blank";
errorsCount++;
continue;
}
Double dScore;
try {
dScore = Double.valueOf(score.grade);
} catch (NumberFormatException e) {
score.error = "SCORE_INVALID: Score ("+score.grade+") is invalid";
errorsCount++;
continue;
}
// Student Score should not be greater than the total points possible
if (dScore > assignment.getPoints()) {
score.error = "POINTS: Score ("+dScore+") > PointsPossible ("+assignment.getPoints()+")";
errorsCount++;
continue;
}
try {
/** check against existing score ** NOT NEEDED
String currentScore = gradebookService.getAssignmentScoreString(gradebookUid, gbItem.name, studentId);
if (currentScore != null) {
try {
Double dCurrentScore = Double.valueOf(currentScore);
if (dScore < dCurrentScore) {
score.error = SCORE_UPDATE_ERRORS;
errorsCount++;
continue;
}
} catch (NumberFormatException e) {
// no comparison since this is not a numerical score
}
}
**/
// null grade deletes the score
if (isExternal) {
gradebookExternalAssessmentService.updateExternalAssessmentScore(gradebookId, gbItem.eid, studentId, dScore.toString());
} else {
gradebookService.setAssignmentScoreString(gradebookId, gbItem.name, studentId, dScore.toString(), EXTERNAL_DATASOURCE);
}
if (score.comment != null && ! "".equals(score.comment)) {
gradebookService.setAssignmentScoreComment(gradebookId, gbItem.name, studentId, score.comment);
}
} catch (Exception e) {
// General errors, caused while performing updates (Tag: generalerrors)
String msg = "Failure saving score ("+score+"): "+e;
log.warn(msg, e);
score.error = "GENERAL: "+msg;
errorsCount++;
}
/* post-2.5 gradebook method
try {
gradebookService.saveGradeAndCommentForStudent(gradebookUid, gbItemId,
studentId, score.grade, score.comment);
} catch (InvalidGradeException e) {
scoreErrors.put(score, "SCORE_INVALID");
continue;
}
GradeDefinition gd = gradebookService.getGradeDefinitionForStudentForItem(
gradebookUid, gbItemId, studentId);
score.assignId(gbItem.id, gd.getStudentUid());
score.comment = gd.getGradeComment();
score.grade = gd.getGrade();
score.graderUserId = gd.getGraderUid();
score.recorded = gd.getDateRecorded();
*/
}
// put the errors in the item if there are errors and they are set
if (errorsCount > 0) {
gbItem.scoreErrors = new HashMap<String, String>();
for (GradebookItemScore score : gbItem.scores) {
if (score.error != null) {
gbItem.scoreErrors.put(score.id, score.error);
}
}
}
}
return gbItem;
}
/**
* Check if a name exists already and if so, adjusts it so it is now safe without doing too many checks
* @param gradebookId the gradebook unique id (can also be the siteId)
* @param name the current assignment name
* @return a name which is safe (may be the original if unused)
*/
protected String makeSafeAssignmentName(String gradebookId, String name) {
String safeName = name;
if (gradebookService.isAssignmentDefined(gradebookId, name)) {
// try to find a safe one
safeName = name + "-1";
if (gradebookService.isAssignmentDefined(gradebookId, name)) {
// now make a name which we know will be safe
safeName = StringUtils.abbreviate(name, 200) + "-" + System.currentTimeMillis();
}
}
return safeName;
}
/**
* Find an internal assignment (with internal id) for an assignment in a gradebook by the external id if it exists
*
* @param gradebookId the gradebook unique id (can also be the siteId)
* @param externalId the external id for the assignment (must be externally controlled)
* @return the Assignment OR null if not found
*/
protected Assignment findAssignmentByExternalId(String gradebookId, String externalId) {
Assignment assignment = null;
if (gradebookExternalAssessmentService.isExternalAssignmentDefined(gradebookId, externalId)) {
@SuppressWarnings("unchecked")
List<Assignment> assignments = gradebookService.getAssignments(gradebookId);
for (Assignment a : assignments) {
if (externalId.equals(a.getExternalId())
&& EXTERNAL_DATASOURCE.equals(a.getExternalAppName()) ) {
assignment = a;
break;
}
}
}
return assignment;
}
/**
* Remove an assignment using the external id to locate it,
* will only remove assignments which actually are externally managed by us
*
* @param gradebookId the gradebook unique id (can also be the siteId)
* @param externalId the external id for the assignment (must be externally controlled)
* @return the Assignment if found and removed OR null if not found and NOT removed
*/
public GradebookItem removeGradebookItem(String gradebookId, String externalId) {
GradebookItem removed = null;
Assignment a = findAssignmentByExternalId(gradebookId, externalId);
if (a != null) {
/* NOTES:
* (1) Removing the Assignment only does not work, adding external ones later will cause exceptions (externalId in use)
* gradebookService.removeAssignment(a.getId());
* (2) Breaking the connection only does not actually remove the Assignment
* gradebookExternalAssessmentService.setExternalAssessmentToGradebookAssignment(gradebookId, externalId);
* (3) The correct way which seems to remove the external assessment, Assignment, and scores is removeExternalAssessment
* gradebookExternalAssessmentService.removeExternalAssessment(gradebookId, externalId);
*/
gradebookExternalAssessmentService.removeExternalAssessment(gradebookId, externalId);
removed = new GradebookItem(gradebookId, a.getName(), a.getPoints(), a.getDueDate(), a.getCategoryName(), a.isReleased());
removed.id = a.getId();
removed.deleted = true;
}
return removed;
}
public boolean removeGradebookItemByName(String gradebookId, String itemName) {
boolean removed = false;
if (gradebookService.isAssignmentDefined(gradebookId, itemName)) {
Assignment a = gradebookService.getAssignment(gradebookId, itemName);
gradebookService.removeAssignment(a.getId());
}
return removed;
}
/**
* String type: gets the printable name of this server
*/
public static String SETTING_SERVER_NAME = "server.name";
/**
* String type: gets the unique id of this server (safe for clustering if used)
*/
public static String SETTING_SERVER_ID = "server.cluster.id";
/**
* String type: gets the URL to this server
*/
public static String SETTING_SERVER_URL = "server.main.URL";
/**
* String type: gets the URL to the portal on this server (or just returns the server URL if no
* portal in use)
*/
public static String SETTING_PORTAL_URL = "server.portal.URL";
/**
* Boolean type: if true then there will be data preloads and DDL creation, if false then data
* preloads are disabled (and will cause exceptions if preload data is missing)
*/
public static String SETTING_AUTO_DDL = "auto.ddl";
/**
* Retrieves settings from the configuration service (sakai.properties)
*
* @param settingName
* the name of the setting to retrieve, Should be a string name: e.g. auto.ddl,
* mystuff.config, etc. OR one of the SETTING constants (e.g
* {@link #SETTING_AUTO_DDL})
*
* @param defaultValue
* a specified default value to return if this setting cannot be found, <b>NOTE:</b>
* You can set the default value to null but you must specify the class type in
* parens
* @return the value of the configuration setting OR the default value if none can be found
*/
@SuppressWarnings("unchecked")
public <T> T getConfigurationSetting(String settingName, T defaultValue) {
T returnValue = defaultValue;
if (SETTING_SERVER_NAME.equals(settingName)) {
returnValue = (T) serverConfigurationService.getServerName();
} else if (SETTING_SERVER_URL.equals(settingName)) {
returnValue = (T) serverConfigurationService.getServerUrl();
} else if (SETTING_PORTAL_URL.equals(settingName)) {
returnValue = (T) serverConfigurationService.getPortalUrl();
} else if (SETTING_SERVER_ID.equals(settingName)) {
returnValue = (T) serverConfigurationService.getServerIdInstance();
} else {
if (defaultValue == null) {
returnValue = (T) serverConfigurationService.getString(settingName);
if ("".equals(returnValue)) {
returnValue = null;
}
} else {
if (defaultValue instanceof Number) {
int num = ((Number) defaultValue).intValue();
int value = serverConfigurationService.getInt(settingName, num);
returnValue = (T) Integer.valueOf(value);
} else if (defaultValue instanceof Boolean) {
boolean bool = ((Boolean) defaultValue).booleanValue();
boolean value = serverConfigurationService.getBoolean(settingName, bool);
returnValue = (T) Boolean.valueOf(value);
} else if (defaultValue instanceof String) {
returnValue = (T) serverConfigurationService.getString(settingName,
(String) defaultValue);
}
}
}
return returnValue;
}
public static boolean isBlank(String str) {
if (str == null || "".equals(str)) {
return true;
}
return false;
}
/**
* Set a current user for the current thread, create session if needed
* @param userId the userId to set
*/
public void setCurrentUser(String userId) {
if (userId == null) {
throw new IllegalArgumentException("userId cannot be null");
}
Session currentSession = sessionManager.getCurrentSession();
if (currentSession == null) {
// start a session if none is around
currentSession = sessionManager.startSession(userId);
}
currentSession.setUserId(userId);
currentSession.setActive();
sessionManager.setCurrentSession(currentSession);
authzGroupService.refreshUser(userId);
}
}