/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <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 the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <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> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.ims.qti21.ui.statistics.interactions; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.util.Util; import org.olat.ims.qti21.QTI21StatisticsManager; import org.olat.ims.qti21.model.statistics.MatchStatistics; import org.olat.ims.qti21.model.xml.AssessmentHtmlBuilder; import org.olat.ims.qti21.model.xml.QtiNodesExtractor; import org.olat.ims.qti21.ui.statistics.QTI21AssessmentItemStatisticsController; import org.olat.ims.qti21.ui.statistics.QTI21StatisticResourceResult; import org.springframework.beans.factory.annotation.Autowired; import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem; import uk.ac.ed.ph.jqtiplus.node.item.interaction.MatchInteraction; import uk.ac.ed.ph.jqtiplus.node.item.interaction.choice.SimpleAssociableChoice; import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.ResponseDeclaration; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef; import uk.ac.ed.ph.jqtiplus.types.Identifier; /** * * Initial date: 22 nov. 2016<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class MatchStatisticsController extends BasicController { private final VelocityContainer mainVC; private final MatchInteraction interaction; private final AssessmentItemRef itemRef; private final AssessmentItem assessmentItem; private final QTI21StatisticResourceResult resourceResult; private final AssessmentHtmlBuilder assessmentHtmlBuilder; private final List<ChoiceWrapper> sourceWrappers = new ArrayList<>(); private final List<ChoiceWrapper> targetWrappers = new ArrayList<>(); private final Map<Identifier,List<Identifier>> correctAnswers = new HashMap<>(); @Autowired private QTI21StatisticsManager qtiStatisticsManager; public MatchStatisticsController(UserRequest ureq, WindowControl wControl, AssessmentItemRef itemRef, AssessmentItem assessmentItem, MatchInteraction interaction, QTI21StatisticResourceResult resourceResult) { super(ureq, wControl, Util.createPackageTranslator(QTI21AssessmentItemStatisticsController.class, ureq.getLocale())); this.interaction = interaction; this.itemRef = itemRef; this.assessmentItem = assessmentItem; this.resourceResult = resourceResult; assessmentHtmlBuilder = new AssessmentHtmlBuilder(); mainVC = createVelocityContainer("match_interaction"); List<SimpleAssociableChoice> sourceChoices = interaction .getSimpleMatchSets().get(0).getSimpleAssociableChoices(); for(SimpleAssociableChoice choice:sourceChoices) { sourceWrappers.add(new ChoiceWrapper(choice)); } List<SimpleAssociableChoice> targetChoices = interaction .getSimpleMatchSets().get(1).getSimpleAssociableChoices(); for(SimpleAssociableChoice choice:targetChoices) { targetWrappers.add(new ChoiceWrapper(choice)); } mainVC.contextPut("sourceChoices", sourceWrappers); mainVC.contextPut("targetChoices", targetWrappers); if(assessmentItem.getResponseDeclaration(interaction.getResponseIdentifier()) != null) { ResponseDeclaration responseDeclaration = assessmentItem.getResponseDeclaration(interaction.getResponseIdentifier()); QtiNodesExtractor.extractIdentifiersFromCorrectResponse(responseDeclaration.getCorrectResponse(), correctAnswers); } renderMatch(); putInitialPanel(mainVC); } @Override protected void doDispose() { // } @Override protected void event(UserRequest ureq, Component source, Event event) { } private void renderMatch() { List<MatchStatistics> statisticResponses = qtiStatisticsManager .getMatchStatistics(itemRef.getIdentifier().toString(), assessmentItem, interaction, resourceResult.getSearchParams()); long max = 0; for(MatchStatistics statistics:statisticResponses) { max = Math.max(max, statistics.getNumOfCorrect() + statistics.getNumOfIncorrect()); } for(MatchStatistics statistics:statisticResponses) { Identifier sourceIdentifier = statistics.getSourceIdentifier(); Identifier targetIdentifier = statistics.getDestinationIdentifier(); String key = sourceIdentifier.toString() + "-" + targetIdentifier.toString(); StatisticsWrapper sWrapper = new StatisticsWrapper(sourceIdentifier, targetIdentifier, statistics, max); mainVC.contextPut(key, sWrapper); } //fill the blanks for(ChoiceWrapper sourceChoice:sourceWrappers) { for(ChoiceWrapper targetChoice:targetWrappers) { Identifier sourceIdentifier = sourceChoice.getChoiceIdentifier(); Identifier targetIdentifier = targetChoice.getChoiceIdentifier(); String key = sourceIdentifier.toString() + "-" + targetIdentifier.toString(); if(mainVC.contextGet(key) == null) { mainVC.contextPut(key, new StatisticsWrapper(sourceIdentifier, targetIdentifier, null, max)); } } } } public class StatisticsWrapper { private final long max; private final Identifier sourceIdentifier; private final Identifier targetIdentifier; private final MatchStatistics statistics; public StatisticsWrapper(Identifier sourceIdentifier, Identifier targetIdentifier, MatchStatistics statistics, long max) { this.sourceIdentifier = sourceIdentifier; this.targetIdentifier = targetIdentifier; this.statistics = statistics; this.max = max; } public Identifier getSourceIdentifier() { return sourceIdentifier; } public Identifier getTargetIdentifier() { return targetIdentifier; } public long getNumOfCorrect() { return statistics == null ? 0 : statistics.getNumOfCorrect(); } public long getNumOfIncorrect() { return statistics == null ? 0 : statistics.getNumOfIncorrect(); } public String getCssClass() { String cssClass; if(max == 0 || statistics == null) { cssClass = ""; } else if(statistics.getNumOfCorrect() > 0) { cssClass = "bar_green"; } else if(statistics.getNumOfIncorrect() > 0) { cssClass = "bar_red"; } else { cssClass = ""; } return cssClass; } public String getValue() { String val; if(max == 0 || statistics == null) { val = ""; } else if(statistics.getNumOfCorrect() > 0) { val = Long.toString(statistics.getNumOfCorrect()); } else if(statistics.getNumOfIncorrect() > 0) { val = Long.toString(statistics.getNumOfIncorrect()); } else { val = ""; } return val; } public long getRelative(int ref) { if(max == 0 || statistics == null) { return 0; } long val = 0; if(statistics.getNumOfCorrect() > 0) { val = statistics.getNumOfCorrect(); } else if(statistics.getNumOfIncorrect() > 0) { val = statistics.getNumOfIncorrect(); } long point = ref / max; return point * val; } public long getRelativeBorder(int ref) { long border = getRelative(ref); return border <= 0 ? 0 : border / 2; } public long getRelativeMargin(int ref) { long val = this.getRelative(ref); return val == 0 ? ref : (ref - val) / 2; } } public class ChoiceWrapper { private final Identifier choiceIdentifier; private final SimpleAssociableChoice choice; private final String summary; public ChoiceWrapper(SimpleAssociableChoice choice) { this.choice = choice; this.choiceIdentifier = choice.getIdentifier(); summary = assessmentHtmlBuilder.flowStaticString(choice.getFlowStatics()); } public Identifier getChoiceIdentifier() { return choiceIdentifier; } public SimpleAssociableChoice getChoice() { return choice; } public String getSummary() { return summary; } } }