From d27257fdffd0c4079d090cbb30194992cea0a807 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Thu, 19 Oct 2017 17:25:22 +0200 Subject: [PATCH] OO-2870: allow negative score for "choice" interactions like single and multiple choices, hotspot, hottext and match (drag and drop or not) --- .../qti21/manager/CorrectResponsesUtil.java | 22 +++++++++ .../model/xml/AssessmentTestBuilder.java | 23 +++++++++- .../model/xml/AssessmentTestFactory.java | 45 +++++++++++++++++++ .../qti21/model/xml/QtiNodesExtractor.java | 27 +++++++++++ .../interactions/ChoiceScoreController.java | 11 +++-- .../interactions/FIBScoreController.java | 1 - .../HotspotChoiceScoreController.java | 12 +++-- .../interactions/MatchScoreController.java | 13 ++++-- ...ChoiceInteractionStatisticsController.java | 19 +++++++- ...otspotInteractionStatisticsController.java | 8 +++- .../_content/statistics_interaction.html | 2 +- 11 files changed, 166 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/olat/ims/qti21/manager/CorrectResponsesUtil.java b/src/main/java/org/olat/ims/qti21/manager/CorrectResponsesUtil.java index e2715fba17f..16a88771ceb 100644 --- a/src/main/java/org/olat/ims/qti21/manager/CorrectResponsesUtil.java +++ b/src/main/java/org/olat/ims/qti21/manager/CorrectResponsesUtil.java @@ -40,6 +40,9 @@ import uk.ac.ed.ph.jqtiplus.node.item.CorrectResponse; import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction; import uk.ac.ed.ph.jqtiplus.node.item.interaction.Interaction; import uk.ac.ed.ph.jqtiplus.node.item.interaction.TextEntryInteraction; +import uk.ac.ed.ph.jqtiplus.node.item.interaction.choice.Choice; +import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.MapEntry; +import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.Mapping; import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.ResponseDeclaration; import uk.ac.ed.ph.jqtiplus.node.shared.FieldValue; import uk.ac.ed.ph.jqtiplus.types.Identifier; @@ -342,4 +345,23 @@ public class CorrectResponsesUtil { } return null; } + + public static final Double getMappedValue(AssessmentItem assessmentItem, Interaction interaction, Choice choice) { + ResponseDeclaration responseDeclaration = assessmentItem.getResponseDeclaration(interaction.getResponseIdentifier()); + if(responseDeclaration != null && responseDeclaration.getMapping() != null) { + Mapping mapping = responseDeclaration.getMapping(); + if(mapping != null && mapping.getMapEntries() != null) { + for(MapEntry entry:mapping.getMapEntries()) { + SingleValue sValue = entry.getMapKey(); + if(sValue instanceof IdentifierValue) { + Identifier identifier = ((IdentifierValue)sValue).identifierValue(); + if(identifier.equals(choice.getIdentifier())) { + return entry.getMappedValue(); + } + } + } + } + } + return null; + } } diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestBuilder.java index f36e4a63ce4..c35d2bd9906 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestBuilder.java @@ -59,7 +59,7 @@ public class AssessmentTestBuilder { private Double maxScore; private Long maximumTimeLimits; private OutcomeRule testScoreRule; - private OutcomeCondition cutValueRule; + private OutcomeCondition cutValueRule, minScoreRule; private TestFeedbackBuilder passedFeedback; private TestFeedbackBuilder failedFeedback; @@ -104,6 +104,12 @@ public class AssessmentTestBuilder { cutValue = QtiNodesExtractor.extractCutValue(outcomeCondition.getOutcomeIf()); cutValueRule = outcomeCondition; } + + boolean findMinIf = QtiNodesExtractor.findLtValue(outcomeCondition.getOutcomeIf(), QTI21Constants.MINSCORE_IDENTIFIER) + && QtiNodesExtractor.findLtValue(outcomeCondition.getOutcomeIf(), QTI21Constants.SCORE_IDENTIFIER); + if(findMinIf) { + minScoreRule = outcomeCondition; + } } } } @@ -239,6 +245,15 @@ public class AssessmentTestBuilder { assessmentTest.getOutcomeDeclarations().remove(maxScoreDeclaration); } } + + // add min. score + OutcomeDeclaration minScoreDeclaration = assessmentTest.getOutcomeDeclaration(QTI21Constants.MINSCORE_IDENTIFIER); + if(minScoreDeclaration == null) { + minScoreDeclaration = AssessmentTestFactory.createOutcomeDeclaration(assessmentTest, QTI21Constants.MINSCORE_IDENTIFIER, 0.0d); + assessmentTest.getOutcomeDeclarations().add(0, minScoreDeclaration); + } else {//update value + AssessmentTestFactory.updateDefaultValue(minScoreDeclaration, 0.0d); + } } /* Overall score of this test @@ -263,6 +278,12 @@ public class AssessmentTestBuilder { assessmentTest.getOutcomeProcessing().getOutcomeRules().add(0, scoreRule); testScoreRule = scoreRule; } + + if(minScoreRule == null) { + OutcomeCondition scoreRule = AssessmentTestFactory.createMinScoreRule(assessmentTest); + assessmentTest.getOutcomeProcessing().getOutcomeRules().add(1, scoreRule); + minScoreRule = scoreRule; + } } /* Passed diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestFactory.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestFactory.java index cacd6c0d513..735bad668e1 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestFactory.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestFactory.java @@ -19,6 +19,10 @@ */ package org.olat.ims.qti21.model.xml; +import static org.olat.ims.qti21.QTI21Constants.MINSCORE_CLX_IDENTIFIER; +import static org.olat.ims.qti21.QTI21Constants.SCORE_CLX_IDENTIFIER; +import static org.olat.ims.qti21.QTI21Constants.SCORE_IDENTIFIER; + import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -35,6 +39,7 @@ import uk.ac.ed.ph.jqtiplus.node.expression.general.BaseValue; import uk.ac.ed.ph.jqtiplus.node.expression.general.Variable; import uk.ac.ed.ph.jqtiplus.node.expression.operator.And; import uk.ac.ed.ph.jqtiplus.node.expression.operator.Gte; +import uk.ac.ed.ph.jqtiplus.node.expression.operator.Lt; import uk.ac.ed.ph.jqtiplus.node.expression.operator.Match; import uk.ac.ed.ph.jqtiplus.node.expression.operator.Multiple; import uk.ac.ed.ph.jqtiplus.node.expression.operator.Sum; @@ -237,6 +242,46 @@ public class AssessmentTestFactory { return outcomeCondition; } + /* + <outcomeCondition> + <outcomeIf> + <lt> + <variable identifier="SCORE"/> + <variable identifier="MINSCORE"/> + </lt> + <setOutcomeValue identifier="SCORE"> + <variable identifier="MINSCORE"/> + </setOutcomeValue> + </outcomeIf> + </outcomeCondition> + */ + public static OutcomeCondition createMinScoreRule(AssessmentTest assessmentTest) { + OutcomeCondition outcomeCondition = new OutcomeCondition(assessmentTest); + OutcomeIf outcomeIf = new OutcomeIf(outcomeCondition); + outcomeCondition.setOutcomeIf(outcomeIf); + + Lt lt = new Lt(outcomeIf); + outcomeIf.setExpression(lt); + + Variable scoreVar = new Variable(lt); + scoreVar.setIdentifier(SCORE_CLX_IDENTIFIER); + lt.getExpressions().add(scoreVar); + + Variable minScoreVar = new Variable(lt); + minScoreVar.setIdentifier(MINSCORE_CLX_IDENTIFIER); + lt.getExpressions().add(minScoreVar); + + SetOutcomeValue setOutcomeValue = new SetOutcomeValue(outcomeIf); + setOutcomeValue.setIdentifier(SCORE_IDENTIFIER); + + Variable minScoreOutcomeVar = new Variable(setOutcomeValue); + minScoreOutcomeVar.setIdentifier(MINSCORE_CLX_IDENTIFIER); + setOutcomeValue.setExpression(minScoreOutcomeVar); + outcomeIf.getOutcomeRules().add(setOutcomeValue); + + return outcomeCondition; + } + /* <sum> <testVariables variableIdentifier="SCORE" /> diff --git a/src/main/java/org/olat/ims/qti21/model/xml/QtiNodesExtractor.java b/src/main/java/org/olat/ims/qti21/model/xml/QtiNodesExtractor.java index 50e65a8911c..fea7038682e 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/QtiNodesExtractor.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/QtiNodesExtractor.java @@ -30,6 +30,8 @@ import org.olat.ims.qti21.QTI21Constants; import uk.ac.ed.ph.jqtiplus.node.expression.Expression; import uk.ac.ed.ph.jqtiplus.node.expression.general.BaseValue; +import uk.ac.ed.ph.jqtiplus.node.expression.general.Variable; +import uk.ac.ed.ph.jqtiplus.node.expression.operator.Lt; import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem; import uk.ac.ed.ph.jqtiplus.node.item.CorrectResponse; import uk.ac.ed.ph.jqtiplus.node.outcome.declaration.OutcomeDeclaration; @@ -39,6 +41,7 @@ import uk.ac.ed.ph.jqtiplus.node.shared.declaration.DefaultValue; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeCondition; import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeConditionChild; +import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeConditionExpressionChild; import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeIf; import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeRule; import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.SetOutcomeValue; @@ -168,6 +171,30 @@ public interface QtiNodesExtractor { return false; } + public static boolean findLtValue(OutcomeConditionExpressionChild outcomeIfElse, Identifier identifier) { + if(outcomeIfElse == null + || outcomeIfElse.getOutcomeRules() == null + || outcomeIfElse.getOutcomeRules().isEmpty()) return false; + + List<Expression> expressions = outcomeIfElse.getExpressions(); + for(Expression expression:expressions) { + if(expression instanceof Lt) { + Lt lt = (Lt)expression; + for(Expression ltExpression:lt.getExpressions()) { + if(ltExpression instanceof Variable) { + Variable variable = (Variable)ltExpression; + if(identifier.toString().equals(variable.getIdentifier().toString())) { + return true; + } + } + } + } + + } + + return false; + } + public static Double extractCutValue(OutcomeIf outcomeIf) { if(outcomeIf != null && outcomeIf.getExpressions().size() > 0) { Expression gte = outcomeIf.getExpressions().get(0); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/ChoiceScoreController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/ChoiceScoreController.java index 00f33d343ed..d8d762dfd7b 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/ChoiceScoreController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/ChoiceScoreController.java @@ -103,9 +103,11 @@ public class ChoiceScoreController extends AssessmentItemRefEditorController imp setFormContextHelp(contextHelpUrl); super.initForm(formLayout, listener, ureq); - minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, "0.0", formLayout); + ScoreBuilder minScore = itemBuilder.getMinScoreBuilder(); + String minValue = minScore == null ? "" : (minScore.getScore() == null ? "" : minScore.getScore().toString()); + minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, minValue, formLayout); minScoreEl.setElementCssClass("o_sel_assessment_item_min_score"); - minScoreEl.setEnabled(false); + minScoreEl.setEnabled(!restrictedEdit); ScoreBuilder maxScore = itemBuilder.getMaxScoreBuilder(); String maxValue = maxScore == null ? "" : (maxScore.getScore() == null ? "" : maxScore.getScore().toString()); @@ -194,6 +196,7 @@ public class ChoiceScoreController extends AssessmentItemRefEditorController imp protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; allOk &= validateDouble(maxScoreEl); + allOk &= validateDouble(minScoreEl); if(assessmentModeEl.isOneSelected() && assessmentModeEl.isSelected(1)) { for(ChoiceWrapper wrapper:wrappers) { @@ -220,7 +223,9 @@ public class ChoiceScoreController extends AssessmentItemRefEditorController imp String maxScoreValue = maxScoreEl.getValue(); Double maxScore = Double.parseDouble(maxScoreValue); itemBuilder.setMaxScore(maxScore); - itemBuilder.setMinScore(new Double(0d)); + String minScoreValue = minScoreEl.getValue(); + Double minScore = Double.parseDouble(minScoreValue); + itemBuilder.setMinScore(minScore); if(assessmentModeEl.isOneSelected() && assessmentModeEl.isSelected(1)) { itemBuilder.setScoreEvaluationMode(ScoreEvaluation.perAnswer); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBScoreController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBScoreController.java index 2c58c387b15..d8574bd6dc4 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBScoreController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBScoreController.java @@ -85,7 +85,6 @@ public class FIBScoreController extends AssessmentItemRefEditorController implem super.initForm(formLayout, listener, ureq); minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, "0.0", formLayout); minScoreEl.setEnabled(false); - minScoreEl.setEnabled(!restrictedEdit); ScoreBuilder maxScore = itemBuilder.getMaxScoreBuilder(); String maxValue = maxScore == null ? "" : (maxScore.getScore() == null ? "" : maxScore.getScore().toString()); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/HotspotChoiceScoreController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/HotspotChoiceScoreController.java index 3354e792990..00e938b96e0 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/HotspotChoiceScoreController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/HotspotChoiceScoreController.java @@ -104,9 +104,10 @@ public class HotspotChoiceScoreController extends AssessmentItemRefEditorControl protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { super.initForm(formLayout, listener, ureq); setFormContextHelp("Test editor QTI 2.1 in detail#details_testeditor_score"); - - minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, "0.0", formLayout); - minScoreEl.setEnabled(false); + + ScoreBuilder minScore = itemBuilder.getMinScoreBuilder(); + String minValue = minScore == null ? "" : (minScore.getScore() == null ? "" : minScore.getScore().toString()); + minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, minValue, formLayout); minScoreEl.setEnabled(!restrictedEdit); ScoreBuilder maxScore = itemBuilder.getMaxScoreBuilder(); @@ -243,6 +244,7 @@ public class HotspotChoiceScoreController extends AssessmentItemRefEditorControl protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; allOk &= validateDouble(maxScoreEl); + allOk &= validateDouble(minScoreEl); if(assessmentModeEl.isOneSelected() && assessmentModeEl.isSelected(1)) { for(HotspotChoiceWrapper wrapper:wrappers) { @@ -267,7 +269,9 @@ public class HotspotChoiceScoreController extends AssessmentItemRefEditorControl String maxScoreValue = maxScoreEl.getValue(); Double maxScore = Double.parseDouble(maxScoreValue); itemBuilder.setMaxScore(maxScore); - itemBuilder.setMinScore(new Double(0d)); + String minScoreValue = minScoreEl.getValue(); + Double minScore = Double.parseDouble(minScoreValue); + itemBuilder.setMinScore(minScore); if(assessmentModeEl.isOneSelected() && assessmentModeEl.isSelected(1)) { itemBuilder.setScoreEvaluationMode(ScoreEvaluation.perAnswer); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MatchScoreController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MatchScoreController.java index 6c451a1c272..dd2377a914f 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MatchScoreController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MatchScoreController.java @@ -102,10 +102,12 @@ public class MatchScoreController extends AssessmentItemRefEditorController impl protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { super.initForm(formLayout, listener, ureq); setFormContextHelp("Test editor QTI 2.1 in detail#details_testeditor_score"); - - minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, "0.0", formLayout); + + ScoreBuilder minScore = itemBuilder.getMinScoreBuilder(); + String minValue = minScore == null ? "" : (minScore.getScore() == null ? "" : minScore.getScore().toString()); + minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, minValue, formLayout); minScoreEl.setElementCssClass("o_sel_assessment_item_min_score"); - minScoreEl.setEnabled(false); + minScoreEl.setEnabled(!restrictedEdit); ScoreBuilder maxScore = itemBuilder.getMaxScoreBuilder(); String maxValue = maxScore == null ? "" : (maxScore.getScore() == null ? "" : maxScore.getScore().toString()); @@ -214,6 +216,7 @@ public class MatchScoreController extends AssessmentItemRefEditorController impl protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; allOk &= validateDouble(maxScoreEl); + allOk &= validateDouble(minScoreEl); if(assessmentModeEl.isOneSelected() && assessmentModeEl.isSelected(1)) { for(Map.Entry<DirectedPairValue, MatchScoreWrapper> entry:scoreWrappers.entrySet()) { @@ -252,7 +255,9 @@ public class MatchScoreController extends AssessmentItemRefEditorController impl String maxScoreValue = maxScoreEl.getValue(); Double maxScore = Double.parseDouble(maxScoreValue); itemBuilder.setMaxScore(maxScore); - itemBuilder.setMinScore(new Double(0d)); + String minScoreValue = minScoreEl.getValue(); + Double minScore = Double.parseDouble(minScoreValue); + itemBuilder.setMinScore(minScore); if(assessmentModeEl.isOneSelected() && assessmentModeEl.isSelected(1)) { itemBuilder.setScoreEvaluationMode(ScoreEvaluation.perAnswer); diff --git a/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/ChoiceInteractionStatisticsController.java b/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/ChoiceInteractionStatisticsController.java index 095debe0b96..09521ba74aa 100644 --- a/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/ChoiceInteractionStatisticsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/ChoiceInteractionStatisticsController.java @@ -137,7 +137,12 @@ public abstract class ChoiceInteractionStatisticsController extends BasicControl points = null; cssColor = "bar_default"; } else { - points = correct ? 1.0f : 0.0f; //response.getPoints(); + Double mappedValue = CorrectResponsesUtil.getMappedValue(assessmentItem, interaction, choice); + if(mappedValue != null) { + points = mappedValue.floatValue(); + } else { + points = correct ? 1.0f : 0.0f; //response.getPoints(); + } cssColor = correct ? "bar_green" : "bar_red"; } @@ -204,7 +209,17 @@ public abstract class ChoiceInteractionStatisticsController extends BasicControl d2.add(wrongA, label); d3.add(notAnswered, label); - Float pointsObj = survey ? null : (correct ? 1.0f : 0.0f); + Float pointsObj; + if(survey) { + pointsObj = null; + } else { + Double mappedValue = CorrectResponsesUtil.getMappedValue(assessmentItem, interaction, choice); + if(mappedValue != null) { + pointsObj = mappedValue.floatValue(); + } else { + pointsObj = correct ? 1.0f : 0.0f; + } + } responseInfos.add(new ResponseInfos(label, text, pointsObj, correct, survey, false)); } diff --git a/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/HotspotInteractionStatisticsController.java b/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/HotspotInteractionStatisticsController.java index 7d1e21b9da8..3377dd1f3e3 100644 --- a/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/HotspotInteractionStatisticsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/HotspotInteractionStatisticsController.java @@ -47,6 +47,7 @@ import org.olat.ims.qti.statistics.model.StatisticsItem; import org.olat.ims.qti.statistics.ui.ResponseInfos; import org.olat.ims.qti.statistics.ui.Series; import org.olat.ims.qti21.QTI21StatisticsManager; +import org.olat.ims.qti21.manager.CorrectResponsesUtil; import org.olat.ims.qti21.model.statistics.HotspotChoiceStatistics; import org.olat.ims.qti21.ui.statistics.QTI21AssessmentItemStatisticsController; import org.olat.ims.qti21.ui.statistics.QTI21StatisticResourceResult; @@ -190,7 +191,12 @@ public class HotspotInteractionStatisticsController extends BasicController { points = null; cssColor = "bar_default"; } else { - points = correct ? 1.0f : 0.0f; //response.getPoints(); + Double mappedValue = CorrectResponsesUtil.getMappedValue(assessmentItem, interaction, choice); + if(mappedValue != null) { + points = mappedValue.floatValue(); + } else { + points = correct ? 1.0f : 0.0f; + } cssColor = correct ? "bar_green" : "bar_red"; } diff --git a/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/_content/statistics_interaction.html b/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/_content/statistics_interaction.html index a923dcd1027..4eb39de6ea9 100644 --- a/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/_content/statistics_interaction.html +++ b/src/main/java/org/olat/ims/qti21/ui/statistics/interactions/_content/statistics_interaction.html @@ -34,7 +34,7 @@ #end #end <strong>$responseInfo.label.</strong> - #if($responseInfo.points) $r.translate("answer.points", $responseInfo.formattedPoints) #end + #if($r.isNotNull($responseInfo.points)) $r.translate("answer.points", $responseInfo.formattedPoints) #end #if($responseInfo.wrongAnswersAvailable) <span class="o_qti_statistics_answer"><strong>$responseInfo.text</strong></span> <p class="o_qti_statistics_wrong_answer">#if($responseInfo.survey) -- GitLab