/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/sam/trunk/samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/delivery/SubmitToGradingActionListener.java $ * $Id: SubmitToGradingActionListener.java 132168 2013-12-03 20:25:29Z ktsao@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.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.tool.assessment.ui.listener.delivery; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.AbortProcessingException; import javax.faces.event.ActionEvent; import javax.faces.event.ActionListener; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.event.api.Event; import org.sakaiproject.event.api.LearningResourceStoreService; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Actor; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Context; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Object; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Result; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Statement; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Verb; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Verb.SAKAI_VERB; import org.sakaiproject.event.cover.EventTrackingService; import org.sakaiproject.tool.assessment.data.dao.grading.AssessmentGradingData; import org.sakaiproject.tool.assessment.data.dao.grading.ItemGradingData; import org.sakaiproject.tool.assessment.data.exception.SamigoDataAccessException; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentAccessControlIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemDataIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.PublishedAssessmentIfc; import org.sakaiproject.tool.assessment.facade.AgentFacade; import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacade; import org.sakaiproject.tool.assessment.services.FinFormatException; import org.sakaiproject.tool.assessment.services.GradebookServiceException; import org.sakaiproject.tool.assessment.services.GradingService; import org.sakaiproject.tool.assessment.services.ItemService; import org.sakaiproject.tool.assessment.services.SaLengthException; import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService; import org.sakaiproject.tool.assessment.ui.bean.delivery.DeliveryBean; import org.sakaiproject.tool.assessment.ui.bean.delivery.FinBean; import org.sakaiproject.tool.assessment.ui.bean.delivery.ItemContentsBean; import org.sakaiproject.tool.assessment.ui.bean.delivery.SectionContentsBean; import org.sakaiproject.tool.assessment.ui.bean.shared.PersonBean; import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil; import org.sakaiproject.tool.assessment.util.TextFormat; /** * <p> * Title: Samigo * </p> * <p> * Purpose: This listener is called in delivery when the user clicks on submit, * save, or previous, next, toc.... It calculates and saves scores in DB * * @version $Id: SubmitToGradingActionListener.java 11634 2006-07-06 17:35:54Z * daisyf@stanford.edu $ */ public class SubmitToGradingActionListener implements ActionListener { private static Log log = LogFactory .getLog(SubmitToGradingActionListener.class); /** * The publishedAssesmentService */ private PublishedAssessmentService publishedAssesmentService = new PublishedAssessmentService(); /** * ACTION. * * @param ae * @throws AbortProcessingException */ public void processAction(ActionEvent ae) throws AbortProcessingException, FinFormatException, SaLengthException { try { log.debug("SubmitToGradingActionListener.processAction() "); // get managed bean DeliveryBean delivery = (DeliveryBean) ContextUtil .lookupBean("delivery"); if ((ContextUtil.lookupParam("showfeedbacknow") != null && "true" .equals(ContextUtil.lookupParam("showfeedbacknow")) || delivery .getActionMode() == DeliveryBean.PREVIEW_ASSESSMENT)) delivery.setForGrade(false); // get assessment PublishedAssessmentFacade publishedAssessment = null; if (delivery.getPublishedAssessment() != null) publishedAssessment = delivery.getPublishedAssessment(); else { publishedAssessment = publishedAssesmentService .getPublishedAssessment(delivery.getAssessmentId()); delivery.setPublishedAssessment(publishedAssessment); } HashMap invalidFINMap = new HashMap(); ArrayList invalidSALengthList = new ArrayList(); AssessmentGradingData adata = submitToGradingService(ae, publishedAssessment, delivery, invalidFINMap, invalidSALengthList); // set AssessmentGrading in delivery delivery.setAssessmentGrading(adata); if (adata.getForGrade()) { Event event = EventTrackingService.newEvent("", adata.getPublishedAssessmentTitle(), true); LearningResourceStoreService lrss = (LearningResourceStoreService) ComponentManager .get("org.sakaiproject.event.api.LearningResourceStoreService"); if (null != lrss && lrss.getEventActor(event) != null) { lrss.registerStatement(getStatementForGradedAssessment(adata, lrss.getEventActor(event), publishedAssessment), "sakai.samigo"); } } // set url & confirmation after saving the record for grade if (adata != null && delivery.getForGrade()) setConfirmation(adata, publishedAssessment, delivery); if (isForGrade(adata) && !isUnlimited(publishedAssessment)) { delivery.setSubmissionsRemaining(delivery .getSubmissionsRemaining() - 1); } if (invalidFINMap.size() != 0) { delivery.setIsAnyInvalidFinInput(true); throw new FinFormatException ("Not a valid FIN input"); } if (invalidSALengthList.size() != 0) { delivery.setIsAnyInvalidFinInput(true); throw new SaLengthException ("Short Answer input is too long"); } delivery.setIsAnyInvalidFinInput(false); } catch (GradebookServiceException ge) { ge.printStackTrace(); FacesContext context = FacesContext.getCurrentInstance(); String err = (String) ContextUtil.getLocalizedString( "org.sakaiproject.tool.assessment.bundle.AuthorMessages", "gradebook_exception_error"); context.addMessage(null, new FacesMessage(err)); return; } } private boolean isForGrade(AssessmentGradingData aData) { if (aData != null) return (Boolean.TRUE).equals(aData.getForGrade()); else return false; } private boolean isUnlimited(PublishedAssessmentFacade publishedAssessment) { return (Boolean.TRUE).equals(publishedAssessment .getAssessmentAccessControl().getUnlimitedSubmissions()); } /** * This method set the url & confirmation string for submitted.jsp. The * confirmation string = * assessmentGradingId-publishedAssessmentId-agentId-submitteddate * * @param adata * @param publishedAssessment * @param delivery */ private void setConfirmation(AssessmentGradingData adata, PublishedAssessmentFacade publishedAssessment, DeliveryBean delivery) { if (publishedAssessment.getAssessmentAccessControl() != null) { setFinalPage(publishedAssessment, delivery); setSubmissionMessage(publishedAssessment, delivery); } setConfirmationId(adata, publishedAssessment, delivery); } /** * Set confirmationId which is AssessmentGradingId-TimeStamp. * * @param adata * @param publishedAssessment * @param delivery */ private void setConfirmationId(AssessmentGradingData adata, PublishedAssessmentFacade publishedAssessment, DeliveryBean delivery) { delivery.setConfirmation(adata.getAssessmentGradingId() + "-" + publishedAssessment.getPublishedAssessmentId() + "-" + adata.getAgentId() + "-" + adata.getSubmittedDate().toString()); } /** * Set the submission message. * * @param publishedAssessment * @param delivery */ private void setSubmissionMessage( PublishedAssessmentFacade publishedAssessment, DeliveryBean delivery) { String submissionMessage = publishedAssessment .getAssessmentAccessControl().getSubmissionMessage(); if (submissionMessage != null) delivery.setSubmissionMessage(submissionMessage); } /** * Set finalPage url in delivery bean. * * @param publishedAssessment * @param delivery */ private void setFinalPage(PublishedAssessmentFacade publishedAssessment, DeliveryBean delivery) { String url = publishedAssessment.getAssessmentAccessControl() .getFinalPageUrl(); if (url != null) url = url.trim(); delivery.setUrl(url); } /** * Invoke submission and return the grading data * * @param publishedAssessment * @param delivery * @return * @throws SamigoDataAccessException */ private synchronized AssessmentGradingData submitToGradingService( ActionEvent ae, PublishedAssessmentFacade publishedAssessment, DeliveryBean delivery, HashMap invalidFINMap, ArrayList invalidSALengthList) throws FinFormatException, SamigoDataAccessException { log.debug("****1a. inside submitToGradingService "); String submissionId = ""; HashSet<ItemGradingData> itemGradingHash = new HashSet<ItemGradingData>(); // daisyf decoding: get page contents contains SectionContentsBean, a // wrapper for SectionDataIfc Iterator<SectionContentsBean> iter = delivery.getPageContents().getPartsContents() .iterator(); log.debug("****1b. inside submitToGradingService, iter= " + iter); HashSet<ItemGradingData> adds = new HashSet<ItemGradingData>(); HashSet<ItemGradingData> removes = new HashSet<ItemGradingData>(); // we go through all the answer collected from JSF form per each // publsihedItem and // work out which answer is an new addition and in cases like // MC/MCMR/Survey, we will // discard any existing one and just save the new one. For other // question type, we // simply modify the publishedText or publishedAnswer of the existing // ones. while (iter.hasNext()) { SectionContentsBean part = iter.next(); log.debug("****1c. inside submitToGradingService, part " + part); Iterator<ItemContentsBean> iter2 = part.getItemContents().iterator(); while (iter2.hasNext()) { // go through each item from form ItemContentsBean item = iter2.next(); log.debug("****** before prepareItemGradingPerItem"); prepareItemGradingPerItem(ae, delivery, item, adds, removes); log.debug("****** after prepareItemGradingPerItem"); } } AssessmentGradingData adata = persistAssessmentGrading(ae, delivery, itemGradingHash, publishedAssessment, adds, removes, invalidFINMap, invalidSALengthList); StringBuffer redrawAnchorName = new StringBuffer("p"); String tmpAnchorName = ""; Iterator<SectionContentsBean> iterPart = delivery.getPageContents().getPartsContents().iterator(); while (iterPart.hasNext()) { SectionContentsBean part = iterPart.next(); String partSeq = part.getNumber(); Iterator<ItemContentsBean> iterItem = part.getItemContents().iterator(); while (iterItem.hasNext()) { // go through each item from form ItemContentsBean item = iterItem.next(); String itemSeq = item.getSequence(); Long itemId = item.getItemData().getItemId(); if (item.getItemData().getTypeId() == 5) { if (invalidSALengthList.contains(itemId)) { item.setIsInvalidSALengthInput(true); redrawAnchorName.append(partSeq); redrawAnchorName.append("q"); redrawAnchorName.append(itemSeq); if (tmpAnchorName.equals("") || tmpAnchorName.compareToIgnoreCase(redrawAnchorName.toString()) > 0) { tmpAnchorName = redrawAnchorName.toString(); } } else { item.setIsInvalidSALengthInput(false); } } else if (item.getItemData().getTypeId() == 11) { if (invalidFINMap.containsKey(itemId)) { item.setIsInvalidFinInput(true); redrawAnchorName.append(partSeq); redrawAnchorName.append("q"); redrawAnchorName.append(itemSeq); if (tmpAnchorName.equals("") || tmpAnchorName.compareToIgnoreCase(redrawAnchorName.toString()) > 0) { tmpAnchorName = redrawAnchorName.toString(); } ArrayList list = (ArrayList) invalidFINMap.get(itemId); ArrayList<FinBean> finArray = item.getFinArray(); Iterator<FinBean> iterFin = finArray.iterator(); while (iterFin.hasNext()) { FinBean finBean = iterFin.next(); if (finBean.getItemGradingData() != null) { Long itemGradingId = finBean.getItemGradingData().getItemGradingId(); if (list.contains(itemGradingId)) { finBean.setIsCorrect(false); } } } } else { item.setIsInvalidFinInput(false); } } } } if (tmpAnchorName != null && !tmpAnchorName.equals("")) { delivery.setRedrawAnchorName(tmpAnchorName.toString()); } else { delivery.setRedrawAnchorName(""); } delivery.setSubmissionId(submissionId); delivery.setSubmissionTicket(submissionId);// is this the same thing? // hmmmm delivery.setSubmissionDate(new Date()); delivery.setSubmitted(true); return adata; } private AssessmentGradingData persistAssessmentGrading(ActionEvent ae, DeliveryBean delivery, HashSet<ItemGradingData> itemGradingHash, PublishedAssessmentFacade publishedAssessment, HashSet<ItemGradingData> adds, HashSet<ItemGradingData> removes, HashMap invalidFINMap, ArrayList invalidSALengthList) throws FinFormatException, SamigoDataAccessException { AssessmentGradingData adata = null; if (delivery.getAssessmentGrading() != null) { adata = delivery.getAssessmentGrading(); } GradingService service = new GradingService(); log.debug("**adata=" + adata); if (adata == null) { // <--- this shouldn't happened 'cos it should // have been created by BeginDelivery adata = makeNewAssessmentGrading(publishedAssessment, delivery, itemGradingHash); delivery.setAssessmentGrading(adata); } else { // 1. add all the new itemgrading for MC/Survey and discard any // itemgrading for MC/Survey // 2. add any modified SAQ/TF/FIB/Matching/MCMR/FIN // 3. save any modified Mark for Review in FileUplaod/Audio HashMap<Long, ItemDataIfc> fibMap = getFIBMap(publishedAssessment); HashMap<Long, ItemDataIfc> finMap = getFINMap(publishedAssessment); HashMap<Long, ItemDataIfc> calcQuestionMap = getCalcQuestionMap(publishedAssessment); // CALCULATED_QUESTION HashMap<Long, ItemDataIfc> mcmrMap = getMCMRMap(publishedAssessment); Set<ItemGradingData> itemGradingSet = adata.getItemGradingSet(); log.debug("*** 2a. before removal & addition " + (new Date())); if (itemGradingSet != null) { log.debug("*** 2aa. removing old itemGrading " + (new Date())); itemGradingSet.removeAll(removes); service.deleteAll(removes); // refresh itemGradingSet & assessmentGrading after removal log.debug("*** 2ab. reload itemGradingSet " + (new Date())); itemGradingSet = service.getItemGradingSet(adata .getAssessmentGradingId().toString()); log.debug("*** 2ac. load assessmentGarding " + (new Date())); adata = service.load(adata.getAssessmentGradingId().toString(), false); Iterator<ItemGradingData> iter = adds.iterator(); while (iter.hasNext()) { iter.next().setAssessmentGradingId(adata .getAssessmentGradingId()); } // make update to old item and insert new item // and we will only update item that has been changed log .debug("*** 2ad. set assessmentGrading with new/updated itemGrading " + (new Date())); log .debug("Submitforgrading: before calling .....................oldItemGradingSet.size = " + itemGradingSet.size()); log.debug("Submitforgrading: newItemGradingSet.size = " + adds.size()); HashSet<ItemGradingData> updateItemGradingSet = getUpdateItemGradingSet( itemGradingSet, adds, fibMap, finMap, calcQuestionMap,mcmrMap, adata); adata.setItemGradingSet(updateItemGradingSet); } } adata.setIsLate(isLate(publishedAssessment)); adata.setForGrade(Boolean.valueOf(delivery.getForGrade())); // If this assessment grading data has been updated (comments or adj. score) by grader and then republic and allow student to resubmit // when the student submit his answers, we update the status back to 0 and remove the grading entry/info. if (AssessmentGradingData.ASSESSMENT_UPDATED_NEED_RESUBMIT.equals(adata.getStatus()) || AssessmentGradingData.ASSESSMENT_UPDATED.equals(adata.getStatus())) { adata.setStatus(Integer.valueOf(0)); adata.setGradedBy(null); adata.setGradedDate(null); adata.setComments(null); adata.setTotalOverrideScore(Double.valueOf(0d)); } log.debug("*** 2b. before storingGrades, did all the removes and adds " + (new Date())); if (delivery.getNavigation().equals("1") && ae != null && "showFeedback".equals(ae.getComponent().getId())) { log.debug("Do not persist to db if it is linear access and the action is show feedback"); // 3. let's build three HashMap with (publishedItemId, publishedItem), // (publishedItemTextId, publishedItem), (publishedAnswerId, // publishedItem) to help with storing grades to adata only, not db HashMap publishedItemHash = delivery.getPublishedItemHash(); HashMap publishedItemTextHash = delivery.getPublishedItemTextHash(); HashMap publishedAnswerHash = delivery.getPublishedAnswerHash(); service.storeGrades(adata, publishedAssessment, publishedItemHash, publishedItemTextHash, publishedAnswerHash, false, invalidFINMap, invalidSALengthList); } else { log.debug("Persist to db otherwise"); // The following line seems redundant. I cannot see a reason why we need to save the adata here // and then again in following service.storeGrades(). Comment it out. //service.saveOrUpdateAssessmentGrading(adata); log.debug("*** 3. before storingGrades, did all the removes and adds " + (new Date())); // 3. let's build three HashMap with (publishedItemId, publishedItem), // (publishedItemTextId, publishedItem), (publishedAnswerId, // publishedItem) to help with storing grades to adata and then persist to DB HashMap publishedItemHash = delivery.getPublishedItemHash(); HashMap publishedItemTextHash = delivery.getPublishedItemTextHash(); HashMap publishedAnswerHash = delivery.getPublishedAnswerHash(); service.storeGrades(adata, publishedAssessment, publishedItemHash, publishedItemTextHash, publishedAnswerHash, invalidFINMap, invalidSALengthList); } return adata; } private HashMap<Long, ItemDataIfc> getFIBMap(PublishedAssessmentIfc publishedAssessment) { return publishedAssesmentService.prepareFIBItemHash(publishedAssessment); } private HashMap<Long, ItemDataIfc> getFINMap(PublishedAssessmentIfc publishedAssessment){ return publishedAssesmentService.prepareFINItemHash(publishedAssessment); } /** * CALCULATED_QUESTION * @param publishedAssessment * @return map of calc items */ private HashMap<Long, ItemDataIfc> getCalcQuestionMap(PublishedAssessmentIfc publishedAssessment){ return (HashMap<Long, ItemDataIfc>) publishedAssesmentService.prepareCalcQuestionItemHash(publishedAssessment); } private HashMap<Long, ItemDataIfc> getMCMRMap(PublishedAssessmentIfc publishedAssessment) { return publishedAssesmentService.prepareMCMRItemHash(publishedAssessment); } private HashSet<ItemGradingData> getUpdateItemGradingSet(Set oldItemGradingSet, Set<ItemGradingData> newItemGradingSet, HashMap<Long, ItemDataIfc> fibMap, HashMap<Long, ItemDataIfc> finMap, HashMap<Long, ItemDataIfc> calcQuestionMap, HashMap<Long, ItemDataIfc> mcmrMap, AssessmentGradingData adata) { log.debug("Submitforgrading: oldItemGradingSet.size = " + oldItemGradingSet.size()); log.debug("Submitforgrading: newItemGradingSet.size = " + newItemGradingSet.size()); HashSet<ItemGradingData> updateItemGradingSet = new HashSet<ItemGradingData>(); Iterator iter = oldItemGradingSet.iterator(); HashMap<Long, ItemGradingData> map = new HashMap<Long, ItemGradingData>(); while (iter.hasNext()) { // create a map with old itemGrading ItemGradingData item = (ItemGradingData) iter.next(); map.put(item.getItemGradingId(), item); } // go through new itemGrading Iterator<ItemGradingData> iter1 = newItemGradingSet.iterator(); while (iter1.hasNext()) { ItemGradingData newItem = iter1.next(); ItemGradingData oldItem = map.get(newItem .getItemGradingId()); if (oldItem != null) { // itemGrading exists and value has been change, then need // update Boolean oldReview = oldItem.getReview(); Boolean newReview = newItem.getReview(); Long oldAnswerId = oldItem.getPublishedAnswerId(); Long newAnswerId = newItem.getPublishedAnswerId(); String oldRationale = oldItem.getRationale(); String newRationale = TextFormat.convertPlaintextToFormattedTextNoHighUnicode(log, newItem.getRationale()); String oldAnswerText = oldItem.getAnswerText(); // Change to allow student submissions in rich-text [SAK-17021] String newAnswerText = ContextUtil.stringWYSIWYG(newItem.getAnswerText()); if ((oldReview != null && !oldReview.equals(newReview)) || (newReview!=null && !newReview.equals(oldReview)) || (oldAnswerId != null && !oldAnswerId .equals(newAnswerId)) || (newAnswerId != null && !newAnswerId .equals(oldAnswerId)) || (oldRationale != null && !oldRationale .equals(newRationale)) || (newRationale != null && !newRationale .equals(oldRationale)) || (oldAnswerText != null && !oldAnswerText .equals(newAnswerText)) || (newAnswerText != null && !newAnswerText .equals(oldAnswerText)) || fibMap.get(oldItem.getPublishedItemId()) != null || finMap.get(oldItem.getPublishedItemId())!=null || calcQuestionMap.get(oldItem.getPublishedItemId())!=null || mcmrMap.get(oldItem.getPublishedItemId()) != null) { oldItem.setReview(newItem.getReview()); oldItem.setPublishedAnswerId(newItem.getPublishedAnswerId()); oldItem.setRationale(newRationale); oldItem.setAnswerText(newAnswerText); oldItem.setSubmittedDate(new Date()); oldItem.setAutoScore(newItem.getAutoScore()); oldItem.setOverrideScore(newItem.getOverrideScore()); updateItemGradingSet.add(oldItem); // log.debug("**** SubmitToGrading: need update // "+oldItem.getItemGradingId()); } } else { // itemGrading from new set doesn't exist, add to set in // this case // log.debug("**** SubmitToGrading: need add new item"); //a new item should always have the grading ID set to null newItem.setItemGradingId(null); newItem.setAgentId(adata.getAgentId()); updateItemGradingSet.add(newItem); } } return updateItemGradingSet; } /** * Make a new AssessmentGradingData object for delivery * * @param publishedAssessment * the PublishedAssessmentFacade * @param delivery * the DeliveryBean * @param itemGradingHash * the item data * @return */ private AssessmentGradingData makeNewAssessmentGrading( PublishedAssessmentFacade publishedAssessment, DeliveryBean delivery, HashSet<ItemGradingData> itemGradingHash) { PersonBean person = (PersonBean) ContextUtil.lookupBean("person"); AssessmentGradingData adata = new AssessmentGradingData(); adata.setAgentId(person.getId()); adata.setPublishedAssessmentId(publishedAssessment .getPublishedAssessmentId()); adata.setForGrade(Boolean.valueOf(delivery.getForGrade())); adata.setItemGradingSet(itemGradingHash); adata.setAttemptDate(new Date()); adata.setIsLate(Boolean.FALSE); adata.setStatus(Integer.valueOf(0)); adata.setTotalOverrideScore(Double.valueOf(0)); adata.setTimeElapsed(Integer.valueOf("0")); return adata; } /* * This is specific to JSF - question for each type is layout differently in * JSF and the answers submitted are being collected differently too. e.g. * for each MC/Survey/MCMR, an itemgrading is associated with each choice. * whereas there is only one itemgrading per each question for SAQ/TF/Audio, * and one for each blank in FIB. To understand the logic in this method, it * is best to study jsf/delivery/item/deliver*.jsp */ private void prepareItemGradingPerItem(ActionEvent ae, DeliveryBean delivery, ItemContentsBean item, HashSet<ItemGradingData> adds, HashSet<ItemGradingData> removes) { ArrayList<ItemGradingData> grading = item.getItemGradingDataArray(); int typeId = item.getItemData().getTypeId().intValue(); //no matter what kinds of type questions, if it marks as review, add it in. for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); if (itemgrading.getItemGradingId() == null && (itemgrading.getReview() != null && itemgrading.getReview().booleanValue()) == true) { adds.addAll(grading); break; } } // 1. add all the new itemgrading for MC/Survey and discard any // itemgrading for MC/Survey // 2. add any modified SAQ/TF/FIB/Matching/MCMR/Audio/FIN switch (typeId) { case 1: // MC case 12: // MC Single Selection case 3: // Survey boolean answerModified = false; for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); if (itemgrading.getItemGradingId() == null || itemgrading.getItemGradingId().intValue() <= 0) { // => // new answer if (itemgrading.getPublishedAnswerId() != null || (itemgrading.getRationale() != null && !itemgrading.getRationale().trim().equals(""))) { answerModified = true; break; } } } // Click the Reset Selection link if(item.getUnanswered()) { answerModified = true; } if (answerModified) { for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading .get(m); if (itemgrading.getItemGradingId() != null && itemgrading.getItemGradingId().intValue() > 0) { // remove all old answer for MC & Surevy removes.add(itemgrading); } else { // add new answer if (itemgrading.getPublishedAnswerId() != null || itemgrading.getAnswerText() != null || (itemgrading.getRationale() != null && !itemgrading.getRationale().trim().equals(""))) { // null=> skipping this question itemgrading.setAgentId(AgentFacade.getAgentString()); itemgrading.setSubmittedDate(new Date()); if (itemgrading.getRationale() != null && itemgrading.getRationale().length() > 0) { itemgrading.setRationale(TextFormat.convertPlaintextToFormattedTextNoHighUnicode(log, itemgrading.getRationale())); } // the rest of the info is collected by // ItemContentsBean via JSF form adds.add(itemgrading); } } } } else{ handleMarkForReview(grading, adds); } break; case 4: // T/F case 9: // Matching for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); itemgrading.setAgentId(AgentFacade.getAgentString()); itemgrading.setSubmittedDate(new Date()); } for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); if ((itemgrading.getItemGradingId() != null && itemgrading.getItemGradingId().intValue() > 0) || (itemgrading.getPublishedAnswerId() != null || itemgrading.getAnswerText() != null) || (itemgrading.getRationale() != null && !itemgrading.getRationale().trim().equals(""))) { adds.addAll(grading); break; } } break; case 5: // SAQ for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); itemgrading.setAgentId(AgentFacade.getAgentString()); itemgrading.setSubmittedDate(new Date()); } for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); if (itemgrading.getItemGradingId() != null && itemgrading.getItemGradingId().intValue() > 0) { adds.addAll(grading); break; } else if (itemgrading.getAnswerText() != null && !itemgrading.getAnswerText().equals("")) { // Change to allow student submissions in rich-text [SAK-17021] itemgrading.setAnswerText(ContextUtil.stringWYSIWYG(itemgrading.getAnswerText())); adds.addAll(grading); break; } } break; case 8: // FIB case 15: // CALCULATED_QUESTION case 11: // FIN boolean addedToAdds = false; for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); itemgrading.setAgentId(AgentFacade.getAgentString()); itemgrading.setSubmittedDate(new Date()); } for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); if (itemgrading.getItemGradingId() != null && itemgrading.getItemGradingId().intValue() > 0) { adds.addAll(grading); break; } else if (itemgrading.getAnswerText() != null && !itemgrading.getAnswerText().equals("")) { String s = itemgrading.getAnswerText(); log.debug("s = " + s); // Change to allow student submissions in rich-text [SAK-17021] itemgrading.setAnswerText(ContextUtil.stringWYSIWYG(s)); adds.addAll(grading); if (!addedToAdds) { adds.addAll(grading); addedToAdds = true; } } } break; case 2: // MCMR for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); if (itemgrading.getItemGradingId() != null && itemgrading.getItemGradingId().intValue() > 0) { // old answer, check which one to keep, not keeping null answer if (itemgrading.getPublishedAnswerId() != null || (itemgrading.getRationale() != null && !itemgrading.getRationale().trim().equals(""))) { itemgrading.setAgentId(AgentFacade.getAgentString()); itemgrading.setSubmittedDate(new Date()); adds.add(itemgrading); } else { removes.add(itemgrading); } } else { // new answer if (itemgrading.getPublishedAnswerId() != null || (itemgrading.getRationale() != null && !itemgrading.getRationale().trim().equals(""))) { // new addition not accepting any new answer with null for MCMR itemgrading.setAgentId(AgentFacade.getAgentString()); itemgrading.setSubmittedDate(new Date()); adds.add(itemgrading); } } } break; case 6: // File Upload case 7: // Audio handleMarkForReview(grading, adds); break; case 13: //Matrix Choices question answerModified = false; for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); if (itemgrading != null) { log.debug("\n:ItemId>>itemTextId>>answerId "+ itemgrading.getPublishedItemId()+itemgrading.getPublishedItemTextId()+itemgrading.getPublishedAnswerId()+"\n"); itemgrading.setAgentId(AgentFacade.getAgentString()); itemgrading.setSubmittedDate(new Date()); if (itemgrading.getRationale() != null && itemgrading.getRationale().length() > 0) { itemgrading.setRationale(TextFormat.convertPlaintextToFormattedTextNoHighUnicode(log, itemgrading.getRationale())); } adds.add(itemgrading); } } break; } // if it is linear access and there is not answer, we add an fake ItemGradingData String actionCommand = ""; if (ae != null) { actionCommand = ae.getComponent().getId(); log.debug("ae is not null, getActionCommand() = " + actionCommand); } else { log.debug("ae is null"); } if ("1".equals(delivery.getNavigation()) && adds.size() ==0 && !"showFeedback".equals(actionCommand)) { log.debug("enter here"); Long assessmentGradingId = delivery.getAssessmentGrading().getAssessmentGradingId(); Long publishedItemId = item.getItemData().getItemId(); log.debug("assessmentGradingId = " + assessmentGradingId); log.debug("publishedItemId = " + publishedItemId); GradingService gradingService = new GradingService(); if (gradingService.getItemGradingData(assessmentGradingId.toString(), publishedItemId.toString()) == null) { log.debug("Create a new (fake) ItemGradingData"); ItemGradingData itemGrading = new ItemGradingData(); itemGrading.setAssessmentGradingId(assessmentGradingId); itemGrading.setAgentId(AgentFacade.getAgentString()); itemGrading.setPublishedItemId(publishedItemId); ItemService itemService = new ItemService(); Long itemTextId = itemService.getItemTextId(publishedItemId); log.debug("itemTextId = " + itemTextId); if(itemTextId != -1){ itemGrading.setPublishedItemTextId(itemTextId); adds.add(itemGrading); } } else { // For File Upload question, if user clicks on "Upload", a ItemGradingData will be created. // Therefore, when user clicks on "Next", we shouldn't create it again. // Same for Audio question, if user records anything, a ItemGradingData will be created. // We don't create it again when user clicks on "Next". if ((typeId == 6 || typeId == 7)) { log.debug("File Upload or Audio! Do not create empty ItemGradingData if there exists one"); } } } } private void handleMarkForReview(ArrayList<ItemGradingData> grading, HashSet<ItemGradingData> adds){ for (int m = 0; m < grading.size(); m++) { ItemGradingData itemgrading = grading.get(m); if (itemgrading.getItemGradingId() != null && itemgrading.getItemGradingId().intValue() > 0 && itemgrading.getReview() != null) { // we will save itemgrading even though answer was not modified // 'cos mark for review may have been modified adds.add(itemgrading); } } } private Boolean isLate(PublishedAssessmentIfc pub) { AssessmentAccessControlIfc a = pub.getAssessmentAccessControl(); if (a.getDueDate() != null && a.getDueDate().before(new Date())) return Boolean.TRUE; else return Boolean.FALSE; } private LRS_Statement getStatementForGradedAssessment(AssessmentGradingData gradingData, LRS_Actor student, PublishedAssessmentFacade publishedAssessment) { LRS_Verb verb = new LRS_Verb(SAKAI_VERB.scored); LRS_Object lrsObject = new LRS_Object(ServerConfigurationService.getPortalUrl() + "/assessment", "received-grade-assessment"); HashMap<String, String> nameMap = new HashMap<String, String>(); nameMap.put("en-US", "User received a grade"); lrsObject.setActivityName(nameMap); HashMap<String, String> descMap = new HashMap<String, String>(); descMap.put("en-US", "User received a grade for their assessment: " + publishedAssessment.getTitle() + "; Submitted: " + (gradingData.getIsLate() ? "late" : "on time")); lrsObject.setDescription(descMap); LRS_Context context = new LRS_Context("other", "assessment"); LRS_Statement statement = new LRS_Statement(student, verb, lrsObject, getLRS_Result(gradingData, publishedAssessment), context); return statement; } private LRS_Result getLRS_Result(AssessmentGradingData gradingData, PublishedAssessmentFacade publishedAssessment) { double score = gradingData.getFinalScore(); LRS_Result result = new LRS_Result(new Float(score), new Float(0.0), new Float(publishedAssessment.getTotalScore()), null); result.setCompletion(true); return result; } }