package org.sakaiproject.tool.assessment.ui.listener.evaluation; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; 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 javax.faces.component.html.HtmlSelectOneMenu; import javax.faces.context.FacesContext; import javax.faces.event.AbortProcessingException; import javax.faces.event.ActionEvent; import javax.faces.event.ActionListener; import javax.faces.event.ValueChangeEvent; import javax.faces.event.ValueChangeListener; import javax.servlet.http.HttpServletRequest; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.tool.assessment.api.SamigoApiFactory; import org.sakaiproject.tool.assessment.data.dao.grading.AssessmentGradingComparatorByScoreAndUniqueIdentifier; import org.sakaiproject.tool.assessment.data.dao.grading.AssessmentGradingData; import org.sakaiproject.tool.assessment.data.dao.grading.ItemGradingData; import org.sakaiproject.tool.assessment.data.ifc.assessment.AnswerIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemDataIfc; 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.assessment.SectionDataIfc; import org.sakaiproject.tool.assessment.facade.AgentFacade; import org.sakaiproject.tool.assessment.services.GradingService; import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService; import org.sakaiproject.tool.assessment.shared.api.assessment.SecureDeliveryServiceAPI; import org.sakaiproject.tool.assessment.shared.api.assessment.SecureDeliveryServiceAPI.Phase; import org.sakaiproject.tool.assessment.shared.api.assessment.SecureDeliveryServiceAPI.PhaseStatus; import org.sakaiproject.tool.assessment.ui.bean.delivery.DeliveryBean; import org.sakaiproject.tool.assessment.ui.bean.evaluation.ExportResponsesBean; import org.sakaiproject.tool.assessment.ui.bean.evaluation.HistogramBarBean; import org.sakaiproject.tool.assessment.ui.bean.evaluation.HistogramQuestionScoresBean; import org.sakaiproject.tool.assessment.ui.bean.evaluation.HistogramScoresBean; import org.sakaiproject.tool.assessment.ui.bean.evaluation.ItemBarBean; import org.sakaiproject.tool.assessment.ui.bean.evaluation.QuestionScoresBean; import org.sakaiproject.tool.assessment.ui.bean.evaluation.TotalScoresBean; import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil; import org.sakaiproject.util.ResourceLoader; /** * <p> * This handles the selection of the Histogram Aggregate Statistics. * </p> * * 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. * * @version $Id: HistogramListener.java 131058 2013-11-01 17:04:40Z ktsao@stanford.edu $ */ public class HistogramListener implements ActionListener, ValueChangeListener { private static Log log = LogFactory.getLog(HistogramListener.class); //private static BeanSort bs; //private static ContextUtil cu; //private static EvaluationListenerUtil util; /** * Standard process action method. * @param ae ActionEvent * @throws AbortProcessingException */ public void processAction(ActionEvent ae) throws AbortProcessingException { log.debug("HistogramListener.processAction()"); TotalScoresBean totalBean = (TotalScoresBean) ContextUtil.lookupBean( "totalScores"); HistogramScoresBean bean = (HistogramScoresBean) ContextUtil.lookupBean( "histogramScores"); if (!histogramScores(bean, totalBean)) { String publishedId = totalBean.getPublishedId(); if (publishedId.equals("0")) { publishedId = (String) ContextUtil.lookupParam("publishedAssessmentId"); } log.error("Error getting statistics for assessment with published id = " + publishedId); FacesContext context = FacesContext.getCurrentInstance(); // reset histogramScoresBean, otherwise the previous assessment viewed is displayed. // note that createValueBinding seems to be deprecated and replaced by a new method in 1.2. Might need to modify this later FacesContext.getCurrentInstance().getApplication().createValueBinding("#{histogramScores}").setValue(FacesContext.getCurrentInstance(), null ); return ; } } /** * Process a value change. */ public void processValueChange(ValueChangeEvent event) { if(!HtmlSelectOneMenu.class.isInstance(event.getSource()) || event.getNewValue() == null || event.getNewValue().equals(event.getOldValue())){ return; } HtmlSelectOneMenu selectOneMenu = HtmlSelectOneMenu.class.cast(event.getSource()); if(selectOneMenu.getId() != null && selectOneMenu.getId().startsWith("allSubmissions")){ processAllSubmissionsChange(event); } } public void processAllSubmissionsChange(ValueChangeEvent event) { TotalScoresBean totalBean = (TotalScoresBean) ContextUtil.lookupBean( "totalScores"); HistogramScoresBean bean = (HistogramScoresBean) ContextUtil.lookupBean( "histogramScores"); QuestionScoresBean questionBean = (QuestionScoresBean) ContextUtil.lookupBean("questionScores"); String selectedvalue= (String) event.getNewValue(); if ((selectedvalue!=null) && (!selectedvalue.equals("")) ){ log.debug("changed submission pulldown "); bean.setAllSubmissions(selectedvalue); // changed for histogram score bean totalBean.setAllSubmissions(selectedvalue); // changed for total score bean questionBean.setAllSubmissions(selectedvalue); // changed for Question score bean } if (!histogramScores(bean, totalBean)) { String publishedId = totalBean.getPublishedId(); if (publishedId.equals("0")) { publishedId = (String) ContextUtil.lookupParam("publishedAssessmentId"); } log.error("Error getting statistics for assessment with published id = " + publishedId); FacesContext context = FacesContext.getCurrentInstance(); FacesContext.getCurrentInstance().getApplication().createValueBinding( "#{histogramScores}").setValue(FacesContext.getCurrentInstance(), null ); return ; } } /** * modified by gopalrc Nov 2007 * * Calculate the detailed statistics * * This will populate the HistogramScoresBean with the data associated with the * particular versioned assessment based on the publishedId. * * Some of this code will change when we move this to Hibernate persistence. * @param publishedId String * @param histogramScores TotalScoresBean * @return boolean true if successful */ public boolean histogramScores(HistogramScoresBean histogramScores, TotalScoresBean totalScores) { DeliveryBean delivery = (DeliveryBean) ContextUtil.lookupBean("delivery"); String publishedId = totalScores.getPublishedId(); if (publishedId.equals("0")) { publishedId = (String) ContextUtil.lookupParam("publishedAssessmentId"); } String actionString = ContextUtil.lookupParam("actionString"); // See if this can fix SAK-16437 if (actionString != null && !actionString.equals("reviewAssessment")){ // Shouldn't come to here. The action should either be null or reviewAssessment. // If we can confirm this is where causes SAK-16437, ask UX for a new screen with warning message. log.error("SAK-16437 happens!! publishedId = " + publishedId + ", agentId = " + AgentFacade.getAgentString()); } ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AuthorMessages"); ResourceLoader rbEval = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.EvaluationMessages"); String assessmentName = ""; // gopalrc Dec 2007 histogramScores.clearLowerQuartileStudents(); histogramScores.clearUpperQuartileStudents(); String which = histogramScores.getAllSubmissions(); if (which == null && totalScores.getAllSubmissions() != null) { // use totalscore's selection which = totalScores.getAllSubmissions(); histogramScores.setAllSubmissions(which); // changed submission pulldown } histogramScores.setItemId(ContextUtil.lookupParam("itemId")); histogramScores.setHasNav(ContextUtil.lookupParam("hasNav")); GradingService delegate = new GradingService(); PublishedAssessmentService pubService = new PublishedAssessmentService(); List<AssessmentGradingData> allscores = delegate.getTotalScores(publishedId, which); //set the ItemGradingData manually here. or we cannot //retrieve it later. for(AssessmentGradingData agd: allscores){ agd.setItemGradingSet(delegate.getItemGradingSet(String.valueOf(agd.getAssessmentGradingId()))); } if (allscores.size() == 0) { // Similar case in Bug 1537, but clicking Statistics link instead of assignment title. // Therefore, redirect the the same page. delivery.setOutcome("reviewAssessmentError"); delivery.setActionString(actionString); return true; } histogramScores.setPublishedId(publishedId); int callerName = TotalScoresBean.CALLED_FROM_HISTOGRAM_LISTENER; String isFromStudent = (String) ContextUtil.lookupParam("isFromStudent"); if (isFromStudent != null && "true".equals(isFromStudent)) { callerName = TotalScoresBean.CALLED_FROM_HISTOGRAM_LISTENER_STUDENT; } // get the Map of all users(keyed on userid) belong to the selected sections // now we only include scores of users belong to the selected sections Map useridMap = null; ArrayList scores = new ArrayList(); // only do section filter if it's published to authenticated users if (totalScores.getReleaseToAnonymous()) { scores.addAll(allscores); } else { useridMap = totalScores.getUserIdMap(callerName); Iterator allscores_iter = allscores.iterator(); while (allscores_iter.hasNext()) { AssessmentGradingData data = (AssessmentGradingData) allscores_iter.next(); String agentid = data.getAgentId(); if (useridMap.containsKey(agentid)) { scores.add(data); } } } Iterator iter = scores.iterator(); //log.info("Has this many agents: " + scores.size()); if (!iter.hasNext()){ log.info("Students who have submitted may have been removed from this site"); return false; } /* * gopalrc - moved up from (1) */ // here scores contain AssessmentGradingData Map assessmentMap = getAssessmentStatisticsMap(scores); /* * gopalrc Nov 2007 * find students in upper and lower quartiles * of assessment scores */ ArrayList submissionsSortedForDiscrim = new ArrayList(scores); boolean anonymous = Boolean.valueOf(totalScores.getAnonymous()).booleanValue(); Collections.sort(submissionsSortedForDiscrim, new AssessmentGradingComparatorByScoreAndUniqueIdentifier(anonymous)); int numSubmissions = scores.size(); //int percent27 = ((numSubmissions*10*27/100)+5)/10; // rounded int percent27 = numSubmissions*27/100; // rounded down if (percent27 == 0) percent27 = 1; // gopalrc - check this - only relevant for very small samples //double q1 = Double.valueOf((String) assessmentMap.get("q1")).doubleValue(); //double q3 = Double.valueOf((String) assessmentMap.get("q3")).doubleValue(); for (int i=0; i<percent27; i++) { histogramScores.addToLowerQuartileStudents(((AssessmentGradingData) submissionsSortedForDiscrim.get(i)).getAgentId()); histogramScores.addToUpperQuartileStudents(((AssessmentGradingData) submissionsSortedForDiscrim.get(numSubmissions-1-i)).getAgentId()); } PublishedAssessmentIfc pub = (PublishedAssessmentIfc) pubService.getPublishedAssessment(publishedId, false); if (pub != null) { if (actionString != null && actionString.equals("reviewAssessment")){ if (AssessmentIfc.RETRACT_FOR_EDIT_STATUS.equals(pub.getStatus())) { // Bug 1547: If this is during review and the assessment is retracted for edit now, // set the outcome to isRetractedForEdit2 error page. delivery.setOutcome("isRetractedForEdit2"); delivery.setActionString(actionString); return true; } else { delivery.setOutcome("histogramScores"); delivery.setSecureDeliveryHTMLFragment( "" ); delivery.setBlockDelivery( false ); SecureDeliveryServiceAPI secureDelivery = SamigoApiFactory.getInstance().getSecureDeliveryServiceAPI(); if ( secureDelivery.isSecureDeliveryAvaliable() ) { String moduleId = pub.getAssessmentMetaDataByLabel( SecureDeliveryServiceAPI.MODULE_KEY ); if ( moduleId != null && ! SecureDeliveryServiceAPI.NONE_ID.equals( moduleId ) ) { HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); PhaseStatus status = secureDelivery.validatePhase(moduleId, Phase.ASSESSMENT_REVIEW, pub, request ); delivery.setSecureDeliveryHTMLFragment( secureDelivery.getHTMLFragment(moduleId, pub, request, Phase.ASSESSMENT_REVIEW, status, new ResourceLoader().getLocale() ) ); if ( PhaseStatus.FAILURE == status ) { delivery.setOutcome( "secureDeliveryError" ); delivery.setActionString(actionString); delivery.setBlockDelivery( true ); return true; } } } } } assessmentName = pub.getTitle(); ArrayList parts = pub.getSectionArraySorted(); histogramScores.setAssesmentParts(parts); ArrayList info = new ArrayList(); Iterator partsIter = parts.iterator(); int secseq = 1; double totalpossible = 0; boolean hasRandompart = false; boolean isRandompart = false; String poolName = null; HashMap itemScoresMap = delegate.getItemScores(Long.valueOf(publishedId), Long.valueOf(0), which); HashMap itemScores = new HashMap(); if (totalScores.getReleaseToAnonymous()) { // skip section filter if it's published to anonymous users itemScores.putAll(itemScoresMap); } else { if (useridMap == null) { useridMap = totalScores.getUserIdMap(callerName); } for (Iterator it = itemScoresMap.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); Long itemId = (Long) entry.getKey(); ArrayList itemScoresList = (ArrayList) entry.getValue(); ArrayList filteredItemScoresList = new ArrayList(); Iterator itemScoresIter = itemScoresList.iterator(); // get the Map of all users(keyed on userid) belong to the // selected sections while (itemScoresIter.hasNext()) { ItemGradingData idata = (ItemGradingData) itemScoresIter.next(); String agentid = idata.getAgentId(); if (useridMap.containsKey(agentid)) { filteredItemScoresList.add(idata); } } itemScores.put(itemId, filteredItemScoresList); } } // Iterate through the assessment parts while (partsIter.hasNext()) { SectionDataIfc section = (SectionDataIfc) partsIter.next(); String authortype = section .getSectionMetaDataByLabel(SectionDataIfc.AUTHOR_TYPE); try{ if (SectionDataIfc.RANDOM_DRAW_FROM_QUESTIONPOOL .equals(Integer.valueOf(authortype))) { hasRandompart = true; isRandompart = true; poolName = section .getSectionMetaDataByLabel(SectionDataIfc.POOLNAME_FOR_RANDOM_DRAW); } else { isRandompart = false; poolName = null; } }catch(NumberFormatException e){ isRandompart = false; poolName = null; } if (section.getSequence() == null) section.setSequence(Integer.valueOf(secseq++)); String title = rb.getString("p") + " " + section.getSequence().toString(); title += ", " + rb.getString("q") + " "; ArrayList itemset = section.getItemArraySortedForGrading(); int seq = 1; Iterator itemsIter = itemset.iterator(); // Iterate through the assessment questions (items) while (itemsIter.hasNext()) { HistogramQuestionScoresBean questionScores = new HistogramQuestionScoresBean(); questionScores.setNumberOfParts(parts.size()); // gopalrc //if this part is a randompart , then set randompart = true questionScores.setRandomType(isRandompart); questionScores.setPoolName(poolName); ItemDataIfc item = (ItemDataIfc) itemsIter.next(); //String type = delegate.getTextForId(item.getTypeId()); String type = getType(item.getTypeId().intValue()); if (item.getSequence() == null) item.setSequence(Integer.valueOf(seq++)); questionScores.setPartNumber( section.getSequence().toString()); //set the question label depending on random pools and parts if(questionScores.getRandomType() && poolName != null){ if(questionScores.getNumberOfParts() > 1){ questionScores.setQuestionLabelFormat(rb.getString("label_question_part_pool", null)); }else{ questionScores.setQuestionLabelFormat(rb.getString("label_question_pool", null)); } }else{ if(questionScores.getNumberOfParts() > 1){ questionScores.setQuestionLabelFormat(rb.getString("label_question_part", null)); }else{ questionScores.setQuestionLabelFormat(rb.getString("label_question", null)); } } questionScores.setQuestionNumber( item.getSequence().toString()); questionScores.setItemId(item.getItemId()); questionScores.setTitle(title + item.getSequence().toString() + " (" + type + ")"); questionScores.setQuestionText(item.getText()); questionScores.setQuestionType(item.getTypeId().toString()); //totalpossible = totalpossible + item.getScore().doubleValue(); //ArrayList responses = null; determineResults(pub, questionScores, (ArrayList) itemScores .get(item.getItemId())); questionScores.setTotalScore(item.getScore().toString()); // below - gopalrc Nov 2007 questionScores.setN(""+numSubmissions); questionScores.setItemId(item.getItemId()); Set studentsWithAllCorrect = questionScores.getStudentsWithAllCorrect(); Set studentsResponded = questionScores.getStudentsResponded(); if (studentsWithAllCorrect == null || studentsResponded == null || studentsWithAllCorrect.isEmpty() || studentsResponded.isEmpty()) { questionScores.setPercentCorrectFromUpperQuartileStudents("0"); questionScores.setPercentCorrectFromLowerQuartileStudents("0"); questionScores.setDiscrimination("0.0"); } else { int percent27ForThisQuestion = percent27; Set<String> upperQuartileStudents = histogramScores.getUpperQuartileStudents().keySet(); Set<String> lowerQuartileStudents = histogramScores.getLowerQuartileStudents().keySet(); if(isRandompart){ //we need to calculate the 27% upper and lower //per question for the people that actually answered //this question. upperQuartileStudents = new HashSet<String>(); lowerQuartileStudents = new HashSet<String>(); percent27ForThisQuestion = questionScores.getNumResponses()*27/100; if (percent27ForThisQuestion == 0) percent27ForThisQuestion = 1; if(questionScores.getNumResponses() != 0){ //need to only get gradings for students that answered this question List<AssessmentGradingData> filteredGradings = filterGradingData(submissionsSortedForDiscrim, questionScores.getItemId()); for (int i = 0; i < percent27ForThisQuestion; i++) { lowerQuartileStudents.add(((AssessmentGradingData) filteredGradings.get(i)).getAgentId()); // upperQuartileStudents.add(((AssessmentGradingData) filteredGradings.get(questionScores.getNumResponses()-1-i)).getAgentId()); } } } if(questionScores.getNumResponses() != 0){ int numStudentsWithAllCorrectFromUpperQuartile = 0; int numStudentsWithAllCorrectFromLowerQuartile = 0; Iterator studentsIter = studentsWithAllCorrect.iterator(); while (studentsIter.hasNext()) { String agentId = (String) studentsIter.next(); if (upperQuartileStudents.contains(agentId)) { numStudentsWithAllCorrectFromUpperQuartile++; } if (lowerQuartileStudents.contains(agentId)) { numStudentsWithAllCorrectFromLowerQuartile++; } } double percentCorrectFromUpperQuartileStudents = ((double) numStudentsWithAllCorrectFromUpperQuartile / (double) percent27ForThisQuestion) * 100d; double percentCorrectFromLowerQuartileStudents = ((double) numStudentsWithAllCorrectFromLowerQuartile / (double) percent27ForThisQuestion) * 100d; questionScores.setPercentCorrectFromUpperQuartileStudents( Integer.toString((int) percentCorrectFromUpperQuartileStudents)); questionScores.setPercentCorrectFromLowerQuartileStudents( Integer.toString((int) percentCorrectFromLowerQuartileStudents)); double discrimination = ((double)numStudentsWithAllCorrectFromUpperQuartile - (double)numStudentsWithAllCorrectFromLowerQuartile)/(double)percent27ForThisQuestion ; // round to 2 decimals if (discrimination > 999999 || discrimination < -999999) { questionScores.setDiscrimination("NaN"); } else { discrimination = ((int) (discrimination*100.00d)) / 100.00d; questionScores.setDiscrimination(Double.toString(discrimination)); } }else{ questionScores.setPercentCorrectFromUpperQuartileStudents(rbEval.getString("na")); questionScores.setPercentCorrectFromLowerQuartileStudents(rbEval.getString("na")); questionScores.setDiscrimination(rbEval.getString("na")); } } // above - gopalrc Nov 2007 info.add(questionScores); } // end-while - items totalpossible = pub.getTotalScore().doubleValue(); } // end-while - parts histogramScores.setInfo(info); histogramScores.setRandomType(hasRandompart); // below - gopalrc Dec 2007 int maxNumOfAnswers = 0; ArrayList detailedStatistics = new ArrayList(); Iterator infoIter = info.iterator(); while (infoIter.hasNext()) { HistogramQuestionScoresBean questionScores = (HistogramQuestionScoresBean)infoIter.next(); if (questionScores.getQuestionType().equals("1") || questionScores.getQuestionType().equals("2") || questionScores.getQuestionType().equals("3") || questionScores.getQuestionType().equals("4") || questionScores.getQuestionType().equals("8") || questionScores.getQuestionType().equals("9") || questionScores.getQuestionType().equals("11") || questionScores.getQuestionType().equals("12") || questionScores.getQuestionType().equals("15") ) { detailedStatistics.add(questionScores); if (questionScores.getHistogramBars() != null) { maxNumOfAnswers = questionScores.getHistogramBars().length >maxNumOfAnswers ? questionScores.getHistogramBars().length : maxNumOfAnswers; } } } histogramScores.setDetailedStatistics(detailedStatistics); histogramScores.setMaxNumberOfAnswers(maxNumOfAnswers); // above - gopalrc Dec 2007 /* * gopalrc - moved up (1) // here scores contain AssessmentGradingData Map assessmentMap = getAssessmentStatisticsMap(scores); */ // test to see if it gets back empty map if (assessmentMap.isEmpty()) { histogramScores.setNumResponses(0); } try { BeanUtils.populate(histogramScores, assessmentMap); // quartiles don't seem to be working, workaround histogramScores.setQ1((String) assessmentMap.get("q1")); histogramScores.setQ2((String) assessmentMap.get("q2")); histogramScores.setQ3((String) assessmentMap.get("q3")); histogramScores.setQ4((String) assessmentMap.get("q4")); histogramScores.setTotalScore((String) assessmentMap .get("totalScore")); histogramScores.setTotalPossibleScore(Double .toString(totalpossible)); HistogramBarBean[] bars = new HistogramBarBean[histogramScores .getColumnHeight().length]; for (int i = 0; i < histogramScores.getColumnHeight().length; i++) { bars[i] = new HistogramBarBean(); bars[i] .setColumnHeight(Integer .toString(histogramScores .getColumnHeight()[i])); bars[i].setNumStudents(histogramScores .getNumStudentCollection()[i]); bars[i].setRangeInfo(histogramScores .getRangeCollection()[i]); //log.info("Set bar " + i + ": " + bean.getColumnHeight()[i] + ", " + bean.getNumStudentCollection()[i] + ", " + bean.getRangeCollection()[i]); } histogramScores.setHistogramBars(bars); /////////////////////////////////////////////////////////// // START DEBUGGING /* log.info("TESTING ASSESSMENT MAP"); log.info("assessmentMap: =>"); log.info(assessmentMap); log.info("--------------------------------------------"); log.info("TESTING TOTALS HISTOGRAM FORM"); log.info( "HistogramScoresForm Form: =>\n" + "bean.getMean()=" + bean.getMean() + "\n" + "bean.getColumnHeight()[0] (first elem)=" + bean.getColumnHeight()[0] + "\n" + "bean.getInterval()=" + bean.getInterval() + "\n" + "bean.getLowerQuartile()=" + bean.getLowerQuartile() + "\n" + "bean.getMaxScore()=" + bean.getMaxScore() + "\n" + "bean.getMean()=" + bean.getMean() + "\n" + "bean.getMedian()=" + bean.getMedian() + "\n" + "bean.getNumResponses()=" + bean.getNumResponses() + "\n" + "bean.getNumStudentCollection()=" + bean.getNumStudentCollection() + "\n" + "bean.getQ1()=" + bean.getQ1() + "\n" + "bean.getQ2()=" + bean.getQ2() + "\n" + "bean.getQ3()=" + bean.getQ3() + "\n" + "bean.getQ4()=" + bean.getQ4()); log.info("--------------------------------------------"); */ // END DEBUGGING CODE /////////////////////////////////////////////////////////// } catch (IllegalAccessException e) { log.warn("IllegalAccessException: unable to populate bean" + e); } catch (InvocationTargetException e) { log.warn("InvocationTargetException: unable to populate bean" + e); } histogramScores.setAssessmentName(assessmentName); } else { log.error("pub is null. publishedId = " + publishedId); return false; } return true; } private void determineResults(PublishedAssessmentIfc pub, HistogramQuestionScoresBean qbean, ArrayList<ItemGradingData> itemScores) { if (itemScores == null) itemScores = new ArrayList<ItemGradingData>(); int responses = 0; Set<Long> assessmentGradingIds = new HashSet<Long>(); int numStudentsWithZeroAnswers = 0; for (ItemGradingData itemGradingData: itemScores) { //only count the unique questions answers if(!assessmentGradingIds.contains(itemGradingData.getAssessmentGradingId())){ responses++; assessmentGradingIds.add(itemGradingData.getAssessmentGradingId()); if (itemGradingData.getSubmittedDate() == null) { numStudentsWithZeroAnswers++; } } } qbean.setNumResponses(responses); qbean.setNumberOfStudentsWithZeroAnswers(numStudentsWithZeroAnswers); if (qbean.getQuestionType().equals("1") || // mcsc qbean.getQuestionType().equals("2") || // mcmcms qbean.getQuestionType().equals("12") || // mcmcss qbean.getQuestionType().equals("3") || // mc survey qbean.getQuestionType().equals("4") || // tf qbean.getQuestionType().equals("9") || // matching qbean.getQuestionType().equals("8") || // Fill in the blank qbean.getQuestionType().equals("11") || // Numeric Response qbean.getQuestionType().equals("15") || // CALCULATED_QUESTION qbean.getQuestionType().equals("13")) // matrix survey doAnswerStatistics(pub, qbean, itemScores); if (qbean.getQuestionType().equals("5") || // essay qbean.getQuestionType().equals("6") || // file upload qbean.getQuestionType().equals("7")) // audio recording doScoreStatistics(qbean, itemScores); } private void doAnswerStatistics(PublishedAssessmentIfc pub, HistogramQuestionScoresBean qbean, ArrayList scores) { // Don't return here. This will cause questions to be displayed inconsistently on the stats page // if (scores.isEmpty()) // { // qbean.setHistogramBars(new HistogramBarBean[0]); // qbean.setNumResponses(0); // qbean.setPercentCorrect(rb.getString("no_responses")); // return; // } PublishedAssessmentService pubService = new PublishedAssessmentService(); //build a hashMap (publishedItemId, publishedItem) HashMap publishedItemHash = pubService.preparePublishedItemHash(pub); HashMap publishedItemTextHash = pubService.preparePublishedItemTextHash(pub); HashMap publishedAnswerHash = pubService.preparePublishedAnswerHash(pub); //int numAnswers = 0; ItemDataIfc item = (ItemDataIfc) publishedItemHash.get(qbean.getItemId()); ArrayList text = item.getItemTextArraySorted(); ArrayList answers = null; if (!qbean.getQuestionType().equals("9")) // matching { ItemTextIfc firstText = (ItemTextIfc) publishedItemTextHash.get(((ItemTextIfc) text.toArray()[0]).getId()); answers = firstText.getAnswerArraySorted(); } if (qbean.getQuestionType().equals("1")) // mcsc getTFMCScores(publishedAnswerHash, scores, qbean, answers); else if (qbean.getQuestionType().equals("2")) // mcmc getFIBMCMCScores(publishedItemHash, publishedAnswerHash, scores, qbean, answers); else if (qbean.getQuestionType().equals("12")) getTFMCScores(publishedAnswerHash, scores, qbean, answers); else if (qbean.getQuestionType().equals("3")) getTFMCScores(publishedAnswerHash, scores, qbean, answers); else if (qbean.getQuestionType().equals("4")) // tf getTFMCScores(publishedAnswerHash, scores, qbean, answers); else if ((qbean.getQuestionType().equals("8"))||(qbean.getQuestionType().equals("11")) ) getFIBMCMCScores(publishedItemHash, publishedAnswerHash, scores, qbean, answers); //else if (qbean.getQuestionType().equals("11")) // getFINMCMCScores(publishedItemHash, publishedAnswerHash, scores, qbean, answers); else if (qbean.getQuestionType().equals("9")) getMatchingScores(publishedItemTextHash, publishedAnswerHash, scores, qbean, text); else if (qbean.getQuestionType().equals("15")) // CALCULATED_QUESTION getCalculatedQuestionScores(scores, qbean, text); else if (qbean.getQuestionType().equals("13")) // matrix survey question getMatrixSurveyScores(publishedItemTextHash, publishedAnswerHash, scores, qbean, text); } private void getFIBMCMCScores(HashMap publishedItemHash, HashMap publishedAnswerHash, ArrayList scores, HistogramQuestionScoresBean qbean, ArrayList answers) { ResourceLoader rb = new ResourceLoader( "org.sakaiproject.tool.assessment.bundle.EvaluationMessages"); HashMap texts = new HashMap(); Iterator iter = answers.iterator(); HashMap results = new HashMap(); HashMap numStudentRespondedMap = new HashMap(); HashMap sequenceMap = new HashMap(); while (iter.hasNext()) { AnswerIfc answer = (AnswerIfc) iter.next(); texts.put(answer.getId(), answer); results.put(answer.getId(), Integer.valueOf(0)); sequenceMap.put(answer.getSequence(), answer.getId()); } iter = scores.iterator(); while (iter.hasNext()) { ItemGradingData data = (ItemGradingData) iter.next(); AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(data .getPublishedAnswerId()); if (answer != null) { // log.info("Rachel: looking for " + answer.getId()); // found a response Integer num = null; // num is a counter try { // we found a response, now get existing count from the // hashmap num = (Integer) results.get(answer.getId()); } catch (Exception e) { log.warn("No results for " + answer.getId()); } if (num == null) num = Integer.valueOf(0); ArrayList studentResponseList = (ArrayList) numStudentRespondedMap .get(data.getAssessmentGradingId()); if (studentResponseList == null) { studentResponseList = new ArrayList(); } studentResponseList.add(data); numStudentRespondedMap.put(data.getAssessmentGradingId(), studentResponseList); // we found a response, and got the existing num , now update // one if ((qbean.getQuestionType().equals("8")) || (qbean.getQuestionType().equals("11"))) { // for fib we only count the number of correct responses Double autoscore = data.getAutoScore(); if (!(Double.valueOf(0)).equals(autoscore)) { results.put(answer.getId(), Integer.valueOf( num.intValue() + 1)); } } else { // for mc, we count the number of all responses results .put(answer.getId(), Integer.valueOf(num.intValue() + 1)); } } } HistogramBarBean[] bars = new HistogramBarBean[results.keySet().size()]; int[] numarray = new int[results.keySet().size()]; ArrayList sequenceList = new ArrayList(); iter = answers.iterator(); while (iter.hasNext()) { AnswerIfc answer = (AnswerIfc) iter.next(); sequenceList.add(answer.getSequence()); } Collections.sort(sequenceList); // iter = results.keySet().iterator(); iter = sequenceList.iterator(); int i = 0; int correctresponses = 0; while (iter.hasNext()) { Long sequenceId = (Long) iter.next(); Long answerId = (Long) sequenceMap.get(sequenceId); AnswerIfc answer = (AnswerIfc) texts.get(answerId); int num = ((Integer) results.get(answerId)).intValue(); numarray[i] = num; bars[i] = new HistogramBarBean(); if (answer != null) bars[i].setLabel(answer.getText()); // this doens't not apply to fib , do not show checkmarks for FIB if (!(qbean.getQuestionType().equals("8")) && !(qbean.getQuestionType().equals("11")) && answer != null) { bars[i].setIsCorrect(answer.getIsCorrect()); } if ((num > 1) || (num == 0)) { bars[i].setNumStudentsText(num + " " + rb.getString("responses")); } else { bars[i] .setNumStudentsText(num + " " + rb.getString("response")); } bars[i].setNumStudents(num); i++; } for (Iterator it = numStudentRespondedMap.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); ArrayList resultsForOneStudent = (ArrayList) entry.getValue(); boolean hasIncorrect = false; Iterator listiter = resultsForOneStudent.iterator(); // iterate through the results for one student // for this question (qbean) while (listiter.hasNext()) { ItemGradingData item = (ItemGradingData) listiter.next(); if ((qbean.getQuestionType().equals("8")) || (qbean.getQuestionType().equals("11"))) { // TODO: we are checking to see if the score is > 0, this // will not work if the question is worth 0 points. // will need to verify each answer individually. Double autoscore = item.getAutoScore(); if ((Double.valueOf(0)).equals(autoscore)) { hasIncorrect = true; break; } } else if (qbean.getQuestionType().equals("2")) { // mcmc // only answered choices are created in the // ItemGradingData_T, so we need to check // if # of checkboxes the student checked is == the number // of correct answers // otherwise if a student only checked one of the multiple // correct answers, // it would count as a correct response try { ArrayList itemTextArray = ((ItemDataIfc) publishedItemHash .get(item.getPublishedItemId())) .getItemTextArraySorted(); ArrayList answerArray = ((ItemTextIfc) itemTextArray .get(0)).getAnswerArraySorted(); int corranswers = 0; Iterator answeriter = answerArray.iterator(); while (answeriter.hasNext()) { AnswerIfc answerchoice = (AnswerIfc) answeriter .next(); if (answerchoice.getIsCorrect().booleanValue()) { corranswers++; } } if (resultsForOneStudent.size() != corranswers) { hasIncorrect = true; break; } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException( "error calculating mcmc question."); } // now check each answer in MCMC AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(item .getPublishedAnswerId()); if (answer != null && (answer.getIsCorrect() == null || (!answer .getIsCorrect().booleanValue()))) { hasIncorrect = true; break; } } } if (!hasIncorrect) { correctresponses = correctresponses + 1; // gopalrc - Nov 2007 qbean.addStudentWithAllCorrect(((ItemGradingData)resultsForOneStudent.get(0)).getAgentId()); } // gopalrc - Dec 2007 qbean.addStudentResponded(((ItemGradingData)resultsForOneStudent.get(0)).getAgentId()); } // NEW int[] heights = calColumnHeight(numarray, qbean.getNumResponses()); // int[] heights = calColumnHeight(numarray); for (i = 0; i < bars.length; i++) { try { bars[i].setColumnHeight(Integer.toString(heights[i])); } catch(NullPointerException npe) { log.warn("bars[" + i + "] is null. " + npe); } } qbean.setHistogramBars(bars); if (qbean.getNumResponses() > 0) qbean .setPercentCorrect(Integer .toString((int) (((double) correctresponses / (double) qbean.getNumResponses()) * 100))); } /* * private void getFINMCMCScores(HashMap publishedItemHash, HashMap * publishedAnswerHash, ArrayList scores, HistogramQuestionScoresBean qbean, * ArrayList answers) { HashMap texts = new HashMap(); Iterator iter = * answers.iterator(); HashMap results = new HashMap(); HashMap * numStudentRespondedMap= new HashMap(); while (iter.hasNext()) { AnswerIfc * answer = (AnswerIfc) iter.next(); texts.put(answer.getId(), answer); * results.put(answer.getId(), Integer.valueOf(0)); } iter = scores.iterator(); * while (iter.hasNext()) { ItemGradingData data = (ItemGradingData) * iter.next(); AnswerIfc answer = (AnswerIfc) * publishedAnswerHash.get(data.getPublishedAnswerId()); if (answer != null) { * //log.info("Rachel: looking for " + answer.getId()); // found a response * Integer num = null; // num is a counter try { // we found a response, now * get existing count from the hashmap num = (Integer) * results.get(answer.getId()); * * } catch (Exception e) { log.warn("No results for " + answer.getId()); } * if (num == null) num = Integer.valueOf(0); * * ArrayList studentResponseList = * (ArrayList)numStudentRespondedMap.get(data.getAssessmentGradingId()); if * (studentResponseList==null) { studentResponseList = new ArrayList(); } * studentResponseList.add(data); * numStudentRespondedMap.put(data.getAssessmentGradingId(), * studentResponseList); // we found a response, and got the existing num , * now update one if (qbean.getQuestionType().equals("11")) { // for fib we * only count the number of correct responses Double autoscore = * data.getAutoScore(); if (!(new Double(0)).equals(autoscore)) { * results.put(answer.getId(), Integer.valueOf(num.intValue() + 1)); } } else { // * for mc, we count the number of all responses results.put(answer.getId(), * Integer.valueOf(num.intValue() + 1)); } } } HistogramBarBean[] bars = new * HistogramBarBean[results.keySet().size()]; int[] numarray = new * int[results.keySet().size()]; iter = results.keySet().iterator(); int i = * 0; int responses = 0; int correctresponses = 0; while (iter.hasNext()) { * Long answerId = (Long) iter.next(); AnswerIfc answer = (AnswerIfc) * texts.get(answerId); int num = ((Integer) * results.get(answerId)).intValue(); numarray[i] = num; bars[i] = new * HistogramBarBean(); if(answer != null) * bars[i].setLabel(answer.getText()); * // this doens't not apply to fib , do not show checkmarks for FIB if * (!qbean.getQuestionType().equals("11") && answer != null) { * bars[i].setIsCorrect(answer.getIsCorrect()); } * * * if ((num>1)||(num==0)) { bars[i].setNumStudentsText(num + " Responses"); } * else { bars[i].setNumStudentsText(num + " Response"); * } bars[i].setNumStudents(num); i++; } * * * responses = numStudentRespondedMap.size(); Iterator mapiter = * numStudentRespondedMap.keySet().iterator(); while (mapiter.hasNext()) { * Long assessmentGradingId= (Long)mapiter.next(); ArrayList * resultsForOneStudent = * (ArrayList)numStudentRespondedMap.get(assessmentGradingId); boolean * hasIncorrect = false; Iterator listiter = * resultsForOneStudent.iterator(); while (listiter.hasNext()) { * ItemGradingData item = (ItemGradingData)listiter.next(); if * (qbean.getQuestionType().equals("11")) { Double autoscore = * item.getAutoScore(); if (!(new Double(0)).equals(autoscore)) { * hasIncorrect = true; break; } } else if * (qbean.getQuestionType().equals("2")) { * // only answered choices are created in the ItemGradingData_T, so we * need to check // if # of checkboxes the student checked is == the number * of correct answers // otherwise if a student only checked one of the * multiple correct answers, // it would count as a correct response * * try { ArrayList itemTextArray = * ((ItemDataIfc)publishedItemHash.get(item.getPublishedItemId())).getItemTextArraySorted(); * ArrayList answerArray = * ((ItemTextIfc)itemTextArray.get(0)).getAnswerArraySorted(); * * int corranswers = 0; Iterator answeriter = answerArray.iterator(); while * (answeriter.hasNext()){ AnswerIfc answerchoice = (AnswerIfc) * answeriter.next(); if (answerchoice.getIsCorrect().booleanValue()){ * corranswers++; } } if (resultsForOneStudent.size() != corranswers){ * hasIncorrect = true; break; } } catch (Exception e) { * e.printStackTrace(); throw new RuntimeException("error calculating mcmc * question."); } * // now check each answer in MCMC * * AnswerIfc answer = (AnswerIfc) * publishedAnswerHash.get(item.getPublishedAnswerId()); if ( answer != null && * (answer.getIsCorrect() == null || * (!answer.getIsCorrect().booleanValue()))) { hasIncorrect = true; break; } } } * if (!hasIncorrect) { correctresponses = correctresponses + 1; } } //NEW * int[] heights = calColumnHeight(numarray,responses); // int[] heights = * calColumnHeight(numarray); for (i=0; i<bars.length; i++) * bars[i].setColumnHeight(Integer.toString(heights[i])); * qbean.setHistogramBars(bars); qbean.setNumResponses(responses); if * (responses > 0) qbean.setPercentCorrect(Integer.toString((int)(((double) * correctresponses/(double) responses) * 100))); } */ private void getTFMCScores(HashMap publishedAnswerHash, ArrayList scores, HistogramQuestionScoresBean qbean, ArrayList answers) { ResourceLoader rb = new ResourceLoader( "org.sakaiproject.tool.assessment.bundle.EvaluationMessages"); HashMap texts = new HashMap(); Iterator iter = answers.iterator(); HashMap results = new HashMap(); HashMap sequenceMap = new HashMap(); // create the lookup maps while (iter.hasNext()) { AnswerIfc answer = (AnswerIfc) iter.next(); texts.put(answer.getId(), answer); results.put(answer.getId(), Integer.valueOf(0)); sequenceMap.put(answer.getSequence(), answer.getId()); } // find the number of responses (ItemGradingData) for each answer iter = scores.iterator(); while (iter.hasNext()) { ItemGradingData data = (ItemGradingData) iter.next(); AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(data .getPublishedAnswerId()); if (answer != null) { // log.info("Rachel: looking for " + answer.getId()); // found a response Integer num = null; // num is a counter try { // we found a response, now get existing count from the // hashmap num = (Integer) results.get(answer.getId()); } catch (Exception e) { log.warn("No results for " + answer.getId()); e.printStackTrace(); } if (num == null) num = Integer.valueOf(0); // we found a response, and got the existing num , now update // one // check here for the other bug about non-autograded items // having 1 even with no responses results.put(answer.getId(), Integer.valueOf(num.intValue() + 1)); // gopalrc - Nov 2007 // this should work because for tf/mc(single) // questions, there should be at most // one submitted answer per student/assessment if (answer.getIsCorrect() != null && answer.getIsCorrect().booleanValue()) { qbean.addStudentWithAllCorrect(data.getAgentId()); } // gopalrc - Nov 2007 qbean.addStudentResponded(data.getAgentId()); } } HistogramBarBean[] bars = new HistogramBarBean[results.keySet().size()]; int[] numarray = new int[results.keySet().size()]; ArrayList sequenceList = new ArrayList(); // get an arraylist of answer sequences iter = answers.iterator(); while (iter.hasNext()) { AnswerIfc answer = (AnswerIfc) iter.next(); sequenceList.add(answer.getSequence()); } // sort the sequences Collections.sort(sequenceList); iter = sequenceList.iterator(); // iter = results.keySet().iterator(); int i = 0; int correctresponses = 0; // find answers sorted by sequence while (iter.hasNext()) { Long sequenceId = (Long) iter.next(); Long answerId = (Long) sequenceMap.get(sequenceId); AnswerIfc answer = (AnswerIfc) texts.get(answerId); int num = ((Integer) results.get(answerId)).intValue(); // set i to be the sequence, so that the answer choices will be in // the right order on Statistics page , see Bug SAM-440 i = answer.getSequence().intValue() - 1; numarray[i] = num; bars[i] = new HistogramBarBean(); if (qbean.getQuestionType().equals("4")) { // true-false String origText = answer.getText(); String text = ""; if ("true".equals(origText)) { text = rb.getString("true_msg"); } else { text = rb.getString("false_msg"); } bars[i].setLabel(text); } else { bars[i].setLabel(answer.getText()); } bars[i].setIsCorrect(answer.getIsCorrect()); if ((num > 1) || (num == 0)) { bars[i].setNumStudentsText(num + " " + rb.getString("responses")); } else { bars[i] .setNumStudentsText(num + " " + rb.getString("response")); } bars[i].setNumStudents(num); if (answer.getIsCorrect() != null && answer.getIsCorrect().booleanValue()) { correctresponses += num; } // i++; } // NEW int[] heights = calColumnHeight(numarray, qbean.getNumResponses()); // int[] heights = calColumnHeight(numarray); for (i = 0; i < bars.length; i++) { try { bars[i].setColumnHeight(Integer.toString(heights[i])); } catch (NullPointerException npe) { log.warn("bars[" + i + "] is null. " + npe); } } qbean.setHistogramBars(bars); if (qbean.getNumResponses() > 0) qbean .setPercentCorrect(Integer .toString((int) (((double) correctresponses / (double) qbean.getNumResponses()) * 100))); } private void getCalculatedQuestionScores(List<ItemGradingData> scores, HistogramQuestionScoresBean qbean, ArrayList labels) { final String CORRECT = "Correct"; final String INCORRECT = "Incorrect"; final int COLUMN_MAX_HEIGHT = 600; ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.EvaluationMessages"); ResourceLoader rc = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.CommonMessages"); // count incorrect and correct to support column height calculation Map<String, Integer> results = new HashMap<String, Integer>(); results.put(CORRECT, Integer.valueOf(0)); results.put(INCORRECT, Integer.valueOf(0)); for (ItemGradingData score : scores) { if (score.getAutoScore() > 0) { Integer value = results.get(CORRECT); results.put(CORRECT, ++value); } else { Integer value = results.get(INCORRECT); results.put(INCORRECT, ++value); } } // build the histogram bar for correct/incorrect answers List<HistogramBarBean> barList = new ArrayList<HistogramBarBean>(); for (Map.Entry<String, Integer> entry : results.entrySet()) { HistogramBarBean bar = new HistogramBarBean(); bar.setLabel(entry.getKey()); bar.setNumStudents(entry.getValue()); if (entry.getValue() > 1) { bar.setNumStudentsText(entry.getValue() + " " + rb.getString("correct_responses")); } else { bar.setNumStudentsText(entry.getValue() + " " + rc.getString("correct_response")); } bar.setNumStudentsText(entry.getValue() + " " + entry.getKey()); bar.setIsCorrect(entry.getKey().equals(CORRECT)); int height = 0; if (scores.size() > 0) { height = COLUMN_MAX_HEIGHT * entry.getValue() / scores.size(); } bar.setColumnHeight(Integer.toString(height)); barList.add(bar); } HistogramBarBean[] bars = new HistogramBarBean[barList.size()]; bars = barList.toArray(bars); qbean.setHistogramBars(bars); // store any assessment grading ID's that are incorrect. // this will allow us to calculate % Students All correct by giving // us a count of assessmnets that had an incorrect answer Set<Long> assessmentQuestionIncorrect = new HashSet<Long>(); for (ItemGradingData score : scores) { if (score.getAutoScore() == 0) { assessmentQuestionIncorrect.add(score.getAssessmentGradingId()); } } if (qbean.getNumResponses() > 0) { int correct = qbean.getNumResponses() - assessmentQuestionIncorrect.size(); int total = qbean.getNumResponses(); double percentCorrect = ((double) correct / (double) total) * 100; String percentCorrectStr = Integer.toString((int)percentCorrect); qbean.setPercentCorrect(percentCorrectStr); } } private void getMatchingScores(HashMap publishedItemTextHash, HashMap publishedAnswerHash, ArrayList scores, HistogramQuestionScoresBean qbean, ArrayList labels) { ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.EvaluationMessages"); ResourceLoader rc = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.CommonMessages"); HashMap texts = new HashMap(); Iterator iter = labels.iterator(); HashMap results = new HashMap(); HashMap numStudentRespondedMap= new HashMap(); HashMap sequenceMap = new HashMap(); while (iter.hasNext()) { ItemTextIfc label = (ItemTextIfc) iter.next(); texts.put(label.getId(), label); results.put(label.getId(), Integer.valueOf(0)); sequenceMap.put(label.getSequence(), label.getId()); } iter = scores.iterator(); while (iter.hasNext()) { ItemGradingData data = (ItemGradingData) iter.next(); ItemTextIfc text = (ItemTextIfc) publishedItemTextHash.get(data.getPublishedItemTextId()); AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(data.getPublishedAnswerId()); // if (answer.getIsCorrect() != null && answer.getIsCorrect().booleanValue()) if (answer != null) { Integer num = (Integer) results.get(text.getId()); if (num == null) num = Integer.valueOf(0); ArrayList studentResponseList = (ArrayList)numStudentRespondedMap.get(data.getAssessmentGradingId()); if (studentResponseList==null) { studentResponseList = new ArrayList(); } studentResponseList.add(data); numStudentRespondedMap.put(data.getAssessmentGradingId(), studentResponseList); if (answer.getIsCorrect() != null && answer.getIsCorrect().booleanValue()) // only store correct responses in the results { results.put(text.getId(), Integer.valueOf(num.intValue() + 1)); } } } HistogramBarBean[] bars = new HistogramBarBean[results.keySet().size()]; int[] numarray = new int[results.keySet().size()]; ArrayList sequenceList = new ArrayList(); iter = labels.iterator(); while (iter.hasNext()) { ItemTextIfc label = (ItemTextIfc) iter.next(); sequenceList.add(label.getSequence()); } Collections.sort(sequenceList); iter = sequenceList.iterator(); //iter = results.keySet().iterator(); int i = 0; int correctresponses = 0; while (iter.hasNext()) { Long sequenceId = (Long) iter.next(); Long textId = (Long) sequenceMap.get(sequenceId); ItemTextIfc text = (ItemTextIfc) texts.get(textId); int num = ((Integer) results.get(textId)).intValue(); numarray[i] = num; bars[i] = new HistogramBarBean(); bars[i].setLabel(text.getText()); bars[i].setNumStudents(num); if ((num>1)||(num==0)) { bars[i].setNumStudentsText(num + " " +rb.getString("correct_responses")); } else { bars[i].setNumStudentsText(num + " " +rc.getString("correct_response")); } i++; } // now calculate correctresponses // correctresponses = # of students who got all answers correct, for (Iterator it = numStudentRespondedMap.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); ArrayList resultsForOneStudent = (ArrayList) entry.getValue(); boolean hasIncorrect = false; Iterator listiter = resultsForOneStudent.iterator(); // numStudentRespondedMap only stores correct answers, so now we need to // check to see if # of rows in itemgradingdata_t == labels.size() // otherwise if a student only answered one correct answer and // skipped the rest, it would count as a correct response while (listiter.hasNext()) { ItemGradingData item = (ItemGradingData)listiter.next(); if (resultsForOneStudent.size()!= labels.size()){ hasIncorrect = true; break; } // now check each answer in Matching AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(item.getPublishedAnswerId()); if (answer.getIsCorrect() == null || (!answer.getIsCorrect().booleanValue())) { hasIncorrect = true; break; } } if (!hasIncorrect) { correctresponses = correctresponses + 1; // gopalrc - Nov 2007 qbean.addStudentWithAllCorrect(((ItemGradingData)resultsForOneStudent.get(0)).getAgentId()); } // gopalrc - Dec 2007 qbean.addStudentResponded(((ItemGradingData)resultsForOneStudent.get(0)).getAgentId()); } //NEW int[] heights = calColumnHeight(numarray, qbean.getNumResponses()); // int[] heights = calColumnHeight(numarray); for (i=0; i<bars.length; i++) { try { bars[i].setColumnHeight(Integer.toString(heights[i])); } catch (NullPointerException npe) { log.warn("bars[" + i + "] is null. " + npe); } } qbean.setHistogramBars(bars); if (qbean.getNumResponses() > 0) qbean.setPercentCorrect(Integer.toString((int)(((double) correctresponses/(double) qbean.getNumResponses()) * 100))); } private void getMatrixSurveyScores(HashMap publishedItemTextHash, HashMap publishedAnswerHash, ArrayList scores, HistogramQuestionScoresBean qbean, ArrayList labels) { HashMap texts = new HashMap(); HashMap rows = new HashMap(); HashMap answers = new HashMap(); HashMap numStudentRespondedMap = new HashMap(); Iterator iter = labels.iterator(); // create labels(rows) and HashMap , rows has the total count of response for that row while (iter.hasNext()) { ItemTextIfc label = (ItemTextIfc) iter.next(); texts.put(label.getId(), label); rows.put(label.getId(), Integer.valueOf(0)); // sequenceMap.put(label.getSequence(), label.getId()); } // log.info("kim debug: row size and texts size " + rows.keySet().size()+ " " + texts.keySet().size()); // result only contains the row information, I should have another HashMap to store the Answer results // find the number of responses (ItemGradingData) for each answer iter = scores.iterator(); while (iter.hasNext()) { ItemGradingData data = (ItemGradingData) iter.next(); AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(data .getPublishedAnswerId()); if (answer != null) { Integer num = null; // num is a counter try { // we found a response, now get existing count from the hashmap // num = (Integer) results.get(answer.getId()); num = (Integer) answers.get(answer.getId()); } catch (Exception e) { log.warn("No results for " + answer.getId()); log.error(e.getMessage()); } if (num == null) num = Integer.valueOf(0); answers.put(answer.getId(), Integer.valueOf(num.intValue() + 1)); Long id = ((ItemTextIfc)answer.getItemText()).getId(); Integer rCount = null; try { rCount = (Integer)rows.get(id); } catch (Exception e) { log.warn("No results for " + id); log.error(e.getMessage()); } if(rCount != null) rows.put(id, Integer.valueOf(rCount.intValue()+1)); } ArrayList studentResponseList = (ArrayList)numStudentRespondedMap.get(data.getAssessmentGradingId()); if (studentResponseList==null) { studentResponseList = new ArrayList(); } studentResponseList.add(data); numStudentRespondedMap.put(data.getAssessmentGradingId(), studentResponseList); qbean.addStudentResponded(data.getAgentId()); } //create the arraylist for answer text ArrayList answerTextList = new ArrayList<String>(); iter = publishedAnswerHash.keySet().iterator(); boolean isIn = false; while(iter.hasNext()){ Long id = (Long)iter.next(); AnswerIfc answer = (AnswerIfc) publishedAnswerHash.get(id); if (!qbean.getItemId().equals(answer.getItem().getItemId())) { continue; } isIn = false; //log.info("kim debug: publishedAnswerHash: key value " + id + answer.getText()); for(int i=0; i< answerTextList.size(); i++){ if((((String)answer.getText()).trim()).equals(((String)answerTextList.get(i)).trim())){ isIn = true; break; } } if(!isIn){ String ansStr = answer.getText().trim(); answerTextList.add(ansStr); } } //create the HistogramBarBean ArrayList<HistogramBarBean> histogramBarList = new ArrayList<HistogramBarBean>(); iter = texts.keySet().iterator(); while (iter.hasNext()){ Long id = (Long)iter.next(); HistogramBarBean gramBar = new HistogramBarBean(); ItemTextIfc ifc = (ItemTextIfc)texts.get(id); Integer totalCount = (Integer)rows.get(id); //log.info("kim debug: total count: " + totalCount.intValue()); //log.info("kim debug: row.next()" + ifc.getText()); gramBar.setLabel(ifc.getText()); //add each small beans ArrayList<ItemBarBean> itemBars = new ArrayList<ItemBarBean>(); for(int i=0; i< answerTextList.size(); i++){ ItemBarBean barBean = new ItemBarBean(); int count = 0; //get the count of checked //1. first get the answerId for ItemText+AnswerText combination Iterator iter1 = publishedAnswerHash.keySet().iterator(); while(iter1.hasNext()){ Long id1 = (Long)iter1.next(); AnswerIfc answer1 = (AnswerIfc)publishedAnswerHash.get(id1); //log.info("kim debug:answer1.getText() + ifc.getText()" + //answer1.getText() + answer1.getItemText().getText() + ifc.getText() + answer1.getId()); // bjones86 - SAM-2232 - null checks if( answer1 == null ) { continue; } ItemTextIfc answer1ItemText = answer1.getItemText(); if( answer1ItemText == null ) { continue; } String answer1Text = answer1.getText(); String answer1ItemTextText = answer1ItemText.getText(); if( answer1Text == null || answer1ItemTextText == null ) { continue; } String answerText = (String) answerTextList.get( i ); String ifcText = ifc.getText(); if(answer1Text.equals(answerText) && answer1ItemTextText.equals(ifcText)) { //2. then get the count from HashMap if(answers.containsKey(answer1.getId())) { count =((Integer) answers.get(answer1.getId())).intValue(); //log.info("kim debug: count " + count); break; } } } if (count > 1) barBean.setNumStudentsText(count + " responses"); else barBean.setNumStudentsText(count + " response"); //2. get the answer text barBean.setItemText((String)answerTextList.get(i)); //log.info("kim debug: getItemText " + barBean.getItemText()); //3. set the columnHeight int height= 0; if (totalCount.intValue() != 0) height = 300 * count/totalCount.intValue(); barBean.setColumnHeight(Integer.toString(height)); itemBars.add(barBean); } //log.info("kim debug: itemBars size: " +itemBars.size()); gramBar.setItemBars(itemBars); histogramBarList.add(gramBar); } //debug purpose //log.info("kim debug: histogramBarList size: " +histogramBarList.size()); qbean.setHistogramBars(histogramBarList.toArray(new HistogramBarBean[histogramBarList.size()])); qbean.setNumResponses(numStudentRespondedMap.size()); } private void doScoreStatistics(HistogramQuestionScoresBean qbean, ArrayList scores) { // here scores contain ItemGradingData Map assessmentMap = getAssessmentStatisticsMap(scores); // test to see if it gets back empty map if (assessmentMap.isEmpty()) { qbean.setNumResponses(0); } try { BeanUtils.populate(qbean, assessmentMap); // quartiles don't seem to be working, workaround qbean.setQ1( (String) assessmentMap.get("q1")); qbean.setQ2( (String) assessmentMap.get("q2")); qbean.setQ3( (String) assessmentMap.get("q3")); qbean.setQ4( (String) assessmentMap.get("q4")); //qbean.setTotalScore( (String) assessmentMap.get("maxScore")); HistogramBarBean[] bars = new HistogramBarBean[qbean.getColumnHeight().length]; // SAK-1933: if there is no response, do not show bars at all // do not check if assessmentMap is empty, because it's never empty. if (scores.size() == 0) { bars = new HistogramBarBean[0]; } else { for (int i=0; i<qbean.getColumnHeight().length; i++) { bars[i] = new HistogramBarBean(); bars[i].setColumnHeight (Integer.toString(qbean.getColumnHeight()[i])); bars[i].setNumStudents(qbean.getNumStudentCollection()[i]); if (qbean.getNumStudentCollection()[i]>1) { bars[i].setNumStudentsText(qbean.getNumStudentCollection()[i] + " Responses"); } else { bars[i].setNumStudentsText(qbean.getNumStudentCollection()[i] + " Response"); } // bars[i].setNumStudentsText(qbean.getNumStudentCollection()[i] + // " Responses"); bars[i].setRangeInfo(qbean.getRangeCollection()[i]); bars[i].setLabel(qbean.getRangeCollection()[i]); } } qbean.setHistogramBars(bars); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } private Map getAssessmentStatisticsMap(ArrayList scoreList) { // this function is used to calculate stats for an entire assessment // or for a non-autograded question // depending on data's instanceof Iterator iter = scoreList.iterator(); ArrayList floats = new ArrayList(); while (iter.hasNext()) { Object data = iter.next(); if (data instanceof AssessmentGradingData) { Double finalScore = ((AssessmentGradingData) data).getFinalScore(); if (finalScore == null) { finalScore = Double.valueOf("0"); } floats.add(finalScore); } else { double autoScore = (double) 0.0; if (((ItemGradingData) data).getAutoScore() != null) autoScore = ((ItemGradingData) data).getAutoScore().doubleValue(); double overrideScore = (double) 0.0; if (((ItemGradingData) data).getOverrideScore() != null) overrideScore = ((ItemGradingData) data).getOverrideScore().doubleValue(); floats.add(Double.valueOf(autoScore + overrideScore)); } } if (floats.isEmpty()) floats.add(new Double(0.0)); Object[] array = floats.toArray(); Arrays.sort(array); double[] scores = new double[array.length]; for (int i=0; i<array.length; i++) { scores[i] = ((Double) array[i]).doubleValue(); } HashMap statMap = new HashMap(); double min = scores[0]; double max = scores[scores.length - 1]; double total = calTotal(scores); double mean = calMean(scores, total); int interval = 0; interval = calInterval(scores, min, max); int[] numStudents = calNumStudents(scores, min, max, interval); statMap.put("maxScore", castingNum(max,2)); statMap.put("interval", Integer.valueOf(interval)); statMap.put("numResponses", Integer.valueOf(scoreList.size())); // statMap.put("numResponses", Integer.valueOf(scores.length)); statMap.put("totalScore",castingNum(total,2)); statMap.put("mean", castingNum(mean,2)); statMap.put("median", castingNum(calMedian(scores),2)); statMap.put("mode", castingNumForMode(calMode(scores))); statMap.put("numStudentCollection", numStudents); statMap.put( "rangeCollection", calRange(scores, numStudents, min, max, interval)); statMap.put("standDev", castingNum(calStandDev(scores, mean),2)); //NEW //statMap.put("columnHeight", calColumnHeight(numStudents)); statMap.put("columnHeight", calColumnHeight(numStudents,scoreList.size())); statMap.put("arrayLength", Integer.valueOf(numStudents.length)); statMap.put( "range", castingNum(scores[0],2) + " - " + castingNum(scores[scores.length - 1],2)); statMap.put("q1", castingNum(calQuartiles(scores, 0.25),2)); statMap.put("q2", castingNum(calQuartiles(scores, 0.5),2)); statMap.put("q3", castingNum(calQuartiles(scores, 0.75),2)); statMap.put("q4", castingNum(max,2)); return statMap; } /*** What follows is Huong Nguyen's statistics code. ***/ /*** We love you Huong! --rmg ***/ /** * Calculate the total score for all students * @param scores array of scores * @return the total */ private static double calTotal(double[] scores) { double total = 0; for(int i = 0; i < scores.length; i++) { total = total + scores[i]; } return total; } /** * Calculate mean. * * @param scores array of scores * @param total the total of all scores * * @return mean */ private static double calMean(double[] scores, double total) { return total / scores.length; } /** * Calculate median. * * @param scores array of scores * * @return median */ private static double calMedian(double[] scores) { double median; if(((scores.length) % 2) == 0) { median = (scores[(scores.length / 2)] + scores[(scores.length / 2) - 1]) / 2; } else { median = scores[(scores.length - 1) / 2]; } return median; } /** * Calculate mode * * @param scores array of scores * * @return mode */ private static String calMode(double[]scores){ // double[]scores={1,2,3,4,3,6,5,5,6}; Arrays.sort(scores); String maxString=""+scores[0]; int maxCount=1; int currentCount=1; for(int i=1;i<scores.length;i++){ if(!(""+scores[i]).equals(""+scores[i-1])){ currentCount=1; if(maxCount==currentCount) maxString=maxString+", "+scores[i]; } else{ currentCount++; if(maxCount==currentCount) maxString=maxString+", "+scores[i]; if(maxCount<currentCount){ maxString=""+scores[i]; maxCount=currentCount; } } } return maxString; } /** * Calculate standard Deviation * * @param scores array of scores * @param mean the mean * @param total the total * * @return the standard deviation */ private static double calStandDev(double[] scores, double mean) { double total = 0; for(int i = 0; i < scores.length; i++) { total = total + ((scores[i] - mean) * (scores[i] - mean)); } return Math.sqrt(total / (scores.length - 1)); } /** * Calculate the interval to use for histograms. * * @param scores array of scores * @param min the minimum score * @param max the maximum score * * @return the interval */ private static int calInterval(double[] scores, double min, double max) { int interval; if((max - min) < 10) { interval = 1; } else { interval = (int) Math.ceil((max - min) / 10); } return interval; } /** * Calculate the number for each answer. * * @param answers array of answers * * * @return array of number giving each answer. */ /* private static int[] calNum(String[] answers, String[] choices, String type) { int[] num = new int[choices.length]; for(int i = 0; i < answers.length; i++) { for(int j = 0; j < choices.length; j++) { if(type.equals("Multiple Correct Answer")) { // TODO: using Tokenizer because split() doesn't seem to work. StringTokenizer st = new StringTokenizer(answers[i], "|"); while(st.hasMoreTokens()) { String nt = st.nextToken(); if((nt.trim()).equals(choices[j].trim())) { num[j] = num[j] + 1; } } } else { if(answers[i].equals(choices[j])) { num[j] = num[j] + 1; } } } } return num; } */ /** * Calculate the number correct answer * * @param answers array of answers * @param correct the correct answer * * @return the number correct */ /* private int calCorrect(String[] answers, String correct) { int cal = 0; for(int i = 0; i < answers.length; i++) { if(answers[i].equals(correct)) { cal++; } } return cal; } */ /** * Calculate the number of students per interval for histograms. * * @param scores array of scores * @param min the minimum score * @param max the maximum score * @param interval the interval * * @return number of students per interval */ private static int[] calNumStudents( double[] scores, double min, double max, int interval) { if(min > max) { //log.info("max(" + max + ") <min(" + min + ")"); max = min; } int[] numStudents = new int[(int) Math.ceil((max - min) / interval)]; // this handles a case where there are no num students, treats as if // a single value of 0 if(numStudents.length == 0) { numStudents = new int[1]; numStudents[0] = 0; } for(int i = 0; i < scores.length; i++) { if(scores[i] <= (min + interval)) { numStudents[0]++; } else { for(int j = 1; j < (numStudents.length); j++) { if( ((scores[i] > (min + (j * interval))) && (scores[i] <= (min + ((j + 1) * interval))))) { numStudents[j]++; break; } } } } return numStudents; } /** * Get range text for each interval * * @param answers array of ansers * * * @return array of range text strings for each interval */ /* private static String[] calRange(String[] answers, String[] choices) { String[] range = new String[choices.length]; int current = 0; // gracefully handle a condition where there are no answers if(answers.length == 0) { for(int i = 0; i < range.length; i++) { range[i] = "unknown"; } return range; } choices[0] = answers[0]; for(int a = 1; a < answers.length; a++) { if(! (answers[a].equals(choices[current]))) { current++; choices[current] = answers[a]; } } return range; } */ /** * Calculate range strings for each interval. * * @param scores array of scores * @param numStudents number of students for each interval * @param min the minimium * @param max the maximum * @param interval the number of intervals * * @return array of range strings for each interval. */ private static String[] calRange( double[] scores, int[] numStudents, double min, double max, int interval) { String[] ranges = new String[numStudents.length]; ranges[0] = (int) min + " - " + (int) (min + interval); int i = 1; while(i < ranges.length) { if((((i + 1) * interval) + min) < max) { ranges[i] = ">" + (int) ((i * interval) + min) + " - " + (int) (((i + 1) * interval) + min); } else { ranges[i] = ">" + (int) ((i * interval) + min) + " - " + (int) max; } i++; } return ranges; } /** * Calculate the height of each histogram column. * * @param numStudents the number of students for each column * * @return array of column heights */ /* private static int[] calColumnHeightold(int[] numStudents) { int length = numStudents.length; int[] temp = new int[length]; int[] height = new int[length]; int i = 0; while(i < length) { temp[i] = numStudents[i]; i++; } Arrays.sort(temp); int num = 1; if((temp.length > 0) && (temp[temp.length - 1] > 0)) { num = (int) (300 / temp[temp.length - 1]); int j = 0; while(j < length) { height[j] = num * numStudents[j]; j++; } } return height; } */ private static int[] calColumnHeight(int[] numStudents, int totalResponse) { int[] height = new int[numStudents.length]; int index=0; while(index <numStudents.length){ if(totalResponse>0) height[index] = (int)((600*numStudents[index])/totalResponse); else height[index]=0; index++; } return height; } /** * Calculate quartiles. * * @param scores score array * @param r the quartile rank * * @return the quartile */ private static double calQuartiles(double[] scores, double r) { int k; double f; k = (int) (Math.floor((r * (scores.length - 1)) + 1)); f = (r * (scores.length - 1)) - Math.floor(r * (scores.length - 1)); // special handling if insufficient data to calculate if(k < 2) { return scores[0]; } return scores[k - 1] + (f * (scores[k] - scores[k - 1])); } /** * DOCUMENTATION PENDING * * @param n DOCUMENTATION PENDING * * @return DOCUMENTATION PENDING */ private String castingNum(double number,int decimal) { int indexOfDec=0; String n; int index; if(Math.ceil(number) == Math.floor(number)) { return ("" + (int) number); } else { n=""+number; indexOfDec=n.indexOf("."); index=indexOfDec+decimal+1; //log.info("NUMBER : "+n); //log.info("NUMBER LENGTH : "+n.length()); if(n.length()>index) { return n.substring(0,index); } else{ return ""+number; } } } private String castingNumForMode(String oldmode) // only show 2 decimal points for Mode { String[] tokens = oldmode.split(","); String[] roundedtokens = new String[tokens.length]; StringBuilder newModebuf = new StringBuilder(); for (int j = 0; j < tokens.length; j++) { roundedtokens[j] = castingNum(new Double(tokens[j]).doubleValue(), 2); newModebuf.append(", " + roundedtokens[j]); } String newMode = newModebuf.toString(); newMode = newMode.substring(2, newMode.length()); return newMode; } private String getType(int typeId) { ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.EvaluationMessages"); ResourceLoader rc = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.CommonMessages"); if (typeId == 1) { return rc.getString("multiple_choice_sin"); } if (typeId == 2) { return rc.getString("multipl_mc_ms"); } if (typeId == 12) { return rc.getString("multipl_mc_ss"); } if (typeId == 3) { return rb.getString("q_mult_surv"); } if (typeId == 4) { return rb.getString("q_tf"); } if (typeId == 5) { return rb.getString("q_short_ess"); } if (typeId == 6) { return rb.getString("q_fu"); } if (typeId == 7) { return rb.getString("q_aud"); } if (typeId == 8) { return rb.getString("q_fib"); } if (typeId == 9) { return rb.getString("q_match"); } if (typeId == 11) { return rb.getString("q_fin"); } if (typeId == 13) { return rb.getString("q_matrix_choices_surv"); } return ""; } /** * added by gopalrc - Dec 2007 * * Standard process action method. * @param ae ActionEvent * @throws AbortProcessingException */ public List getDetailedStatisticsSpreadsheetData(String publishedId) throws AbortProcessingException { log.debug("HistogramAggregate Statistics LISTENER."); TotalScoresBean totalBean = (TotalScoresBean) ContextUtil.lookupBean( "totalScores"); HistogramScoresBean bean = (HistogramScoresBean) ContextUtil.lookupBean( "histogramScores"); totalBean.setPublishedId(publishedId); //String publishedId = totalBean.getPublishedId(); if (!histogramScores(bean, totalBean)) { throw new RuntimeException("failed to call histogramScores."); } ArrayList spreadsheetRows = new ArrayList(); Collection detailedStatistics = bean.getDetailedStatistics(); spreadsheetRows.add(bean.getShowPartAndTotalScoreSpreadsheetColumns()); //spreadsheetRows.add(bean.getShowDiscriminationColumn()); boolean showDetailedStatisticsSheet; if (totalBean.getFirstItem().equals("")) { showDetailedStatisticsSheet = false; spreadsheetRows.add(showDetailedStatisticsSheet); return spreadsheetRows; } else { showDetailedStatisticsSheet = true; spreadsheetRows.add(showDetailedStatisticsSheet); } if (detailedStatistics==null || detailedStatistics.size()==0) { return spreadsheetRows; } ResourceLoader rb = new ResourceLoader( "org.sakaiproject.tool.assessment.bundle.EvaluationMessages"); ArrayList<Object> headerList = new ArrayList<Object>(); headerList = new ArrayList<Object>(); headerList.add(ExportResponsesBean.HEADER_MARKER); headerList.add(rb.getString("question")); if(bean.getRandomType()){ headerList.add("N(" + bean.getNumResponses() + ")"); }else{ headerList.add("N"); } headerList.add(rb.getString("pct_correct_of")); if (bean.getShowDiscriminationColumn()) { headerList.add(rb.getString("pct_correct_of")); headerList.add(rb.getString("pct_correct_of")); headerList.add(rb.getString("discrim_abbrev")); } headerList.add(rb.getString("frequency")); spreadsheetRows.add(headerList); headerList = new ArrayList<Object>(); headerList.add(ExportResponsesBean.HEADER_MARKER); headerList.add(""); headerList.add(""); headerList.add(rb.getString("whole_group")); if (bean.getShowDiscriminationColumn()) { headerList.add(rb.getString("upper_pct")); headerList.add(rb.getString("lower_pct")); headerList.add(""); } // No Answer headerList.add(rb.getString("no_answer")); // Label the response options A, B, C, ... int aChar = 65; for (char colHeader=65; colHeader < 65+bean.getMaxNumberOfAnswers(); colHeader++) { headerList.add(String.valueOf(colHeader)); } spreadsheetRows.add(headerList); Iterator detailedStatsIter = detailedStatistics.iterator(); ArrayList statsLine = null; while (detailedStatsIter.hasNext()) { HistogramQuestionScoresBean questionBean = (HistogramQuestionScoresBean)detailedStatsIter.next(); statsLine = new ArrayList(); statsLine.add(questionBean.getQuestionLabel()); Double dVal; statsLine.add(questionBean.getNumResponses()); try { if (questionBean.getShowPercentageCorrectAndDiscriminationFigures()) { dVal = Double.parseDouble(questionBean.getPercentCorrect()); statsLine.add(dVal); } else { statsLine.add(" "); } } catch (NumberFormatException ex) { statsLine.add(questionBean.getPercentCorrect()); } if (bean.getShowDiscriminationColumn()) { try { if (questionBean.getShowPercentageCorrectAndDiscriminationFigures()) { dVal = Double.parseDouble(questionBean.getPercentCorrectFromUpperQuartileStudents()); statsLine.add(dVal); } else { statsLine.add(" "); } } catch (NumberFormatException ex) { statsLine.add(questionBean.getPercentCorrectFromUpperQuartileStudents()); } try { if (questionBean.getShowPercentageCorrectAndDiscriminationFigures()) { dVal = Double.parseDouble(questionBean.getPercentCorrectFromLowerQuartileStudents()); statsLine.add(dVal); } else { statsLine.add(" "); } } catch (NumberFormatException ex) { statsLine.add(questionBean.getPercentCorrectFromLowerQuartileStudents()); } try { if (questionBean.getShowPercentageCorrectAndDiscriminationFigures()) { dVal = Double.parseDouble(questionBean.getDiscrimination()); statsLine.add(dVal); } else { statsLine.add(" "); } } catch (NumberFormatException ex) { statsLine.add(questionBean.getDiscrimination()); } } dVal = Double.parseDouble("" + questionBean.getNumberOfStudentsWithZeroAnswers()); statsLine.add(dVal); for (int i=0; i<questionBean.getHistogramBars().length; i++) { try { if (questionBean.getHistogramBars()[i].getIsCorrect()) { statsLine.add(ExportResponsesBean.FORMAT_BOLD); } dVal = Double.parseDouble("" + questionBean.getHistogramBars()[i].getNumStudents() ); statsLine.add(dVal); } catch (NullPointerException npe) { log.warn("questionBean.getHistogramBars()[" + i + "] is null. " + npe); } } spreadsheetRows.add(statsLine); } return spreadsheetRows; } private List<AssessmentGradingData> filterGradingData(List<AssessmentGradingData> submissionsSortedForDiscrim, Long itemId) { List<AssessmentGradingData> submissionsForItemSortedForDiscrim = new ArrayList<AssessmentGradingData>(); for(AssessmentGradingData agd: submissionsSortedForDiscrim){ Set<ItemGradingData> itemGradings = agd.getItemGradingSet(); for(ItemGradingData igd: itemGradings){ if(igd.getPublishedItemId().equals(itemId)){ submissionsForItemSortedForDiscrim.add(agd); break; } } } return submissionsForItemSortedForDiscrim; } }