diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties index c9a3012c3d8ed2a52e555677e4eee659ac552d17..63b2de809c99972380cd4e3a380b9e476861d29b 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties @@ -273,6 +273,8 @@ warning.atleastone=Bitte w\u00E4hlen Sie mindestens ein Element. warning.atleastonesection=Diese Sektion kann nicht gel\u00F6scht werden. Ein Test oder ein Test-Part muss mindestens eine Sektion enthalten. warning.conversion.list=Es wurde Inkompatibilit\u00E4ten gefunden\: warning.conversion.standard=Es gibt ein Risiko, dass sie Daten verlieren, obwohl kein Inkompatibilit\u00E4ten entdeckt wurden. +warning.correct.answer.score=Die korrekte Antwort gibt kein Punkt zu, aber eine inkorrekte schon. +warning.correct.answers.score=Korrekte Antwort geben keine Punkte zu, aber inkorrekte schon. warning.copy.from.pool=Die Frage wurde vom Fragenpool kopiert. Sie k\u00F6nnen die Originalfrage unter Master ID finden. warning.custom.operator=Es wurde eine her\u00ADstel\u00ADler\u00ADspe\u00ADzi\u00ADfische Erweiterung gefunden. Diese ist nicht von Editor unterst\u00FCtzt. warning.feedback.cutvalue=Feedback wird aktiviert, sobald bei "Notwendige Punktzahl f\u00FCr 'Bestanden'" eine Punktzahl eingegeben wurde. diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties index 8a492b47d264982c58d2a13de8fb49f575a3e5cd..f26e8a21410897d92af0c64808b0d99991e24599 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties @@ -274,6 +274,8 @@ warning.atleastonesection=The section cannot be deleted. A test or a test part m warning.conversion.list=Some incompatibilities was found\: warning.conversion.standard=There is a risk that some data got lost after conversion but no flagrant incompatibilities was found. warning.copy.from.pool=The question was copied from the question bank. You can find the original question under the master id. +warning.correct.answer.score=The correct answer doesn't give any point but a wrong one do. +warning.correct.answer.score=Correct answers don't give any points but wrong one(s) do. warning.custom.operator=This question contains creator specific extension which are currently not supported by OpenOlat. warning.feedback.cutvalue=The feedback is based on the cut value. You need to define it first. warning.in.use=The resource is already used for assessment purpose. Editing is limited. 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 8405e9554d9f991c36d27b096afe7147e5ed8eae..7264dda4c14016e58fed8ea42b13da15f6ca27e0 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 @@ -256,7 +256,7 @@ public class ChoiceScoreController extends AssessmentItemRefEditorController imp @Override protected boolean validateFormLogic(UserRequest ureq) { - boolean allOk = true; + boolean allOk = super.validateFormLogic(ureq); allOk &= validateMinMaxScores(minScoreEl, maxScoreEl); if(assessmentModeEl.isOneSelected() && assessmentModeEl.isSelected(1)) { @@ -265,7 +265,7 @@ public class ChoiceScoreController extends AssessmentItemRefEditorController imp } } - return allOk & super.validateFormLogic(ureq); + return allOk; } @Override @@ -301,7 +301,7 @@ public class ChoiceScoreController extends AssessmentItemRefEditorController imp itemBuilder.clearMapping(); for(ChoiceWrapper wrapper:wrappers) { String pointsStr = wrapper.getPointsEl().getValue(); - Double points = new Double(pointsStr); + Double points = Double.valueOf(pointsStr); itemBuilder.setMapping(wrapper.getChoice().getIdentifier(), points); } } else { diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MultipleChoiceEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MultipleChoiceEditorController.java index dd4494a895e507fbc4d96ea1128010a008400cd7..0bb1a9086c2190c933e99b4b859cfd8607cf422d 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MultipleChoiceEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MultipleChoiceEditorController.java @@ -44,11 +44,14 @@ import org.olat.core.util.vfs.VFSContainer; import org.olat.ims.qti21.QTI21Constants; import org.olat.ims.qti21.model.IdentifierGenerator; import org.olat.ims.qti21.model.QTI21QuestionType; +import org.olat.ims.qti21.model.xml.AssessmentItemBuilder; import org.olat.ims.qti21.model.xml.AssessmentItemFactory; import org.olat.ims.qti21.model.xml.interactions.MultipleChoiceAssessmentItemBuilder; +import org.olat.ims.qti21.model.xml.interactions.SimpleChoiceAssessmentItemBuilder.ScoreEvaluation; import org.olat.ims.qti21.ui.ResourcesMapper; import org.olat.ims.qti21.ui.components.FlowFormItem; import org.olat.ims.qti21.ui.editor.AssessmentTestEditorController; +import org.olat.ims.qti21.ui.editor.SyncAssessmentItem; import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent; import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic; @@ -64,7 +67,7 @@ import uk.ac.ed.ph.jqtiplus.value.Orientation; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class MultipleChoiceEditorController extends FormBasicController { +public class MultipleChoiceEditorController extends FormBasicController implements SyncAssessmentItem { private TextElement titleEl; private RichTextElement textEl; @@ -102,6 +105,7 @@ public class MultipleChoiceEditorController extends FormBasicController { itemContainer = (VFSContainer)rootContainer.resolve(relativePath); initForm(ureq); + validateScoreOfCorrectAnswer(); } @Override @@ -233,17 +237,20 @@ public class MultipleChoiceEditorController extends FormBasicController { choiceWrappers.add(new SimpleChoiceWrapper(choice, choiceEl, choiceReadOnlyEl, removeLink, addLink, upLink, downLink)); } - - @Override protected void doDispose() { // } + @Override + public void sync(UserRequest ureq, AssessmentItemBuilder itemBuilder) { + validateScoreOfCorrectAnswer(); + } + @Override protected boolean validateFormLogic(UserRequest ureq) { - boolean allOk = true; + boolean allOk = super.validateFormLogic(ureq); titleEl.clearError(); if(!StringHelper.containsNonWhitespace(titleEl.getValue())) { @@ -259,8 +266,37 @@ public class MultipleChoiceEditorController extends FormBasicController { allOk &= false; } } - - return allOk & super.validateFormLogic(ureq); + validateScoreOfCorrectAnswer();// show only a warning + return allOk; + } + + private void validateScoreOfCorrectAnswer() { + try { + Boolean warning = (Boolean)answersCont.contextGet("scoreWarning"); + Boolean newWarning; + if(itemBuilder.getScoreEvaluationMode() == ScoreEvaluation.perAnswer) { + boolean wrongAnswerHasPoint = false; + boolean correctAnswerHasNotPoint = true; + for(SimpleChoiceWrapper choiceWrapper:choiceWrappers) { + Double score = itemBuilder.getMapping(choiceWrapper.getIdentifier()); + if(choiceWrapper.isCorrect()) { + correctAnswerHasNotPoint &= (score == null || score.doubleValue() < 0.000001); + } else { + wrongAnswerHasPoint |= (score != null && score.doubleValue() > 0.6); + } + } + newWarning = Boolean.valueOf(wrongAnswerHasPoint && correctAnswerHasNotPoint); + } else { + newWarning = Boolean.FALSE; + } + + if(warning == null || !warning.equals(newWarning)) { + answersCont.contextPut("scoreWarning", newWarning); + } + } catch (Exception e) { + // not a critical feature, don't produce red screen + logError("", e); + } } @Override @@ -306,6 +342,7 @@ public class MultipleChoiceEditorController extends FormBasicController { choiceList.add(choice); } itemBuilder.setSimpleChoices(choiceList); + validateScoreOfCorrectAnswer(); fireEvent(ureq, new AssessmentItemEvent(AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED, itemBuilder.getAssessmentItem(), QTI21QuestionType.sc)); } diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/SingleChoiceEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/SingleChoiceEditorController.java index de0b24ea14bdf49707d83edbdd53ea37d3879c58..8bcc458fc2cf24ae695c6d963b41d12d7378099f 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/SingleChoiceEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/SingleChoiceEditorController.java @@ -43,11 +43,14 @@ import org.olat.core.util.Util; import org.olat.core.util.vfs.VFSContainer; import org.olat.ims.qti21.QTI21Constants; import org.olat.ims.qti21.model.QTI21QuestionType; +import org.olat.ims.qti21.model.xml.AssessmentItemBuilder; import org.olat.ims.qti21.model.xml.AssessmentItemFactory; +import org.olat.ims.qti21.model.xml.interactions.SimpleChoiceAssessmentItemBuilder.ScoreEvaluation; import org.olat.ims.qti21.model.xml.interactions.SingleChoiceAssessmentItemBuilder; import org.olat.ims.qti21.ui.ResourcesMapper; import org.olat.ims.qti21.ui.components.FlowFormItem; import org.olat.ims.qti21.ui.editor.AssessmentTestEditorController; +import org.olat.ims.qti21.ui.editor.SyncAssessmentItem; import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent; import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic; @@ -62,11 +65,13 @@ import uk.ac.ed.ph.jqtiplus.value.Orientation; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class SingleChoiceEditorController extends FormBasicController { +public class SingleChoiceEditorController extends FormBasicController implements SyncAssessmentItem { private TextElement titleEl; private RichTextElement textEl; - private SingleSelection shuffleEl, orientationEl, alignmentEl; + private SingleSelection shuffleEl; + private SingleSelection orientationEl; + private SingleSelection alignmentEl; private FormLayoutContainer answersCont; private final List<SimpleChoiceWrapper> choiceWrappers = new ArrayList<>(); @@ -99,6 +104,7 @@ public class SingleChoiceEditorController extends FormBasicController { itemContainer = (VFSContainer)rootContainer.resolve(relativePath); initForm(ureq); + validateScoreOfCorrectAnswer(); } @Override @@ -237,9 +243,14 @@ public class SingleChoiceEditorController extends FormBasicController { // } + @Override + public void sync(UserRequest ureq, AssessmentItemBuilder builder) { + validateScoreOfCorrectAnswer(); + } + @Override protected boolean validateFormLogic(UserRequest ureq) { - boolean allOk = true; + boolean allOk = super.validateFormLogic(ureq); titleEl.clearError(); if(!StringHelper.containsNonWhitespace(titleEl.getValue())) { @@ -255,8 +266,38 @@ public class SingleChoiceEditorController extends FormBasicController { answersCont.setErrorKey("error.need.correct.answer", null); } } + validateScoreOfCorrectAnswer();// the validation is not mandatory, issue onnly a warning - return allOk & super.validateFormLogic(ureq); + return allOk; + } + + private void validateScoreOfCorrectAnswer() { + try { + Boolean warning = (Boolean)answersCont.contextGet("scoreWarning"); + Boolean newWarning; + if(itemBuilder.getScoreEvaluationMode() == ScoreEvaluation.perAnswer) { + boolean wrongAnswerHasPoint = false; + boolean correctAnswerHasNotPoint = false; + for(SimpleChoiceWrapper choiceWrapper:choiceWrappers) { + Double score = itemBuilder.getMapping(choiceWrapper.getIdentifier()); + if(choiceWrapper.isCorrect()) { + correctAnswerHasNotPoint = (score == null || score.doubleValue() < 0.000001); + } else { + wrongAnswerHasPoint |= (score != null && score.doubleValue() > 0.6); + } + } + newWarning = Boolean.valueOf(wrongAnswerHasPoint && correctAnswerHasNotPoint); + } else { + newWarning = Boolean.FALSE; + } + + if(warning == null || !warning.equals(newWarning)) { + answersCont.contextPut("scoreWarning", newWarning); + } + } catch (Exception e) { + // not a critical feature, don't produce red screen + logError("", e); + } } @Override @@ -297,6 +338,7 @@ public class SingleChoiceEditorController extends FormBasicController { choiceList.add(choice); } itemBuilder.setSimpleChoices(choiceList); + validateScoreOfCorrectAnswer(); fireEvent(ureq, new AssessmentItemEvent(AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED, itemBuilder.getAssessmentItem(), QTI21QuestionType.sc)); } diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/multiple_choices.html b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/multiple_choices.html index 61588709f66ecd021883c3d3a9502be06d8dc887..3ca1a74ebc3f8e85fb681fd13bb7277d333e7e55 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/multiple_choices.html +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/multiple_choices.html @@ -1,4 +1,7 @@ <fieldset class="o_form form-horizontal"> +#if($r.isTrue($scoreWarning)) + <div class="o_warning" role="alert">$r.translate("warning.correct.answers.score")</div> +#end <div class="form-group #if($f.hasError($item)) has-feedback has-error #end clearfix"> <div class="col-sm-1 col-md-offset-1"><strong>$r.translate("correct.answers")</strong></div> <div class="col-sm-10"><strong>$r.translate("answers")</strong></div> diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/simple_choices.html b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/simple_choices.html index cd31f3fa733bfc1e2d31d22896b683d5f56e38db..0b1bdfa0881c1f303bc08dbb9e84ec3096522d06 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/simple_choices.html +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/_content/simple_choices.html @@ -1,9 +1,11 @@ <fieldset class="o_form form-horizontal"> +#if($r.isTrue($scoreWarning)) + <div class="o_warning" role="alert">$r.translate("warning.correct.answer.score")</div> +#end <div class="form-group #if($f.hasError($item)) has-feedback has-error #end clearfix"> <div class="col-sm-1 col-md-offset-1"><strong>$r.translate("correct.answers")</strong></div> <div class="col-sm-10"><strong>$r.translate("answers")</strong></div> </div> - #foreach($choice in $choices) <div class="form-group #if($f.hasError($item)) has-feedback has-error #end clearfix o_sel_choice_${foreach.index}"> <div class="col-sm-1">