/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <p> */ package org.olat.ims.qti.statistics.manager; import static org.junit.Assert.assertNotNull; import java.io.InputStream; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import org.dom4j.Document; import org.dom4j.Element; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.olat.core.commons.persistence.DB; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.xml.XMLParser; import org.olat.ims.qti.QTIResult; import org.olat.ims.qti.QTIResultSet; import org.olat.ims.qti.export.helper.ItemWithResponseLid; import org.olat.ims.qti.export.helper.ItemWithResponseStr; import org.olat.ims.qti.export.helper.QTIItemObject; import org.olat.ims.qti.statistics.QTIStatisticSearchParams; import org.olat.ims.qti.statistics.QTIStatisticsManager; import org.olat.ims.qti.statistics.model.StatisticAssessment; import org.olat.ims.qti.statistics.model.StatisticsItem; import org.olat.ims.resources.IMSEntityResolver; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryService; import org.olat.resource.OLATResource; import org.olat.resource.OLATResourceManager; import org.olat.test.JunitTestHelper; import org.olat.test.OlatTestCase; import org.springframework.beans.factory.annotation.Autowired; /** * Description:<br> * Test QTIStatisticsManager and QTIItemStatisticsManager * * <P> * Initial Date: 02.08.2011 <br> * * @author mkuendig, srosse, stephane.rosse@frentix.com, http://www.frentix.com */ public class QTIStatisticsManagerLargeTest extends OlatTestCase { private static OLog log = Tracing.createLoggerFor(QTIStatisticsManagerLargeTest.class); private static boolean isInitialized = false; private static Long olatResource ; private static String olatResourceDetail; private static Long repositoryRef; private static List<QTIItemObject> itemObjects; private static int numberOfParticipants = 1000; private static int numberOfQuestions = 4; private static Map<String,Double> averageScorePerQuestion = new HashMap<String,Double>(); private static List<Float> averageRightAnswersInPercent = new ArrayList<Float>(); private static Map<String,Float> identToANumOfRightAnswers = new HashMap<>(); private static List<Long> allDurations = new ArrayList<Long>(); private static List<Float> scorePerParticipant = new ArrayList<Float>(); private static double maxScore = 7.0d; private static float averageScore = 0.0f; private static long averageDuration = 0; private static int numberOfTestFailed = 0; private static int numberOfTestPassed = 0; private static float durationQ3 = 0.0f; private static float scoreQ1 = 0.0f; private static float scoreQ2 = 0.0f; private static int rightAnswersQ2 = 0; private static int wrongAnswersQ2 = 0; private static List<String> fibAnswers = new ArrayList<String>(); @Autowired private DB dbInstance; @Autowired private QTIStatisticsManager qtim; @Autowired private RepositoryService repositoryService; @Before public void setUp() throws Exception { if(isInitialized) return; RepositoryEntry re = createRepository(); olatResource = re.getOlatResource().getResourceableId(); repositoryRef = re.getKey(); olatResourceDetail = UUID.randomUUID().toString().replace("-", ""); getItemObjectList(); double scoreQuestion1 = 0.0d, scoreQuestion2 = 0.0d, scoreQuestion3 = 0.0d, scoreQuestion4 = 0.0d; float maxScoreQ1 = 0, maxScoreQ2 = 0, maxScoreQ3 = 0, maxScoreQ4 = 0; // insert value into resultset for (int i = 0; i < numberOfParticipants; i++) { QTIResultSet test = new QTIResultSet(); float setScore = (float) Math.ceil(Math.random() * maxScore); if (setScore >= 4.0d) { numberOfTestPassed++; test.setIsPassed(true); } else { numberOfTestFailed++; test.setIsPassed(false); } long tempTestDuration = Math.round((Math.random() * 100000) + 1.0); averageDuration += tempTestDuration; averageScore += setScore; scorePerParticipant.add(setScore); assertNotNull(allDurations); allDurations.add(tempTestDuration); test.setOlatResource(olatResource); test.setOlatResourceDetail(olatResourceDetail); test.setRepositoryRef(repositoryRef); test.setScore(setScore); test.setDuration(tempTestDuration); test.setIdentity(JunitTestHelper.createAndPersistIdentityAsUser("test" + i)); test.setAssessmentID(111L); Calendar cal = Calendar.getInstance(); cal.set(2013, 8, 23, (int) (Math.random() * 1000 % 60), (int) (Math.random() * 1000 % 60), (int) (Math.random() * 1000 % 60)); test.setLastModified(cal.getTime()); dbInstance.saveObject(test); // insert values into result for (int j = 0; j < numberOfQuestions; j++) { QTIResult testres = new QTIResult(); testres.setResultSet(test); long tempDuration = Math.round((Math.random() * 10000) + 1.0); testres.setDuration(tempDuration); testres.setIp("127.0.0.1"); testres.setAnswer("asdf"); if (j == 0) { testres.setItemIdent("QTIEDIT:SCQ:1000007885"); if (i % 4 == 0) { testres.setAnswer("1000007887[[1000007890]]"); } else if (i % 4 == 1) { testres.setAnswer("1000007887[[1000009418]]"); } else if (i % 4 == 2) { testres.setAnswer("1000007887[[1000009464]]"); } else if (i % 4 == 3) { testres.setAnswer("1000007887[[1000007890]]"); } float score = (float)Math.ceil(Math.random()); scoreQ1 += score; testres.setScore(score); scoreQuestion1 += score; if (score == 1.0f) { maxScoreQ1++; } } else if (j == 1) { testres.setItemIdent("QTIEDIT:MCQ:1000009738"); float score = (float)Math.ceil(Math.random() * 3); scoreQ2 += score; testres.setScore(score); scoreQuestion2 += score; if (score < 3.0f) { wrongAnswersQ2++; } else { rightAnswersQ2++; maxScoreQ2++; } } else if (j == 2) { testres.setItemIdent("QTIEDIT:KPRIM:1000010376"); durationQ3 += tempDuration; float score = (float)Math.ceil(Math.random() * 2); testres.setScore(score); scoreQuestion3 += score; if (score == 2.0f) { maxScoreQ3++; } } else if (j == 3) { testres.setItemIdent("QTIEDIT:FIB:1000010792"); if (i % 4 == 0) { testres.setAnswer("Huagagaagaga"); fibAnswers.add("Huagagaagaga"); } else if (i % 4 == 1) { testres.setAnswer("Yikes"); fibAnswers.add("Yikes"); } else if (i % 4 == 2 || i % 4 == 3) { testres.setAnswer("Muragarara"); fibAnswers.add("Muragarara"); } float score = (float)Math.ceil(Math.random()); testres.setScore(score); scoreQuestion4 += score; if (score == 1.0f) { maxScoreQ4++; } } testres.setLastModified(new Date()); testres.setTstamp(new Date()); dbInstance.saveObject(testres); } dbInstance.commitAndCloseSession(); } dbInstance.commitAndCloseSession(); durationQ3 = (durationQ3 / numberOfParticipants); scoreQ1 = scoreQ1 / numberOfParticipants; scoreQ2 = scoreQ2 / numberOfParticipants; averageScore = averageScore / numberOfParticipants; averageDuration = averageDuration / numberOfParticipants; averageScorePerQuestion.put("QTIEDIT:SCQ:1000007885", scoreQuestion1 / numberOfParticipants); averageScorePerQuestion.put("QTIEDIT:MCQ:1000009738", scoreQuestion2 / numberOfParticipants); averageScorePerQuestion.put("QTIEDIT:KPRIM:1000010376", scoreQuestion3 / numberOfParticipants); averageScorePerQuestion.put("QTIEDIT:FIB:1000010792", scoreQuestion4 / numberOfParticipants); averageRightAnswersInPercent.add(maxScoreQ1 / numberOfParticipants); averageRightAnswersInPercent.add(maxScoreQ2 / numberOfParticipants); averageRightAnswersInPercent.add(maxScoreQ3 / numberOfParticipants); averageRightAnswersInPercent.add(maxScoreQ4 / numberOfParticipants); identToANumOfRightAnswers.put("QTIEDIT:SCQ:1000007885", maxScoreQ1); identToANumOfRightAnswers.put("QTIEDIT:MCQ:1000009738", maxScoreQ2); identToANumOfRightAnswers.put("QTIEDIT:KPRIM:1000010376", maxScoreQ3); identToANumOfRightAnswers.put("QTIEDIT:FIB:1000010792", maxScoreQ4); // sort allDurations List asc Collections.sort(allDurations); Collections.sort(scorePerParticipant); isInitialized = true; } @Test public void testStatistics() { long start = System.currentTimeMillis(); QTIStatisticSearchParams searchParams = new QTIStatisticSearchParams(olatResource, olatResourceDetail); StatisticAssessment stats = qtim.getAssessmentStatistics(searchParams); log.info("Statistics of resource takes (ms): " + (System.currentTimeMillis() - start)); Assert.assertNotNull(stats); Assert.assertEquals(averageScore, stats.getAverage(), 0.01); Assert.assertEquals(numberOfParticipants, stats.getNumOfParticipants()); Assert.assertEquals(numberOfTestFailed, stats.getNumOfFailed()); Assert.assertEquals(numberOfTestPassed, stats.getNumOfPassed()); double maxScore = scorePerParticipant.get(scorePerParticipant.size() - 1).doubleValue(); double minScore = scorePerParticipant.get(0).doubleValue(); double range = maxScore - minScore; Assert.assertEquals(maxScore, stats.getMaxScore(), 0.1); Assert.assertEquals(minScore, stats.getMinScore(), 0.1); Assert.assertEquals(range, stats.getRange(), 0.1); Assert.assertEquals(averageDuration, stats.getAverageDuration(), 2); Assert.assertTrue(stats.getStandardDeviation() > 0); Assert.assertTrue(stats.getMedian() > 0); Assert.assertNotNull(stats.getDurations()); Assert.assertNotNull(stats.getScores()); Assert.assertNotNull(stats.getMode()); Assert.assertFalse(stats.getMode().isEmpty()); } @Test public void testItemStatistics_singleChoice_0() { QTIItemObject itemObject = itemObjects.get(0); double maxValue = Double.parseDouble(itemObject.getItemMaxValue()); QTIStatisticSearchParams searchParams = new QTIStatisticSearchParams(olatResource, olatResourceDetail); StatisticsItem stats = qtim.getItemStatistics(itemObject.getItemIdent(), maxValue, searchParams); Assert.assertEquals(scoreQ1, stats.getAverageScore(), 0.1); } @Test public void testItemStatistics_multipleChoice_1() { QTIItemObject itemObject = itemObjects.get(1); double maxValue = Double.parseDouble(itemObject.getItemMaxValue()); QTIStatisticSearchParams searchParams = new QTIStatisticSearchParams(olatResource, olatResourceDetail); StatisticsItem stats = qtim.getItemStatistics(itemObject.getItemIdent(), maxValue, searchParams); double difficulty = rightAnswersQ2 / (double)numberOfParticipants; Assert.assertEquals(difficulty, stats.getDifficulty(), 0.1); Assert.assertEquals(scoreQ2, stats.getAverageScore(), 0.1); Assert.assertEquals(wrongAnswersQ2, stats.getNumOfIncorrectAnswers()); Assert.assertEquals(numberOfParticipants - wrongAnswersQ2, stats.getNumOfCorrectAnswers()); } @Test public void testItemStatistics_kprim_2() { QTIItemObject itemObject = itemObjects.get(2); double maxValue = Double.parseDouble(itemObject.getItemMaxValue()); QTIStatisticSearchParams searchParams = new QTIStatisticSearchParams(olatResource, olatResourceDetail); StatisticsItem stats = qtim.getItemStatistics(itemObject.getItemIdent(), maxValue, searchParams); float durationQ3InSec = durationQ3; Assert.assertEquals(durationQ3InSec, stats.getAverageDuration(), 1.0f); } @Test public void testAnswerTexts() { QTIStatisticSearchParams searchParams = new QTIStatisticSearchParams(olatResource, olatResourceDetail); List<String> answers = qtim.getAnswers("QTIEDIT:FIB:1000010792", searchParams); Assert.assertTrue(answers.containsAll(fibAnswers)); Assert.assertTrue(fibAnswers.containsAll(answers)); } @SuppressWarnings("rawtypes") private void getItemObjectList() { InputStream in = QTIStatisticsManagerLargeTest.class.getResourceAsStream("qti.xml"); XMLParser xmlParser = new XMLParser(new IMSEntityResolver()); Document doc = xmlParser.parse(in, false); Element root = doc.getRootElement(); List items = root.selectNodes("//item"); itemObjects = new ArrayList<QTIItemObject>(); for (Iterator iter = items.iterator(); iter.hasNext();) { Element el_item = (Element) iter.next(); if (el_item.selectNodes(".//response_lid").size() > 0) { itemObjects.add(new ItemWithResponseLid(el_item)); } else if (el_item.selectNodes(".//response_str").size() > 0) { itemObjects.add(new ItemWithResponseStr(el_item)); } } } private RepositoryEntry createRepository() { OLATResourceManager rm = OLATResourceManager.getInstance(); // create course and persist as OLATResourceImpl OLATResource r = rm.createOLATResourceInstance("QTIStatisticsTest"); dbInstance.saveObject(r); dbInstance.intermediateCommit(); RepositoryEntry d = repositoryService.create("Kanu Unchou", "QTIStatisticsTest", "QTIStatisticsTest", "Repo entry", r); dbInstance.saveObject(d); dbInstance.intermediateCommit(); return d; } }