/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/sam/trunk/component/src/java/org/sakaiproject/tool/assessment/services/GradingService.java $ * $Id: GradingService.java 9784 2006-05-22 19:33:28Z daisyf@stanford.edu $ *********************************************************************************** * * Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009 The Sakai 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://www.osedu.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.assessment.services; import java.util.ArrayList; import java.util.Collection; import java.util.Date; 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 java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.event.cover.EventTrackingService; import org.sakaiproject.service.gradebook.shared.GradebookService; import org.sakaiproject.spring.SpringBeanLocator; import org.sakaiproject.tool.assessment.data.dao.grading.AssessmentGradingData; import org.sakaiproject.tool.assessment.data.dao.grading.ItemGradingData; import org.sakaiproject.tool.assessment.data.dao.grading.MediaData; import org.sakaiproject.tool.assessment.data.ifc.assessment.AnswerIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentAttachmentIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.EvaluationModelIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemDataIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemMetaDataIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemTextIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.PublishedAssessmentIfc; import org.sakaiproject.tool.assessment.data.ifc.grading.AssessmentGradingIfc; import org.sakaiproject.tool.assessment.data.ifc.grading.ItemGradingAttachmentIfc; import org.sakaiproject.tool.assessment.data.ifc.grading.ItemGradingIfc; import org.sakaiproject.tool.assessment.data.ifc.grading.StudentGradingSummaryIfc; import org.sakaiproject.tool.assessment.data.ifc.shared.TypeIfc; import org.sakaiproject.tool.assessment.facade.AgentFacade; import org.sakaiproject.tool.assessment.facade.AssessmentFacadeQueriesAPI; import org.sakaiproject.tool.assessment.facade.GradebookFacade; import org.sakaiproject.tool.assessment.facade.TypeFacade; import org.sakaiproject.tool.assessment.facade.TypeFacadeQueriesAPI; import org.sakaiproject.tool.assessment.integration.context.IntegrationContextFactory; import org.sakaiproject.tool.assessment.integration.helper.ifc.GradebookServiceHelper; import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService; /** * The GradingService calls the back end to get/store grading information. * It also calculates scores for autograded types. */ public class GradingService { private static Log log = LogFactory.getLog(GradingService.class); /** * Get all scores for a published assessment from the back end. */ public ArrayList getTotalScores(String publishedId, String which) { ArrayList results = null; try { results = new ArrayList(PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getTotalScores(publishedId, which)); } catch (Exception e) { e.printStackTrace(); } return results; } public ArrayList getTotalScores(String publishedId, String which, boolean getSubmittedOnly) { ArrayList results = null; try { results = new ArrayList(PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getTotalScores(publishedId, which, getSubmittedOnly)); } catch (Exception e) { e.printStackTrace(); } return results; } /** * Get all submissions for a published assessment from the back end. */ public List getAllSubmissions(String publishedId) { List results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getAllSubmissions(publishedId); } catch (Exception e) { e.printStackTrace(); } return results; } public List getAllAssessmentGradingData(Long publishedId) { List results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getAllAssessmentGradingData(publishedId); } catch (Exception e) { e.printStackTrace(); } return results; } public ArrayList getHighestAssessmentGradingList(Long publishedId) { ArrayList results = null; try { results = new ArrayList(PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getHighestAssessmentGradingList(publishedId)); } catch (Exception e) { e.printStackTrace(); } return results; } public List getHighestSubmittedOrGradedAssessmentGradingList(Long publishedId) { ArrayList results = null; try { results = new ArrayList(PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getHighestSubmittedOrGradedAssessmentGradingList(publishedId)); } catch (Exception e) { e.printStackTrace(); } return results; } public ArrayList getLastAssessmentGradingList(Long publishedId) { ArrayList results = null; try { results = new ArrayList(PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getLastAssessmentGradingList(publishedId)); } catch (Exception e) { e.printStackTrace(); } return results; } public List getLastSubmittedAssessmentGradingList(Long publishedId) { List results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getLastSubmittedAssessmentGradingList(publishedId); } catch (Exception e) { e.printStackTrace(); } return results; } public List getLastSubmittedOrGradedAssessmentGradingList(Long publishedId) { List results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getLastSubmittedOrGradedAssessmentGradingList(publishedId); } catch (Exception e) { e.printStackTrace(); } return results; } public void saveTotalScores(ArrayList gdataList, PublishedAssessmentIfc pub) { //log.debug("**** GradingService: saveTotalScores"); try { AssessmentGradingData gdata = null; if (gdataList.size()>0) gdata = (AssessmentGradingData) gdataList.get(0); else return; Integer scoringType = getScoringType(pub); ArrayList oldList = getAssessmentGradingsByScoringType( scoringType, gdata.getPublishedAssessmentId()); for (int i=0; i<gdataList.size(); i++){ AssessmentGradingData ag = (AssessmentGradingData)gdataList.get(i); saveOrUpdateAssessmentGrading(ag); EventTrackingService.post(EventTrackingService.newEvent("sam.total.score.update", "gradedBy=" + AgentFacade.getAgentString() + ", assessmentGradingId=" + ag.getAssessmentGradingId() + ", totalAutoScore=" + ag.getTotalAutoScore() + ", totalOverrideScore=" + ag.getTotalOverrideScore() + ", FinalScore=" + ag.getFinalScore() + ", comments=" + ag.getComments() , true)); } // no need to notify gradebook if this submission is not for grade // we only want to notify GB when there are changes ArrayList newList = getAssessmentGradingsByScoringType( scoringType, gdata.getPublishedAssessmentId()); ArrayList l = getListForGradebookNotification(newList, oldList); notifyGradebook(l, pub); } catch (GradebookServiceException ge) { log.error("GradebookServiceException" + ge); throw ge; } } private ArrayList getListForGradebookNotification( ArrayList newList, ArrayList oldList){ ArrayList l = new ArrayList(); HashMap h = new HashMap(); for (int i=0; i<oldList.size(); i++){ AssessmentGradingData ag = (AssessmentGradingData)oldList.get(i); h.put(ag.getAssessmentGradingId(), ag); } for (int i=0; i<newList.size(); i++){ AssessmentGradingData a = (AssessmentGradingData) newList.get(i); Object o = h.get(a.getAssessmentGradingId()); if (o == null){ // this does not exist in old list, so include it for update l.add(a); } else{ // if new is different from old, include it for update AssessmentGradingData b = (AssessmentGradingData) o; if ((a.getFinalScore()!=null && b.getFinalScore()!=null) && !a.getFinalScore().equals(b.getFinalScore())) l.add(a); } } return l; } public ArrayList getAssessmentGradingsByScoringType( Integer scoringType, Long publishedAssessmentId){ List l = null; // get the list of highest score if ((scoringType).equals(EvaluationModelIfc.HIGHEST_SCORE)){ l = getHighestSubmittedOrGradedAssessmentGradingList(publishedAssessmentId); } // get the list of last score else if ((scoringType).equals(EvaluationModelIfc.LAST_SCORE)) { l = getLastSubmittedOrGradedAssessmentGradingList(publishedAssessmentId); } else { l = getTotalScores(publishedAssessmentId.toString(), "3", false); } return new ArrayList(l); } public Integer getScoringType(PublishedAssessmentIfc pub){ Integer scoringType = null; EvaluationModelIfc e = pub.getEvaluationModel(); if ( e!=null ){ scoringType = e.getScoringType(); } return scoringType; } private boolean updateGradebook(AssessmentGradingIfc data, PublishedAssessmentIfc pub){ // no need to notify gradebook if this submission is not for grade boolean forGrade = (Boolean.TRUE).equals(data.getForGrade()); boolean toGradebook = false; EvaluationModelIfc e = pub.getEvaluationModel(); if ( e!=null ){ String toGradebookString = e.getToGradeBook(); toGradebook = toGradebookString.equals(EvaluationModelIfc.TO_DEFAULT_GRADEBOOK.toString()); } return (forGrade && toGradebook); } private void notifyGradebook(ArrayList l, PublishedAssessmentIfc pub){ for (int i=0; i<l.size(); i++){ notifyGradebook((AssessmentGradingData)l.get(i), pub); } } /** * Get the score information for each item from the assessment score. */ public HashMap getItemScores(Long publishedId, Long itemId, String which) { try { return (HashMap) PersistenceService.getInstance(). getAssessmentGradingFacadeQueries() .getItemScores(publishedId, itemId, which); } catch (Exception e) { e.printStackTrace(); return new HashMap(); } } public HashMap getItemScores(Long itemId, List scores) { try { return (HashMap) PersistenceService.getInstance(). getAssessmentGradingFacadeQueries() .getItemScores(itemId, scores); } catch (Exception e) { e.printStackTrace(); return new HashMap(); } } /** * Get the last set of itemgradingdata for a student per assessment */ public HashMap getLastItemGradingData(String publishedId, String agentId) { try { return (HashMap) PersistenceService.getInstance(). getAssessmentGradingFacadeQueries() .getLastItemGradingData(Long.valueOf(publishedId), agentId); } catch (Exception e) { e.printStackTrace(); return new HashMap(); } } /** * Get the grading data for a given submission */ public HashMap getStudentGradingData(String assessmentGradingId) { try { return (HashMap) PersistenceService.getInstance(). getAssessmentGradingFacadeQueries() .getStudentGradingData(assessmentGradingId); } catch (Exception e) { e.printStackTrace(); return new HashMap(); } } /** * Get the last submission for a student per assessment */ public HashMap getSubmitData(String publishedId, String agentId, Integer scoringoption, String assessmentGradingId) { try { Long gradingId = null; if (assessmentGradingId != null) gradingId = Long.valueOf(assessmentGradingId); return (HashMap) PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getSubmitData(Long.valueOf(publishedId), agentId, scoringoption, gradingId); } catch (Exception e) { e.printStackTrace(); return new HashMap(); } } public String getTextForId(Long typeId) { TypeFacadeQueriesAPI typeFacadeQueries = PersistenceService.getInstance().getTypeFacadeQueries(); TypeFacade type = typeFacadeQueries.getTypeFacadeById(typeId); return (type.getKeyword()); } public int getSubmissionSizeOfPublishedAssessment(String publishedAssessmentId) { try{ return PersistenceService.getInstance(). getAssessmentGradingFacadeQueries() .getSubmissionSizeOfPublishedAssessment(Long.valueOf( publishedAssessmentId)); } catch(Exception e) { e.printStackTrace(); return 0; } } public HashMap getSubmissionSizeOfAllPublishedAssessments() { return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getSubmissionSizeOfAllPublishedAssessments(); } public HashMap getAGDataSizeOfAllPublishedAssessments() { return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getAGDataSizeOfAllPublishedAssessments(); } public Long saveMedia(byte[] media, String mimeType){ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). saveMedia(media, mimeType); } public Long saveMedia(MediaData mediaData){ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). saveMedia(mediaData); } public MediaData getMedia(String mediaId){ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getMedia(Long.valueOf(mediaId)); } public ArrayList getMediaArray(String itemGradingId){ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getMediaArray(Long.valueOf(itemGradingId)); } public ArrayList getMediaArray2(String itemGradingId){ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getMediaArray2(Long.valueOf(itemGradingId)); } public ArrayList getMediaArray(ItemGradingData i){ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getMediaArray(i); } public HashMap getMediaItemGradingHash(Long assessmentGradingId) { return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getMediaItemGradingHash(assessmentGradingId); } public List getMediaArray(String publishedId, String publishItemId, String which){ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getMediaArray(Long.valueOf(publishedId), Long.valueOf(publishItemId), which); } public ItemGradingData getLastItemGradingDataByAgent(String publishedItemId, String agentId) { try { return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getLastItemGradingDataByAgent(Long.valueOf(publishedItemId), agentId); } catch (Exception e) { e.printStackTrace(); return null; } } public ItemGradingData getItemGradingData(String assessmentGradingId, String publishedItemId) { try { return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getItemGradingData(Long.valueOf(assessmentGradingId), Long.valueOf(publishedItemId)); } catch (Exception e) { e.printStackTrace(); return null; } } public AssessmentGradingData load(String assessmentGradingId) { try{ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). load(Long.valueOf(assessmentGradingId)); } catch(Exception e) { log.error(e); throw new RuntimeException(e); } } public ItemGradingData getItemGrading(String itemGradingId) { try{ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getItemGrading(Long.valueOf(itemGradingId)); } catch(Exception e) { log.error(e); throw new Error(e); } } public AssessmentGradingIfc getLastAssessmentGradingByAgentId(String publishedAssessmentId, String agentIdString) { try{ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getLastAssessmentGradingByAgentId(Long.valueOf(publishedAssessmentId), agentIdString); } catch(Exception e) { log.error(e); throw new RuntimeException(e); } } public AssessmentGradingData getLastSavedAssessmentGradingByAgentId(String publishedAssessmentId, String agentIdString) { try{ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getLastSavedAssessmentGradingByAgentId(Long.valueOf(publishedAssessmentId), agentIdString); } catch(Exception e) { log.error(e); throw new RuntimeException(e); } } public AssessmentGradingData getLastSubmittedAssessmentGradingByAgentId(String publishedAssessmentId, String agentIdString, String assessmentGradingId) { AssessmentGradingData assessmentGranding = null; try { if (assessmentGradingId != null) { assessmentGranding = PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getLastSubmittedAssessmentGradingByAgentId(Long.valueOf(publishedAssessmentId), agentIdString, Long.valueOf(assessmentGradingId)); } else { assessmentGranding = PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getLastSubmittedAssessmentGradingByAgentId(Long.valueOf(publishedAssessmentId), agentIdString, null); } } catch(Exception e) { log.error(e); throw new RuntimeException(e); } return assessmentGranding; } public void saveItemGrading(ItemGradingIfc item) { try { PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().saveItemGrading(item); } catch (Exception e) { e.printStackTrace(); } } public void saveOrUpdateAssessmentGrading(AssessmentGradingIfc assessment) { try { /* // Comment out the whole IF section because the only thing we do here is to // update the itemGradingSet. However, this update is redundant as it will // be updated in saveOrUpdateAssessmentGrading(assessment). if (assessment.getAssessmentGradingId()!=null && assessment.getAssessmentGradingId().longValue()>0){ //1. if assessmentGrading contain itemGrading, we want to insert/update itemGrading first Set itemGradingSet = assessment.getItemGradingSet(); Iterator iter = itemGradingSet.iterator(); while (iter.hasNext()) { ItemGradingData itemGradingData = (ItemGradingData) iter.next(); log.debug("date = " + itemGradingData.getSubmittedDate()); } // The following line seems redundant. I cannot see a reason why we need to save the itmeGradingSet // here and then again in following saveOrUpdateAssessmentGrading(assessment). Comment it out. //saveOrUpdateAll(itemGradingSet); } */ // this will update itemGradingSet and assessmentGrading. May as well, otherwise I would have // to reload assessment again PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().saveOrUpdateAssessmentGrading(assessment); } catch (Exception e) { e.printStackTrace(); } } // This API only touch SAM_ASSESSMENTGRADING_T. No data gets inserted/updated in SAM_ITEMGRADING_T public void saveOrUpdateAssessmentGradingOnly(AssessmentGradingIfc assessment) { Set origItemGradingSet = assessment.getItemGradingSet(); HashSet h = new HashSet(origItemGradingSet); // Clear the itemGradingSet so no data gets inserted/updated in SAM_ITEMGRADING_T; origItemGradingSet.clear(); int size = assessment.getItemGradingSet().size(); log.debug("before persist to db: size = " + size); try { PersistenceService.getInstance().getAssessmentGradingFacadeQueries().saveOrUpdateAssessmentGrading(assessment); } catch (Exception e) { e.printStackTrace(); } finally { // Restore the original itemGradingSet back assessment.setItemGradingSet(h); size = assessment.getItemGradingSet().size(); log.debug("after persist to db: size = " + size); } } public List getAssessmentGradingIds(String publishedItemId){ try{ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getAssessmentGradingIds(Long.valueOf(publishedItemId)); } catch(Exception e) { log.error(e); throw new RuntimeException(e); } } public AssessmentGradingIfc getHighestAssessmentGrading(String publishedAssessmentId, String agentId){ try{ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getHighestAssessmentGrading(Long.valueOf(publishedAssessmentId), agentId); } catch(Exception e) { log.error(e); throw new RuntimeException(e); } } public AssessmentGradingIfc getHighestSubmittedAssessmentGrading(String publishedAssessmentId, String agentId, String assessmentGradingId){ AssessmentGradingIfc assessmentGrading = null; try { if (assessmentGradingId != null) { assessmentGrading = PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getHighestSubmittedAssessmentGrading(Long.valueOf(publishedAssessmentId), agentId, Long.valueOf(assessmentGradingId)); } else { assessmentGrading = PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getHighestSubmittedAssessmentGrading(Long.valueOf(publishedAssessmentId), agentId, null); } } catch(Exception e) { log.error(e); throw new RuntimeException(e); } return assessmentGrading; } public AssessmentGradingIfc getHighestSubmittedAssessmentGrading(String publishedAssessmentId, String agentId){ return getHighestSubmittedAssessmentGrading(publishedAssessmentId, agentId, null); } public Set getItemGradingSet(String assessmentGradingId){ try{ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getItemGradingSet(Long.valueOf(assessmentGradingId)); } catch(Exception e){ log.error(e); throw new RuntimeException(e); } } public HashMap getAssessmentGradingByItemGradingId(String publishedAssessmentId){ try{ return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getAssessmentGradingByItemGradingId(Long.valueOf(publishedAssessmentId)); } catch(Exception e){ log.error(e); throw new RuntimeException(e); } } public void updateItemScore(ItemGradingData gdata, float scoreDifference, PublishedAssessmentIfc pub){ try { AssessmentGradingData adata = load(gdata.getAssessmentGradingId().toString()); adata.setItemGradingSet(getItemGradingSet(adata.getAssessmentGradingId().toString())); Set itemGradingSet = adata.getItemGradingSet(); Iterator iter = itemGradingSet.iterator(); float totalAutoScore = 0; float totalOverrideScore = adata.getTotalOverrideScore().floatValue(); while (iter.hasNext()){ ItemGradingIfc i = (ItemGradingIfc)iter.next(); if (i.getItemGradingId().equals(gdata.getItemGradingId())){ i.setAutoScore(gdata.getAutoScore()); i.setComments(gdata.getComments()); i.setGradedBy(AgentFacade.getAgentString()); i.setGradedDate(new Date()); } if (i.getAutoScore()!=null) totalAutoScore += i.getAutoScore().floatValue(); } adata.setTotalAutoScore( Float.valueOf(totalAutoScore)); if (Float.compare((totalAutoScore+totalOverrideScore),Float.valueOf("0").floatValue())<0){ adata.setFinalScore(Float.valueOf("0")); }else{ adata.setFinalScore(Float.valueOf(totalAutoScore+totalOverrideScore)); } saveOrUpdateAssessmentGrading(adata); if (scoreDifference != 0){ notifyGradebookByScoringType(adata, pub); } } catch (GradebookServiceException ge) { ge.printStackTrace(); throw ge; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * Assume this is a new item. */ public void storeGrades(AssessmentGradingIfc data, PublishedAssessmentIfc pub, HashMap publishedItemHash, HashMap publishedItemTextHash, HashMap publishedAnswerHash) { log.debug("storeGrades: data.getSubmittedDate()" + data.getSubmittedDate()); storeGrades(data, false, pub, publishedItemHash, publishedItemTextHash, publishedAnswerHash, true); } /** * Assume this is a new item. */ public void storeGrades(AssessmentGradingIfc data, PublishedAssessmentIfc pub, HashMap publishedItemHash, HashMap publishedItemTextHash, HashMap publishedAnswerHash, boolean persistToDB) { log.debug("storeGrades (not persistToDB) : data.getSubmittedDate()" + data.getSubmittedDate()); storeGrades(data, false, pub, publishedItemHash, publishedItemTextHash, publishedAnswerHash, persistToDB); } /** * This is the big, complicated mess where we take all the items in * an assessment, store the grading data, auto-grade it, and update * everything. * * If regrade is true, we just recalculate the graded score. If it's * false, we do everything from scratch. */ public void storeGrades(AssessmentGradingIfc data, boolean regrade, PublishedAssessmentIfc pub, HashMap publishedItemHash, HashMap publishedItemTextHash, HashMap publishedAnswerHash, boolean persistToDB) throws GradebookServiceException { log.debug("****x1. regrade ="+regrade+" "+(new Date()).getTime()); try { String agent = data.getAgentId(); // Added persistToDB because if we don't save data to DB later, we shouldn't update the assessment // submittedDate either. The date should be sync in delivery bean and DB // This is for DeliveryBean.checkDataIntegrity() if (!regrade && persistToDB) { data.setSubmittedDate(new Date()); setIsLate(data, pub); } // note that this itemGradingSet is a partial set of answer submitted. it contains only // newly submitted answers, updated answers and MCMR/FIB/FIN answers ('cos we need the old ones to // calculate scores for new ones) Set itemGradingSet = data.getItemGradingSet(); if (itemGradingSet == null) itemGradingSet = new HashSet(); log.debug("****itemGrading size="+itemGradingSet.size()); Iterator iter = itemGradingSet.iterator(); // fibAnswersMap contains a map of HashSet of answers for a FIB item, // key =itemid, value= HashSet of answers for each item. // This is used to keep track of answers we have already used for // mutually exclusive multiple answer type of FIB, such as // The flag of the US is {red|white|blue},{red|white|blue}, and {red|white|blue}. // so if the first blank has an answer 'red', the 'red' answer should // not be included in the answers for the other mutually exclusive blanks. HashMap fibAnswersMap= new HashMap(); //change algorithm based on each question (SAK-1930 & IM271559) -cwen HashMap totalItems = new HashMap(); log.debug("****x2. "+(new Date()).getTime()); float autoScore = (float) 0; while(iter.hasNext()) { ItemGradingIfc itemGrading = (ItemGradingIfc) iter.next(); Long itemId = itemGrading.getPublishedItemId(); ItemDataIfc item = (ItemDataIfc) publishedItemHash.get(itemId); if (item == null) { //this probably shouldn't happen log.error("unable to retrive itemDataIfc for: " + publishedItemHash.get(itemId)); continue; } Long itemType = item.getTypeId(); autoScore = (float) 0; itemGrading.setAssessmentGradingId(data.getAssessmentGradingId()); //itemGrading.setSubmittedDate(new Date()); itemGrading.setAgentId(agent); itemGrading.setOverrideScore(Float.valueOf(0)); // note that totalItems & fibAnswersMap would be modified by the following method autoScore = getScoreByQuestionType(itemGrading, item, itemType, publishedItemTextHash, totalItems, fibAnswersMap, publishedAnswerHash, regrade); log.debug("**!regrade, autoScore="+autoScore); if (!(TypeIfc.MULTIPLE_CORRECT).equals(itemType)) totalItems.put(itemId, Float.valueOf(autoScore)); if (regrade && TypeIfc.AUDIO_RECORDING.equals(itemType)) itemGrading.setAttemptsRemaining(item.getTriesAllowed()); itemGrading.setAutoScore(Float.valueOf(autoScore)); } log.debug("****x3. "+(new Date()).getTime()); // the following procedure ensure total score awarded per question is no less than 0 // this probably only applies to MCMR question type - daisyf iter = itemGradingSet.iterator(); while(iter.hasNext()) { ItemGradingIfc itemGrading = (ItemGradingIfc) iter.next(); Long itemId = itemGrading.getPublishedItemId(); ItemDataIfc item = (ItemDataIfc) publishedItemHash.get(itemId); Long itemType2 = item.getTypeId(); //float autoScore = (float) 0; float eachItemScore = ((Float) totalItems.get(itemId)).floatValue(); if((eachItemScore < 0) && !((TypeIfc.MULTIPLE_CHOICE).equals(itemType2)||(TypeIfc.TRUE_FALSE).equals(itemType2))) { itemGrading.setAutoScore( Float.valueOf(0)); } } log.debug("****x4. "+(new Date()).getTime()); // save#1: this itemGrading Set is a partial set of answers submitted. it contains new answers and // updated old answers and FIB answers ('cos we need the old answer to calculate the score for new // ones). we need to be cheap, we don't want to update record that hasn't been // changed. Yes, assessmentGrading's total score will be out of sync at this point, I am afraid. It // would be in sync again once the whole method is completed sucessfully. if (persistToDB) { saveOrUpdateAll(itemGradingSet); } log.debug("****x5. "+(new Date()).getTime()); // save#2: now, we need to get the full set so we can calculate the total score accumulate for the // whole assessment. Set fullItemGradingSet = getItemGradingSet(data.getAssessmentGradingId().toString()); float totalAutoScore = getTotalAutoScore(fullItemGradingSet); data.setTotalAutoScore( Float.valueOf(totalAutoScore)); //log.debug("**#1 total AutoScore"+totalAutoScore); if (Float.compare((totalAutoScore + data.getTotalOverrideScore().floatValue()),new Float("0").floatValue())<0){ data.setFinalScore( Float.valueOf("0")); }else{ data.setFinalScore(Float.valueOf(totalAutoScore + data.getTotalOverrideScore().floatValue())); } log.debug("****x6. "+(new Date()).getTime()); } catch (GradebookServiceException ge) { ge.printStackTrace(); throw ge; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } // save#3: itemGradingSet has been saved above so just need to update assessmentGrading // therefore setItemGradingSet as empty first - daisyf // however, if we do not persit to DB, we want to keep itemGradingSet with data for later use // Because if itemGradingSet is not saved to DB, we cannot go to DB to get it. We have to // get it through data. if (persistToDB) { data.setItemGradingSet(new HashSet()); saveOrUpdateAssessmentGrading(data); log.debug("****x7. "+(new Date()).getTime()); if (!regrade) { notifyGradebookByScoringType(data, pub); } } log.debug("****x8. "+(new Date()).getTime()); // I am not quite sure what the following code is doing... I modified this based on my assumption: // If this happens dring regrade, we don't want to clean these data up // We only want to clean them out in delivery if (!regrade && Boolean.TRUE.equals(data.getForGrade())) { // remove the assessmentGradingData created during gradiing (by updatding total score page) removeUnsubmittedAssessmentGradingData(data); } } private float getTotalAutoScore(Set itemGradingSet){ //log.debug("*** no. of itemGrading="+itemGradingSet.size()); float totalAutoScore =0; Iterator iter = itemGradingSet.iterator(); while (iter.hasNext()){ ItemGradingIfc i = (ItemGradingIfc)iter.next(); //log.debug(i.getItemGradingId()+"->"+i.getAutoScore()); if (i.getAutoScore()!=null) totalAutoScore += i.getAutoScore().floatValue(); } return totalAutoScore; } private void notifyGradebookByScoringType(AssessmentGradingIfc data, PublishedAssessmentIfc pub){ Integer scoringType = pub.getEvaluationModel().getScoringType(); if (updateGradebook(data, pub)){ AssessmentGradingIfc d = data; // data is the last submission // need to decide what to tell gradebook if ((scoringType).equals(EvaluationModelIfc.HIGHEST_SCORE)) d = getHighestSubmittedAssessmentGrading(pub.getPublishedAssessmentId().toString(), data.getAgentId()); notifyGradebook(d, pub); } } private float getScoreByQuestionType(ItemGradingIfc itemGrading, ItemDataIfc item, Long itemType, HashMap publishedItemTextHash, HashMap totalItems, HashMap fibAnswersMap, HashMap publishedAnswerHash, boolean regrade){ //float score = (float) 0; float initScore = (float) 0; float autoScore = (float) 0; float accumelateScore = (float) 0; Long itemId = item.getItemId(); int type = itemType.intValue(); switch (type){ case 1: // MC Single Correct if(item.getPartialCreditFlag()) autoScore = getAnswerScoreMCQ(itemGrading, publishedAnswerHash); else{ autoScore = getAnswerScore(itemGrading, publishedAnswerHash); } //overridescore if (itemGrading.getOverrideScore() != null) autoScore += itemGrading.getOverrideScore().floatValue(); totalItems.put(itemId, new Float(autoScore)); break;// MC Single Correct case 12: // MC Multiple Correct Single Selection case 3: // MC Survey case 4: // True/False autoScore = getAnswerScore(itemGrading, publishedAnswerHash); //overridescore if (itemGrading.getOverrideScore() != null) autoScore += itemGrading.getOverrideScore().floatValue(); totalItems.put(itemId, Float.valueOf(autoScore)); break; case 2: // MC Multiple Correct ItemTextIfc itemText = (ItemTextIfc) publishedItemTextHash.get(itemGrading.getPublishedItemTextId()); ArrayList answerArray = itemText.getAnswerArray(); int correctAnswers = 0; if (answerArray != null){ for (int i =0; i<answerArray.size(); i++){ AnswerIfc a = (AnswerIfc) answerArray.get(i); if (a.getIsCorrect().booleanValue()) correctAnswers++; } } initScore = getAnswerScore(itemGrading, publishedAnswerHash); if (initScore > 0) autoScore = initScore / correctAnswers; else autoScore = (getTotalCorrectScore(itemGrading, publishedAnswerHash) / correctAnswers) * ((float) -1); //overridescore? if (itemGrading.getOverrideScore() != null) autoScore += itemGrading.getOverrideScore().floatValue(); if (!totalItems.containsKey(itemId)){ totalItems.put(itemId, Float.valueOf(autoScore)); //log.debug("****0. first answer score = "+autoScore); } else{ accumelateScore = ((Float)totalItems.get(itemId)).floatValue(); //log.debug("****1. before adding new score = "+accumelateScore); //log.debug("****2. this answer score = "+autoScore); accumelateScore += autoScore; //log.debug("****3. add 1+2 score = "+accumelateScore); totalItems.put(itemId, Float.valueOf(accumelateScore)); //log.debug("****4. what did we put in = "+((Float)totalItems.get(itemId)).floatValue()); } break; case 9: // Matching initScore = getAnswerScore(itemGrading, publishedAnswerHash); if (initScore > 0) autoScore = initScore / ((float) item.getItemTextSet().size()); //overridescore? if (itemGrading.getOverrideScore() != null) autoScore += itemGrading.getOverrideScore().floatValue(); if (!totalItems.containsKey(itemId)) totalItems.put(itemId, Float.valueOf(autoScore)); else { accumelateScore = ((Float)totalItems.get(itemId)).floatValue(); accumelateScore += autoScore; totalItems.put(itemId, Float.valueOf(accumelateScore)); } break; case 8: // FIB autoScore = getFIBScore(itemGrading, fibAnswersMap, item, publishedAnswerHash) / (float) ((ItemTextIfc) item.getItemTextSet().toArray()[0]).getAnswerSet().size(); //overridescore - cwen if (itemGrading.getOverrideScore() != null) autoScore += itemGrading.getOverrideScore().floatValue(); if (!totalItems.containsKey(itemId)) totalItems.put(itemId, Float.valueOf(autoScore)); else { accumelateScore = ((Float)totalItems.get(itemId)).floatValue(); accumelateScore += autoScore; totalItems.put(itemId, Float.valueOf(accumelateScore)); } break; case 11: // FIN autoScore = getFINScore(itemGrading, item, publishedAnswerHash) / (float) ((ItemTextIfc) item.getItemTextSet().toArray()[0]).getAnswerSet().size(); //overridescore - cwen if (itemGrading.getOverrideScore() != null) autoScore += itemGrading.getOverrideScore().floatValue(); if (!totalItems.containsKey(itemId)) totalItems.put(itemId, Float.valueOf(autoScore)); else { accumelateScore = ((Float)totalItems.get(itemId)).floatValue(); accumelateScore += autoScore; totalItems.put(itemId, Float.valueOf(accumelateScore)); } break; case 5: // SAQ case 6: // file upload case 7: // audio recording //overridescore - cwen if (regrade && itemGrading.getAutoScore() != null) { autoScore = itemGrading.getAutoScore(); } if (itemGrading.getOverrideScore() != null) autoScore += itemGrading.getOverrideScore().floatValue(); if (!totalItems.containsKey(itemId)) totalItems.put(itemId, Float.valueOf(autoScore)); else { accumelateScore = ((Float)totalItems.get(itemId)).floatValue(); accumelateScore += autoScore; totalItems.put(itemId, Float.valueOf(accumelateScore)); } break; } return autoScore; } /** * This grades multiple choice and true false questions. Since * multiple choice/multiple select has a separate ItemGradingIfc for * each choice, they're graded the same way the single choice are. * Choices should be given negative score values if one wants them * to lose points for the wrong choice. */ public float getAnswerScore(ItemGradingIfc data, HashMap publishedAnswerHash) { AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId()); if (answer == null || answer.getScore() == null) { return (float) 0; } ItemDataIfc item = (ItemDataIfc) answer.getItem(); Long itemType = item.getTypeId(); if (answer.getIsCorrect() == null || !answer.getIsCorrect().booleanValue()) { // return (float) 0; // Para que descuente (For discount) if ((TypeIfc.MULTIPLE_CHOICE).equals(itemType)||(TypeIfc.TRUE_FALSE).equals(itemType)){ return (Math.abs(answer.getDiscount().floatValue()) * ((float) -1)); }else{ return (float) 0; } } return answer.getScore().floatValue(); } public void notifyGradebook(AssessmentGradingIfc data, PublishedAssessmentIfc pub) throws GradebookServiceException { // If the assessment is published to the gradebook, make sure to update the scores in the gradebook String toGradebook = pub.getEvaluationModel().getToGradeBook(); GradebookService g = null; boolean integrated = IntegrationContextFactory.getInstance().isIntegrated(); if (integrated) { g = (GradebookService) SpringBeanLocator.getInstance(). getBean("org.sakaiproject.service.gradebook.GradebookService"); } GradebookServiceHelper gbsHelper = IntegrationContextFactory.getInstance().getGradebookServiceHelper(); PublishedAssessmentService publishedAssessmentService = new PublishedAssessmentService(); String currentSiteId = publishedAssessmentService.getPublishedAssessmentSiteId(pub.getPublishedAssessmentId().toString()); if (gbsHelper.gradebookExists(GradebookFacade.getGradebookUId(currentSiteId), g) && toGradebook.equals(EvaluationModelIfc.TO_DEFAULT_GRADEBOOK.toString())){ if(log.isDebugEnabled()) log.debug("Attempting to update a score in the gradebook"); // add retry logic to resolve deadlock problem while sending grades to gradebook Float originalFinalScore = data.getFinalScore(); int retryCount = PersistenceService.getInstance().getRetryCount().intValue(); while (retryCount > 0){ try { // Send the average score if average was selected for multiple submissions Integer scoringType = pub.getEvaluationModel().getScoringType(); if (scoringType.equals(EvaluationModelIfc.AVERAGE_SCORE)) { Float averageScore = PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getAverageSubmittedAssessmentGrading(Long.valueOf(pub.getPublishedAssessmentId()), data.getAgentId()); data.setFinalScore(averageScore); } gbsHelper.updateExternalAssessmentScore(data, g); retryCount = 0; } catch (org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException ante) { log.warn("problem sending grades to gradebook: " + ante.getMessage()); if (AssessmentIfc.RETRACT_FOR_EDIT_STATUS.equals(pub.getStatus())) { retryCount = retry(retryCount, ante, pub, true); } else { // Otherwise, do the same exeption handling as others retryCount = retry(retryCount, ante, pub, false); } } catch (Exception e) { retryCount = retry(retryCount, e, pub, false); } } //change the final score back to the original score since it may set to average score. if(data.getFinalScore() != originalFinalScore ) { data.setFinalScore(originalFinalScore); } } else { if(log.isDebugEnabled()) log.debug("Not updating the gradebook. toGradebook = " + toGradebook); } } private int retry(int retryCount, Exception e, PublishedAssessmentIfc pub, boolean retractForEditStatus) { log.warn("retrying...sending grades to gradebook. "); log.warn("retry...."); retryCount--; try { int deadlockInterval = PersistenceService.getInstance().getDeadlockInterval().intValue(); Thread.sleep(deadlockInterval); } catch(InterruptedException ex){ log.warn(ex.getMessage()); } if (retryCount==0) { if (retractForEditStatus) { // This happens in following scenario: // 1. The assessment is active and has "None" for GB setting // 2. Instructor retracts it for edit and update the to "Send to GB" // 3. Instructor updates something on the total Score page // Because the GB will not be created until the assessment gets republished, // "AssessmentNotFoundException" will be thrown here. Since, this is the expected // exception, we simply log a debug message without retrying or notifying the user. // Of course, you can argue about what if the assessment gets deleted by other cause. // But I would say the major cause would be this "retract" scenario. Also, without knowing // the change history of the assessment, I think this is the best handling. log.info("We quietly sallow the AssessmentNotFoundException excption here. Published Assessment Name: " + pub.getTitle()); } else { // after retries, still failed updating gradebook log.warn("After all retries, still failed ... Now throw error to UI"); throw new GradebookServiceException(e); } } return retryCount; } /** * This grades Fill In Blank questions. (see SAK-1685) * There will be two valid cases for scoring when there are multiple fill * in blanks in a question: * Case 1- There are different sets of answers (a set can contain one or more * item) for each blank (e.g. The {dog|coyote|wolf} howls and the {lion|cougar} * roars.) In this case each blank is tested for correctness independently. * Case 2-There is the same set of answers for each blank: e.g. The flag of the US * is {red|white|blue},{red|white|blue}, and {red|white|blue}. * These are the only two valid types of questions. When authoring, it is an * ERROR to include: * (1) a mixture of independent answer and common answer blanks * (e.g. The {dog|coyote|wolf} howls at the {red|white|blue}, {red|white|blue}, * and {red|white|blue} flag.) * (2) more than one set of blanks with a common answer ((e.g. The US flag * is {red|white|blue}, {red|white|blue}, and {red|white|blue} and the Italian * flag is {red|white|greem}, {red|white|greem}, and {red|white|greem}.) * These two invalid questions specifications should be authored as two * separate questions. Here are the definition and 12 cases I came up with (lydia, 01/2006): single answers : roses are {red} and vilets are {blue} multiple answers : {dogs|cats} have 4 legs multiple answers , mutually exclusive, all answers must be identical, can be in diff. orders : US flag has {red|blue|white} and {red |white|blue} and {blue|red|white} colors multiple answers , mutually non-exclusive : {dogs|cats} have 4 legs and {dogs|cats} can be pets. wildcard uses * to mean one of more characters -. wildcard single answer, case sensitive -. wildcard single answer, case insensitive -. single answer, no wildcard , case sensitive -. single answer, no wildcard , case insensitive -. multiple answer, mutually non-exclusive, no wildcard , case sensitive -. multiple answer, mutually non-exclusive, no wildcard , case in sensitive -. multiple answer, mutually non-exclusive, wildcard , case sensitive -. multiple answer, mutually non-exclusive, wildcard , case insensitive -. multiple answer, mutually exclusive, no wildcard , case sensitive -. multiple answer, mutually exclusive, no wildcard , case in sensitive -. multiple answer, mutually exclusive, wildcard , case sensitive -. multiple answer, mutually exclusive, wildcard , case insensitive */ public float getFIBScore(ItemGradingIfc data, HashMap fibmap, ItemDataIfc itemdata, HashMap publishedAnswerHash) { String studentanswer = ""; boolean matchresult = false; float totalScore = (float) 0; if (data.getPublishedAnswerId() == null) { return totalScore; } AnswerIfc answerIfc = (AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId()); if (answerIfc == null) { return totalScore; } String answertext = answerIfc.getText(); Long itemId = itemdata.getItemId(); String casesensitive = itemdata.getItemMetaDataByLabel(ItemMetaDataIfc.CASE_SENSITIVE_FOR_FIB); String mutuallyexclusive = itemdata.getItemMetaDataByLabel(ItemMetaDataIfc.MUTUALLY_EXCLUSIVE_FOR_FIB); //Set answerSet = new HashSet(); if (answertext != null) { StringTokenizer st = new StringTokenizer(answertext, "|"); while (st.hasMoreTokens()) { String answer = st.nextToken().trim(); if ("true".equalsIgnoreCase(casesensitive)) { if (data.getAnswerText() != null){ studentanswer= data.getAnswerText().trim(); matchresult = fibmatch(answer, studentanswer, true); } } // if case sensitive else { // case insensitive , if casesensitive is false, or null, or "". if (data.getAnswerText() != null){ studentanswer= data.getAnswerText().trim(); matchresult = fibmatch(answer, studentanswer, false); } } // else , case insensitive if (matchresult){ boolean alreadyused=false; // add check for mutual exclusive if ("true".equalsIgnoreCase(mutuallyexclusive)) { // check if answers are already used. Set answer_used_sofar = (HashSet) fibmap.get(itemId); if ((answer_used_sofar!=null) && ( answer_used_sofar.contains(studentanswer.toLowerCase()))){ // already used, so it's a wrong answer for mutually exclusive questions alreadyused=true; } else { // not used, it's a good answer, now add this to the already_used list. // we only store lowercase strings in the fibmap. if (answer_used_sofar==null) { answer_used_sofar = new HashSet(); } answer_used_sofar.add(studentanswer.toLowerCase()); fibmap.put(itemId, answer_used_sofar); } } if (!alreadyused) { totalScore += ((AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId())).getScore().floatValue(); } // SAK-3005: quit if answer is correct, e.g. if you answered A for {a|A}, you already scored break; } } } return totalScore; } public boolean getFIBResult(ItemGradingIfc data, HashMap fibmap, ItemDataIfc itemdata, HashMap publishedAnswerHash) { // this method is similiar to getFIBScore(), except it returns true/false for the answer, not scores. // may be able to refactor code out to be reused, but totalscores for mutually exclusive case is a bit tricky. String studentanswer = ""; boolean matchresult = false; if (data.getPublishedAnswerId() == null) { return matchresult; } AnswerIfc answerIfc = (AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId()); if (answerIfc == null) { return matchresult; } String answertext = answerIfc.getText(); Long itemId = itemdata.getItemId(); String casesensitive = itemdata.getItemMetaDataByLabel(ItemMetaDataIfc.CASE_SENSITIVE_FOR_FIB); String mutuallyexclusive = itemdata.getItemMetaDataByLabel(ItemMetaDataIfc.MUTUALLY_EXCLUSIVE_FOR_FIB); //Set answerSet = new HashSet(); if (answertext != null) { StringTokenizer st = new StringTokenizer(answertext, "|"); while (st.hasMoreTokens()) { String answer = st.nextToken().trim(); if ("true".equalsIgnoreCase(casesensitive)) { if (data.getAnswerText() != null){ studentanswer= data.getAnswerText().trim(); matchresult = fibmatch(answer, studentanswer, true); } } // if case sensitive else { // case insensitive , if casesensitive is false, or null, or "". if (data.getAnswerText() != null){ studentanswer= data.getAnswerText().trim(); matchresult = fibmatch(answer, studentanswer, false); } } // else , case insensitive if (matchresult){ boolean alreadyused=false; // add check for mutual exclusive if ("true".equalsIgnoreCase(mutuallyexclusive)) { // check if answers are already used. Set answer_used_sofar = (HashSet) fibmap.get(itemId); if ((answer_used_sofar!=null) && ( answer_used_sofar.contains(studentanswer.toLowerCase()))){ // already used, so it's a wrong answer for mutually exclusive questions alreadyused=true; } else { // not used, it's a good answer, now add this to the already_used list. // we only store lowercase strings in the fibmap. if (answer_used_sofar==null) { answer_used_sofar = new HashSet(); } answer_used_sofar.add(studentanswer.toLowerCase()); fibmap.put(itemId, answer_used_sofar); } } if (alreadyused) { matchresult = false; } break; } } } return matchresult; } public float getFINScore(ItemGradingIfc data, ItemDataIfc itemdata, HashMap publishedAnswerHash) { float totalScore = (float) 0; boolean matchresult = getFINResult(data, itemdata, publishedAnswerHash); if (matchresult){ totalScore += ((AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId())).getScore().floatValue(); } return totalScore; } public boolean getFINResult(ItemGradingIfc data, ItemDataIfc itemdata, HashMap publishedAnswerHash) { // this method checks if the FIN answer is correct. String studentanswer = ""; boolean range; boolean matchresult = false; float studentAnswerNum,answer1Num,answer2Num,answerNum; if (data.getPublishedAnswerId() == null) { return false; } AnswerIfc answerIfc = (AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId()); if (answerIfc == null) { return matchresult; } String answertext = answerIfc.getText(); //Long itemId = itemdata.getItemId(); //Set answerSet = new HashSet(); if (answertext != null) { StringTokenizer st = new StringTokenizer(answertext, "|"); range=false; if (st.countTokens()>1){ range=true; } if (range) { String answer1 = st.nextToken().trim(); String answer2 = st.nextToken().trim(); if (answer1 != null){ answer1= answer1.trim().replace(',','.'); // in Spain, comma is used as a decimal point } try{ answer1Num = Float.valueOf(answer1).floatValue(); }catch(NumberFormatException ex){ answer1Num = Float.NaN; } log.info("answer1Num= " + answer1Num); if (answer2 != null){ answer2= answer2.trim().replace(',','.'); // in Spain, comma is used as a decimal point } try{ answer2Num = Float.valueOf(answer2).floatValue(); }catch(NumberFormatException ex){ answer2Num = Float.NaN; } log.info("answer2Num= " + answer2Num); // Can accept increasing and decreasing ranges if (answer1Num > answer2Num) { float swap = answer1Num; answer1Num = answer2Num; answer2Num = swap; } if (data.getAnswerText() != null){ studentanswer= data.getAnswerText().trim().replace(',','.'); // in Spain, comma is used as a decimal point try{ studentAnswerNum = Float.valueOf(studentanswer).floatValue(); }catch(NumberFormatException ex){ studentAnswerNum = Float.NaN; //Temporal. Directamente contar\? como mala. } log.info("studentAnswerNum= " + studentAnswerNum); if (!(Float.isNaN(studentAnswerNum) || Float.isNaN(answer1Num) || Float.isNaN(answer2Num))){ matchresult=((answer1Num <= studentAnswerNum) && (answer2Num >= studentAnswerNum)) ; } } }else{ //range String answer = st.nextToken().trim(); if (answer != null){ answer= answer.trim().replace(',','.'); // in Spain, comma is used as a decimal point } try{ answerNum = Float.valueOf(answer).floatValue(); }catch(NumberFormatException ex){ answerNum = Float.NaN; // should not go here } log.info("answerNum= " + answerNum); if (data.getAnswerText() != null){ studentanswer= data.getAnswerText().trim().replace(',','.'); // in Spain, comma is used as a decimal point try{ studentAnswerNum = Float.valueOf(studentanswer).floatValue(); }catch(NumberFormatException ex){ studentAnswerNum = Float.NaN; } log.info("studentAnswerNum= " + studentAnswerNum); if (!(Float.isNaN(studentAnswerNum) || Float.isNaN(answerNum))){ matchresult=(answerNum == studentAnswerNum) ; } } } } return matchresult; } public float getTotalCorrectScore(ItemGradingIfc data, HashMap publishedAnswerHash) { AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId()); if (answer == null || answer.getScore() == null) return (float) 0; return answer.getScore().floatValue(); } private void setIsLate(AssessmentGradingIfc data, PublishedAssessmentIfc pub){ if (pub.getAssessmentAccessControl() != null && pub.getAssessmentAccessControl().getDueDate() != null && pub.getAssessmentAccessControl().getDueDate().before(new Date())) data.setIsLate(Boolean.TRUE); else data.setIsLate( Boolean.valueOf(false)); if (data.getForGrade().booleanValue()) data.setStatus( Integer.valueOf(1)); data.setTotalOverrideScore(Float.valueOf(0)); } public void deleteAll(Collection c) { try { PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().deleteAll(c); } catch (Exception e) { e.printStackTrace(); } } /* Note: * assessmentGrading contains set of itemGrading that are not saved in the DB yet */ public void updateAssessmentGradingScore(AssessmentGradingIfc adata, PublishedAssessmentIfc pub){ try { Set itemGradingSet = adata.getItemGradingSet(); Iterator iter = itemGradingSet.iterator(); float totalAutoScore = 0; float totalOverrideScore = adata.getTotalOverrideScore().floatValue(); while (iter.hasNext()){ ItemGradingIfc i = (ItemGradingIfc)iter.next(); if (i.getAutoScore()!=null) totalAutoScore += i.getAutoScore().floatValue(); } float oldAutoScore = adata.getTotalAutoScore().floatValue(); float scoreDifference = totalAutoScore - oldAutoScore; adata.setTotalAutoScore(Float.valueOf(totalAutoScore)); if (Float.compare((totalAutoScore+totalOverrideScore),Float.valueOf("0").floatValue())<0){ adata.setFinalScore(Float.valueOf("0")); }else{ adata.setFinalScore(Float.valueOf(totalAutoScore+totalOverrideScore)); } saveOrUpdateAssessmentGrading(adata); if (scoreDifference != 0){ notifyGradebookByScoringType(adata, pub); } } catch (GradebookServiceException ge) { ge.printStackTrace(); throw ge; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } public void saveOrUpdateAll(Collection c) { try { PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().saveOrUpdateAll(c); } catch (Exception e) { e.printStackTrace(); } } public PublishedAssessmentIfc getPublishedAssessmentByAssessmentGradingId(String id){ PublishedAssessmentIfc pub = null; try { pub = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getPublishedAssessmentByAssessmentGradingId(Long.valueOf(id)); } catch (Exception e) { e.printStackTrace(); } return pub; } public PublishedAssessmentIfc getPublishedAssessmentByPublishedItemId(String publishedItemId){ PublishedAssessmentIfc pub = null; try { pub = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getPublishedAssessmentByPublishedItemId(Long.valueOf(publishedItemId)); } catch (Exception e) { e.printStackTrace(); } return pub; } public ArrayList getLastItemGradingDataPosition(Long assessmentGradingId, String agentId) { ArrayList results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getLastItemGradingDataPosition(assessmentGradingId, agentId); } catch (Exception e) { e.printStackTrace(); } return results; } public List getPublishedItemIds(Long assessmentGradingId) { List results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getPublishedItemIds(assessmentGradingId); } catch (Exception e) { e.printStackTrace(); } return results; } public HashSet getItemSet(Long publishedAssessmentId, Long sectionId) { HashSet results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getItemSet(publishedAssessmentId, sectionId); } catch (Exception e) { e.printStackTrace(); } return results; } public Long getTypeId(Long itemGradingId) { Long typeId = null; try { typeId = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getTypeId(itemGradingId); } catch (Exception e) { e.printStackTrace(); } return typeId; } public boolean fibmatch(String answer, String input, boolean casesensitive) { try { // comment this part out if using the jdk 1.5 version /* String REGEX = answer.replaceAll("\\*", ".+"); Pattern p; if (casesensitive) { p = Pattern.compile(REGEX); } else { p = Pattern.compile(REGEX, Pattern.CASE_INSENSITIVE); } Matcher m = p.matcher(input); boolean matchresult = m.matches(); return matchresult; */ // requires jdk 1.5 for Pattern.quote(), allow metacharacters, such as a+b. StringBuilder regex_quotebuf = new StringBuilder(); String REGEX = answer.replaceAll("\\*", "|*|"); String[] oneblank = REGEX.split("\\|"); for (int j = 0; j < oneblank.length; j++) { if ("*".equals(oneblank[j])) { regex_quotebuf.append(".+"); } else { regex_quotebuf.append(Pattern.quote(oneblank[j])); } } String regex_quote = regex_quotebuf.toString(); Pattern p; if (casesensitive){ p = Pattern.compile(regex_quote ); } else { p = Pattern.compile(regex_quote,Pattern.CASE_INSENSITIVE ); } Matcher m = p.matcher(input); boolean result = m.matches(); return result; } catch (Exception e){ return false; } } public List getAllAssessmentGradingByAgentId(Long publishedAssessmentId, String agentIdString) { List results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getAllAssessmentGradingByAgentId(publishedAssessmentId, agentIdString); } catch (Exception e) { e.printStackTrace(); } return results; } public int getActualNumberRetake(Long publishedAssessmentId, String agentIdString) { int actualNumberReatke = 0; try { actualNumberReatke = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getActualNumberRetake(publishedAssessmentId, agentIdString); } catch (Exception e) { e.printStackTrace(); } return actualNumberReatke; } public HashMap getActualNumberRetakeHash(String agentIdString) { HashMap actualNumberReatkeHash = new HashMap(); try { actualNumberReatkeHash = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getActualNumberRetakeHash(agentIdString); } catch (Exception e) { e.printStackTrace(); } return actualNumberReatkeHash; } public List getStudentGradingSummaryData(Long publishedAssessmentId, String agentIdString) { List results = null; try { results = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getStudentGradingSummaryData(publishedAssessmentId, agentIdString); } catch (Exception e) { e.printStackTrace(); } return results; } public int getNumberRetake(Long publishedAssessmentId, String agentIdString) { int numberRetake = 0; try { numberRetake = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getNumberRetake(publishedAssessmentId, agentIdString); } catch (Exception e) { e.printStackTrace(); } return numberRetake; } public HashMap getNumberRetakeHash(String agentIdString) { HashMap numberRetakeHash = new HashMap(); try { numberRetakeHash = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getNumberRetakeHash(agentIdString); } catch (Exception e) { e.printStackTrace(); } return numberRetakeHash; } public void saveStudentGradingSummaryData(StudentGradingSummaryIfc studentGradingSummaryData) { try { PersistenceService.getInstance().getAssessmentGradingFacadeQueries().saveStudentGradingSummaryData(studentGradingSummaryData); } catch (Exception e) { e.printStackTrace(); } } public int getLateSubmissionsNumberByAgentId(Long publishedAssessmentId, String agentIdString, Date dueDate) { int numberRetake = 0; try { numberRetake = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getLateSubmissionsNumberByAgentId(publishedAssessmentId, agentIdString, dueDate); } catch (Exception e) { e.printStackTrace(); } return numberRetake; } public List getExportResponsesData(String publishedAssessmentId, boolean anonymous, String audioMessage, String fileUploadMessage, String noSubmissionMessage, boolean showPartAndTotalScoreSpreadsheetColumns, String questionString, String textString, String rationaleString, Map useridMap) { List list = null; try { list = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getExportResponsesData(publishedAssessmentId, anonymous,audioMessage, fileUploadMessage, noSubmissionMessage, showPartAndTotalScoreSpreadsheetColumns, questionString, textString, rationaleString, useridMap); } catch (Exception e) { e.printStackTrace(); } return list; } private void removeUnsubmittedAssessmentGradingData(AssessmentGradingIfc data){ try { PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().removeUnsubmittedAssessmentGradingData(data); } catch (Exception e) { //e.printStackTrace(); log.error("Exception thrown from removeUnsubmittedAssessmentGradingData" + e.getMessage()); } } public boolean getHasGradingData(Long publishedAssessmentId) { boolean hasGradingData = false; try { hasGradingData = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getHasGradingData(publishedAssessmentId); } catch (Exception e) { e.printStackTrace(); } return hasGradingData; } public ArrayList getHasGradingDataAndHasSubmission(Long publishedAssessmentId) { ArrayList al = new ArrayList(); try { al = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getHasGradingDataAndHasSubmission(publishedAssessmentId); } catch (Exception e) { e.printStackTrace(); } return al; } public String getFileName(Long itemGradingId, String agentId, String filename) { String name = ""; try { name = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getFilename(itemGradingId, agentId, filename); } catch (Exception e) { e.printStackTrace(); } return name; } public List getUpdatedAssessmentList(String agentId, String siteId) { List list = null; try { list = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().getUpdatedAssessmentList(agentId, siteId); } catch (Exception e) { e.printStackTrace(); } return list; } public void autoSubmitAssessments() { try { PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().autoSubmitAssessments(); } catch (Exception e) { e.printStackTrace(); } } public ItemGradingAttachmentIfc createItemGradingAttachment( ItemGradingIfc itemGrading, String resourceId, String filename, String protocol) { ItemGradingAttachmentIfc attachment = null; try { attachment = PersistenceService.getInstance(). getAssessmentGradingFacadeQueries().createItemGradingtAttachment(itemGrading, resourceId, filename, protocol); } catch (Exception e) { e.printStackTrace(); } return attachment; } public void removeItemGradingAttachment(String attachmentId) { PersistenceService.getInstance().getAssessmentGradingFacadeQueries() .removeItemGradingAttachment(Long.valueOf(attachmentId)); } public void saveOrUpdateAttachments(List list) { PersistenceService.getInstance().getAssessmentGradingFacadeQueries() .saveOrUpdateAttachments(list); } public HashMap getInProgressCounts(String siteId) { return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getInProgressCounts(siteId); } public HashMap getSubmittedCounts(String siteId) { return PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). getSubmittedCounts(siteId); } public void completeItemGradingData(AssessmentGradingData assessmentGradingData) { PersistenceService.getInstance().getAssessmentGradingFacadeQueries(). completeItemGradingData(assessmentGradingData); } /** * This grades multiple choice and true false questions. Since * multiple choice/multiple select has a separate ItemGradingIfc for * each choice, they're graded the same way the single choice are. * BUT since we have Partial Credit stuff around we have to have a separate method here --mustansar * Choices should be given negative score values if one wants them * to lose points for the wrong choice. */ public float getAnswerScoreMCQ(ItemGradingIfc data, HashMap publishedAnswerHash) { AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId()); if (answer == null || answer.getScore() == null) { return 0f; } else if (answer.getIsCorrect().booleanValue()){ // instead of using answer score Item score needs to be used here return (answer.getItem().getScore().floatValue()); //--mustansar } return (answer.getItem().getScore().floatValue()*answer.getPartialCredit().floatValue())/100f; } }