diff --git a/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java b/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java index 6fc5109bb62f3b3a56f06c2ecc0fc5f77d2add3a..e4956d13bc5eb052b484379e8c45a77c77d5fbce 100644 --- a/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java +++ b/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java @@ -227,7 +227,7 @@ public class ScoreAccountingHelper { if (attemptsOk) { Integer attempts = am.getNodeAttempts(acnode, identity); - int a = attempts.intValue(); + int a = attempts == null ? 0 : attempts.intValue(); nodeColumnOk = true; tabs.append("\t"); // tabulators for header1 after node title diff --git a/src/main/java/org/olat/ims/qti21/QTI21Constants.java b/src/main/java/org/olat/ims/qti21/QTI21Constants.java index ff159cb1d83c278e5a749ff283f70cf11d2fb308..58d4c6c413e309150a3c6488398489f8676002bb 100644 --- a/src/main/java/org/olat/ims/qti21/QTI21Constants.java +++ b/src/main/java/org/olat/ims/qti21/QTI21Constants.java @@ -94,5 +94,25 @@ public class QTI21Constants { public static final Identifier EMPTY_IDENTIFIER = Identifier.parseString(EMPTY); public static final IdentifierValue EMPTY_IDENTIFIER_VALUE = new IdentifierValue(EMPTY); + + + public static final String HINT = "HINT"; + + public static final Identifier HINT_IDENTIFIER = Identifier.parseString(HINT); + + /** This is the variable identifer */ + public static final String HINT_REQUEST = "HINTREQUEST"; + + public static final Identifier HINT_REQUEST_IDENTIFIER = Identifier.parseString(HINT_REQUEST); + + public static final ComplexReferenceIdentifier HINT_REQUEST_CLX_IDENTIFIER = ComplexReferenceIdentifier.parseString(HINT_REQUEST); + + /** This is the outcome declaration identifier */ + public static final String HINT_FEEDBACKMODAL = "HINTFEEDBACKMODAL"; + + public static final Identifier HINT_FEEDBACKMODAL_IDENTIFIER = Identifier.parseString(HINT_FEEDBACKMODAL); + + public static final ComplexReferenceIdentifier HINT_FEEDBACKMODAL_CLX_IDENTIFIER = ComplexReferenceIdentifier.parseString(HINT_FEEDBACKMODAL); + } diff --git a/src/main/java/org/olat/ims/qti21/model/QTI21QuestionType.java b/src/main/java/org/olat/ims/qti21/model/QTI21QuestionType.java index 24fc0310ad26546027288270ea59527d6677af9a..9dd1b47f5bd173c6c492545b9ae9089bbfb6b387 100644 --- a/src/main/java/org/olat/ims/qti21/model/QTI21QuestionType.java +++ b/src/main/java/org/olat/ims/qti21/model/QTI21QuestionType.java @@ -26,6 +26,7 @@ import org.olat.modules.qpool.QuestionType; import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem; import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction; +import uk.ac.ed.ph.jqtiplus.node.item.interaction.EndAttemptInteraction; import uk.ac.ed.ph.jqtiplus.node.item.interaction.ExtendedTextInteraction; import uk.ac.ed.ph.jqtiplus.node.item.interaction.HotspotInteraction; import uk.ac.ed.ph.jqtiplus.node.item.interaction.Interaction; @@ -120,7 +121,9 @@ public enum QTI21QuestionType { fTextEntry = true; } else if(interaction instanceof HotspotInteraction) { fHotspot = true; - } else { + } else if(interaction instanceof EndAttemptInteraction) { + //ignore + } else { fUnkown = true; } } @@ -129,9 +132,9 @@ public enum QTI21QuestionType { if(fUnkown) { return QTI21QuestionType.unkown; } else if(fChoice && !fMatch && !fTextEntry && !fEssay && !fHotspot && !fUnkown) { - return getTypeOfChoice(item); + return getTypeOfChoice(item, interactions); } else if(!fChoice && fMatch && !fTextEntry && !fEssay && !fHotspot && !fUnkown) { - return getTypeOfMatch(item); + return getTypeOfMatch(item, interactions); } else if(!fChoice && !fMatch && fTextEntry && !fEssay && !fHotspot && !fUnkown) { return getTypeOfTextEntryInteraction(item); } else if(!fChoice && !fMatch && !fTextEntry && fEssay && !fHotspot && !fUnkown) { @@ -156,7 +159,7 @@ public enum QTI21QuestionType { foundText++; } else if(responseDeclaration.hasBaseType(BaseType.FLOAT)) { foundNumerical++; - } else { + } else if(!responseDeclaration.getIdentifier().equals(QTI21Constants.HINT_REQUEST_IDENTIFIER)) { foundUnkown++; } } @@ -171,9 +174,10 @@ public enum QTI21QuestionType { return QTI21QuestionType.unkown; } - private static final QTI21QuestionType getTypeOfMatch(AssessmentItem item) { - if(item.getResponseDeclarations().size() == 1) { - ResponseDeclaration responseDeclaration = item.getResponseDeclarations().get(0); + private static final QTI21QuestionType getTypeOfMatch(AssessmentItem item, List<Interaction> interactions) { + Interaction interaction = interactions.get(0); + if(item.getResponseDeclaration(interaction.getResponseIdentifier()) != null) { + ResponseDeclaration responseDeclaration = item.getResponseDeclaration(interaction.getResponseIdentifier()); String responseIdentifier = responseDeclaration.getIdentifier().toString(); Cardinality cardinalty = responseDeclaration.getCardinality(); if(cardinalty.isMultiple()) { @@ -190,9 +194,10 @@ public enum QTI21QuestionType { } } - private static final QTI21QuestionType getTypeOfChoice(AssessmentItem item) { - if(item.getResponseDeclarations().size() == 1) { - ResponseDeclaration responseDeclaration = item.getResponseDeclarations().get(0); + private static final QTI21QuestionType getTypeOfChoice(AssessmentItem item, List<Interaction> interactions) { + Interaction interaction = interactions.get(0); + if(item.getResponseDeclaration(interaction.getResponseIdentifier()) != null) { + ResponseDeclaration responseDeclaration = item.getResponseDeclaration(interaction.getResponseIdentifier()); Cardinality cardinalty = responseDeclaration.getCardinality(); if(cardinalty.isSingle()) { return QTI21QuestionType.sc; diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java index 260ad7a50c4134e6b068a9fba4326d0997ee6bc7..ea1a0bdd28b772ff3eb3d11651ed2e213bbef3c2 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java @@ -28,17 +28,25 @@ import java.util.List; import org.olat.ims.qti21.QTI21Constants; import org.olat.ims.qti21.model.QTI21QuestionType; +import uk.ac.ed.ph.jqtiplus.node.content.xhtml.text.P; +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.item.AssessmentItem; import uk.ac.ed.ph.jqtiplus.node.item.ModalFeedback; +import uk.ac.ed.ph.jqtiplus.node.item.interaction.EndAttemptInteraction; import uk.ac.ed.ph.jqtiplus.node.item.interaction.Interaction; import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.ResponseDeclaration; import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseCondition; +import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseIf; import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseProcessing; import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseRule; +import uk.ac.ed.ph.jqtiplus.node.item.response.processing.SetOutcomeValue; import uk.ac.ed.ph.jqtiplus.node.outcome.declaration.OutcomeDeclaration; import uk.ac.ed.ph.jqtiplus.node.shared.declaration.DefaultValue; import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer; +import uk.ac.ed.ph.jqtiplus.value.BaseType; import uk.ac.ed.ph.jqtiplus.value.FloatValue; +import uk.ac.ed.ph.jqtiplus.value.IdentifierValue; import uk.ac.ed.ph.jqtiplus.value.Value; /** @@ -58,6 +66,7 @@ public abstract class AssessmentItemBuilder { private ScoreBuilder minScoreBuilder; private ScoreBuilder maxScoreBuilder; + protected ModalFeedbackBuilder hint; protected ModalFeedbackBuilder emptyFeedback; protected ModalFeedbackBuilder correctFeedback; protected ModalFeedbackBuilder incorrectFeedback; @@ -121,6 +130,8 @@ public abstract class AssessmentItemBuilder { incorrectFeedback = feedbackBuilder; } else if(feedbackBuilder.isEmptyRule()) { emptyFeedback = feedbackBuilder; + } else if(feedbackBuilder.isHint()) { + hint = feedbackBuilder; } else { additionalFeedbacks.add(feedbackBuilder); } @@ -163,6 +174,19 @@ public abstract class AssessmentItemBuilder { } } + public ModalFeedbackBuilder getHint() { + return hint; + } + + public ModalFeedbackBuilder createHint() { + hint = new ModalFeedbackBuilder(assessmentItem, null); + return hint; + } + + public void removeHint() { + hint = null; + } + public ModalFeedbackBuilder getCorrectFeedback() { return correctFeedback; } @@ -184,6 +208,7 @@ public abstract class AssessmentItemBuilder { emptyFeedback = new ModalFeedbackBuilder(assessmentItem, null); return emptyFeedback; } + public void removeEmptyFeedback() { emptyFeedback = null; } @@ -231,25 +256,99 @@ public abstract class AssessmentItemBuilder { List<ResponseDeclaration> responseDeclarations = assessmentItem.getResponseDeclarations(); responseDeclarations.clear(); + List<ModalFeedback> modalFeedbacks = assessmentItem.getModalFeedbacks(); + modalFeedbacks.clear(); + buildItemBody(); - buildResponseDeclaration(); - buildModalFeedbacks(outcomeDeclarations, responseRules); + buildResponseAndOutcomeDeclarations(); + buildModalFeedbacksAndHints(outcomeDeclarations, responseRules); buildMinMaxScores(outcomeDeclarations, responseRules); buildMainScoreRule(outcomeDeclarations, responseRules); + buildHint(outcomeDeclarations, responseRules); } - protected void buildResponseDeclaration() { - // - } + protected abstract void buildItemBody(); - protected void buildItemBody() { - // + protected abstract void buildResponseAndOutcomeDeclarations(); + + protected void ensureFeedbackBasicOutcomeDeclaration() { + OutcomeDeclaration feedbackBasicDeclaration = assessmentItem.getOutcomeDeclaration(QTI21Constants.FEEDBACKBASIC_IDENTIFIER); + if(feedbackBasicDeclaration == null) { + feedbackBasicDeclaration = AssessmentItemFactory + .createOutcomeDeclarationForFeedbackBasic(assessmentItem); + assessmentItem.getOutcomeDeclarations().add(feedbackBasicDeclaration); + } } protected abstract void buildMainScoreRule(List<OutcomeDeclaration> outcomeDeclarations, List<ResponseRule> responseRules); - protected void buildModalFeedbacks(List<OutcomeDeclaration> outcomeDeclarations, List<ResponseRule> responseRules) { - //add feedbackbasic and feedbackmodal outcomes + /** + * + * @param outcomeDeclarations + * @param responseRules + */ + protected void buildHint(List<OutcomeDeclaration> outcomeDeclarations, List<ResponseRule> responseRules) { + if(hint == null) return; + + //response declaration -> identifier=HINTREQUEST -> for the end attempt interaction + ResponseDeclaration hintResponseDeclaration = AssessmentItemFactory + .createHintRequestResponseDeclaration(assessmentItem); + assessmentItem.getResponseDeclarations().add(hintResponseDeclaration); + + //outcome declaration -> identifier=HINTFEEDBACKMODAL -> for processing and feedback + OutcomeDeclaration modalOutcomeDeclaration = AssessmentItemFactory + .createOutcomeDeclarationForHint(assessmentItem); + outcomeDeclarations.add(modalOutcomeDeclaration); + + //the body + P paragraph = new P(assessmentItem.getItemBody()); + assessmentItem.getItemBody().getBlocks().add(paragraph); + + EndAttemptInteraction endAttemptInteraction = new EndAttemptInteraction(paragraph); + endAttemptInteraction.setResponseIdentifier(QTI21Constants.HINT_REQUEST_IDENTIFIER); + endAttemptInteraction.setTitle(hint.getTitle()); + + paragraph.getInlines().add(endAttemptInteraction); + + //the feedback + ModalFeedback emptyModalFeedback = AssessmentItemFactory + .createModalFeedback(assessmentItem, QTI21Constants.HINT_FEEDBACKMODAL_IDENTIFIER, QTI21Constants.HINT_IDENTIFIER, + hint.getTitle(), hint.getText()); + assessmentItem.getModalFeedbacks().add(emptyModalFeedback); + + //the response processing + ResponseCondition rule = new ResponseCondition(assessmentItem.getResponseProcessing()); + responseRules.add(0, rule); + + ResponseIf responseIf = new ResponseIf(rule); + rule.setResponseIf(responseIf); + /* + <responseIf> + <variable identifier="HINTREQUEST"/> + <setOutcomeValue identifier="FEEDBACK"> + <baseValue baseType="identifier">HINT</baseValue> + </setOutcomeValue> + </responseIf> + */ + Variable variable = new Variable(responseIf); + variable.setIdentifier(QTI21Constants.HINT_REQUEST_CLX_IDENTIFIER); + responseIf.getExpressions().add(variable); + + SetOutcomeValue hintVar = new SetOutcomeValue(responseIf); + hintVar.setIdentifier(QTI21Constants.HINT_FEEDBACKMODAL_IDENTIFIER); + BaseValue hintVal = new BaseValue(hintVar); + hintVal.setBaseTypeAttrValue(BaseType.IDENTIFIER); + hintVal.setSingleValue(new IdentifierValue(QTI21Constants.HINT)); + hintVar.setExpression(hintVal); + responseIf.getResponseRules().add(hintVar); + } + + /** + * Add feedbackbasic and feedbackmodal outcomes + * @param outcomeDeclarations + * @param responseRules + */ + protected void buildModalFeedbacksAndHints(List<OutcomeDeclaration> outcomeDeclarations, List<ResponseRule> responseRules) { if(correctFeedback != null || incorrectFeedback != null || emptyFeedback != null || additionalFeedbacks.size() > 0) { ensureFeedbackBasicOutcomeDeclaration(); @@ -257,20 +356,17 @@ public abstract class AssessmentItemBuilder { OutcomeDeclaration modalOutcomeDeclaration = AssessmentItemFactory .createOutcomeDeclarationForFeedbackModal(assessmentItem); outcomeDeclarations.add(modalOutcomeDeclaration); - } - //add modal - List<ModalFeedback> modalFeedbacks = assessmentItem.getModalFeedbacks(); - modalFeedbacks.clear(); - - if(correctFeedback != null) { - appendModalFeedback(correctFeedback, QTI21Constants.CORRECT, modalFeedbacks, responseRules); - } - if(incorrectFeedback != null) { - appendModalFeedback(incorrectFeedback, QTI21Constants.INCORRECT, modalFeedbacks, responseRules); - } - if(emptyFeedback != null) { - appendModalFeedback(emptyFeedback, QTI21Constants.EMPTY, modalFeedbacks, responseRules); + List<ModalFeedback> modalFeedbacks = assessmentItem.getModalFeedbacks(); + if(correctFeedback != null) { + appendModalFeedback(correctFeedback, QTI21Constants.CORRECT, modalFeedbacks, responseRules); + } + if(incorrectFeedback != null) { + appendModalFeedback(incorrectFeedback, QTI21Constants.INCORRECT, modalFeedbacks, responseRules); + } + if(emptyFeedback != null) { + appendModalFeedback(emptyFeedback, QTI21Constants.EMPTY, modalFeedbacks, responseRules); + } } } @@ -287,15 +383,6 @@ public abstract class AssessmentItemBuilder { responseRules.add(feedbackCondition); } - protected void ensureFeedbackBasicOutcomeDeclaration() { - OutcomeDeclaration feedbackBasicDeclaration = assessmentItem.getOutcomeDeclaration(QTI21Constants.FEEDBACKBASIC_IDENTIFIER); - if(feedbackBasicDeclaration == null) { - feedbackBasicDeclaration = AssessmentItemFactory - .createOutcomeDeclarationForFeedbackBasic(assessmentItem); - assessmentItem.getOutcomeDeclarations().add(feedbackBasicDeclaration); - } - } - /** * Add outcome declaration for score, min. score and mx. score. * and the response rules which ensure that the score is between diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java index 279e3334e02091c847584624a81dd9bd73659db4..5cfbb320751982f8131eb6443148606bbe56fa25 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java @@ -55,6 +55,7 @@ import uk.ac.ed.ph.jqtiplus.node.expression.operator.IsNull; 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.Not; import uk.ac.ed.ph.jqtiplus.node.expression.operator.Shape; import uk.ac.ed.ph.jqtiplus.node.expression.operator.Sum; import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem; @@ -493,6 +494,15 @@ public class AssessmentItemFactory { return responseDeclaration; } + //<responseDeclaration identifier="HINTREQUEST" cardinality="single" baseType="boolean"/> + public static ResponseDeclaration createHintRequestResponseDeclaration(AssessmentItem assessmentItem) { + ResponseDeclaration responseDeclaration = new ResponseDeclaration(assessmentItem); + responseDeclaration.setIdentifier(QTI21Constants.HINT_REQUEST_IDENTIFIER); + responseDeclaration.setCardinality(Cardinality.SINGLE); + responseDeclaration.setBaseType(BaseType.IDENTIFIER); + return responseDeclaration; + } + public static SimpleChoice createSimpleChoice(ChoiceInteraction choiceInteraction, String text, String prefix) { SimpleChoice newChoice = new SimpleChoice(choiceInteraction); newChoice.setIdentifier(IdentifierGenerator.newAsIdentifier(prefix)); @@ -728,6 +738,15 @@ public class AssessmentItemFactory { return feedbackOutcomeDeclaration; } + public static OutcomeDeclaration createOutcomeDeclarationForHint(AssessmentItem assessmentItem) { + OutcomeDeclaration feedbackOutcomeDeclaration = new OutcomeDeclaration(assessmentItem); + feedbackOutcomeDeclaration.setIdentifier(QTI21Constants.HINT_FEEDBACKMODAL_IDENTIFIER); + feedbackOutcomeDeclaration.setCardinality(Cardinality.SINGLE); + feedbackOutcomeDeclaration.setBaseType(BaseType.IDENTIFIER); + + return feedbackOutcomeDeclaration; + } + public static OutcomeDeclaration createOutcomeDeclarationForFeedbackModal(AssessmentItem assessmentItem) { OutcomeDeclaration feedbackOutcomeDeclaration = new OutcomeDeclaration(assessmentItem); feedbackOutcomeDeclaration.setIdentifier(QTI21Constants.FEEDBACKMODAL_IDENTIFIER); @@ -770,6 +789,11 @@ public class AssessmentItemFactory { } public static ModalFeedback createModalFeedback(AssessmentItem assessmentItem, Identifier identifier, String title, String text) { + return createModalFeedback(assessmentItem, QTI21Constants.FEEDBACKMODAL_IDENTIFIER, identifier, title, text); + } + + public static ModalFeedback createModalFeedback(AssessmentItem assessmentItem, Identifier outcomeIdentifier, Identifier identifier, + String title, String text) { /* <modalFeedback identifier="Feedback1041659806" outcomeIdentifier="FEEDBACKMODAL" showHide="show" title="Wrong answer"> <p>Feedback answer</p> @@ -778,7 +802,7 @@ public class AssessmentItemFactory { ModalFeedback modalFeedback = new ModalFeedback(assessmentItem); modalFeedback.setIdentifier(identifier); - modalFeedback.setOutcomeIdentifier(QTI21Constants.FEEDBACKMODAL_IDENTIFIER); + modalFeedback.setOutcomeIdentifier(outcomeIdentifier); modalFeedback.setVisibilityMode(VisibilityMode.parseVisibilityMode("show")); modalFeedback.getAttributes().getStringAttribute(ModalFeedback.ATTR_TITLE_NAME).setValue(title); @@ -822,7 +846,7 @@ public class AssessmentItemFactory { Match match = new Match(and); and.getExpressions().add(match); - + BaseValue feedbackVal = new BaseValue(match); feedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER); feedbackVal.setSingleValue(new IdentifierValue(inCorrect)); @@ -831,6 +855,23 @@ public class AssessmentItemFactory { Variable variable = new Variable(match); variable.setIdentifier(ComplexReferenceIdentifier.parseString(QTI21Constants.FEEDBACKBASIC)); match.getExpressions().add(variable); + + //not match the HINT + Not not = new Not(and); + and.getExpressions().add(not); + + Match notMatch = new Match(and); + not.getExpressions().add(notMatch); + + BaseValue hintVal = new BaseValue(notMatch); + hintVal.setBaseTypeAttrValue(BaseType.IDENTIFIER); + hintVal.setSingleValue(new IdentifierValue(QTI21Constants.HINT)); + notMatch.getExpressions().add(hintVal); + + Variable hintVar = new Variable(notMatch); + hintVar.setIdentifier(QTI21Constants.HINT_REQUEST_CLX_IDENTIFIER); + notMatch.getExpressions().add(hintVar); + } {//outcome diff --git a/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java index 7b78b076fdec2d202790ef4e59e631e9d0f266f3..c5ca7d572e0e76afa736376a7e0713b236bc569a 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java @@ -89,6 +89,11 @@ public class ModalFeedbackBuilder { return findFeedbackRule(feedbackRule, QTI21Constants.EMPTY_IDENTIFIER); } + public boolean isHint() { + return modalFeedback.getIdentifier().equals(QTI21Constants.HINT_IDENTIFIER) + && modalFeedback.getOutcomeIdentifier().equals(QTI21Constants.HINT_FEEDBACKMODAL_IDENTIFIER); + } + public String getTitle() { return title; } diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/EssayAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/EssayAssessmentItemBuilder.java index 2c34305e0c0ae543a2ad0f1e87b2aae0ad451296..b46b10d0a890be25e415ebbbbde6fa7ee43e5a25 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/EssayAssessmentItemBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/EssayAssessmentItemBuilder.java @@ -180,7 +180,7 @@ public class EssayAssessmentItemBuilder extends AssessmentItemBuilder { } @Override - protected void buildResponseDeclaration() { + protected void buildResponseAndOutcomeDeclarations() { ResponseDeclaration responseDeclaration = createExtendedTextResponseDeclaration(assessmentItem, responseIdentifier); assessmentItem.getResponseDeclarations().add(responseDeclaration); diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/FIBAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/FIBAssessmentItemBuilder.java index b5badada957958ef434682404a27c3776ce27000..b3a785bec1fa3e3c3cf5b55ab48b68b3a583166a 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/FIBAssessmentItemBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/FIBAssessmentItemBuilder.java @@ -466,7 +466,7 @@ public class FIBAssessmentItemBuilder extends AssessmentItemBuilder { } @Override - protected void buildResponseDeclaration() { + protected void buildResponseAndOutcomeDeclarations() { List<ResponseDeclaration> responseDeclarations = assessmentItem.getResponseDeclarations(); /* diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/HotspotAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/HotspotAssessmentItemBuilder.java index 66fcab0d32908d1282744903dc25caab74499b31..ef5a88a675d32e45f8b6ccae9817746db1066944 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/HotspotAssessmentItemBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/HotspotAssessmentItemBuilder.java @@ -305,7 +305,7 @@ public class HotspotAssessmentItemBuilder extends AssessmentItemBuilder { } @Override - protected void buildResponseDeclaration() { + protected void buildResponseAndOutcomeDeclarations() { ResponseDeclaration responseDeclaration = AssessmentItemFactory .createHotspotCorrectResponseDeclaration(assessmentItem, responseIdentifier, correctAnswers); if(scoreEvaluation == ScoreEvaluation.perAnswer) { diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/KPrimAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/KPrimAssessmentItemBuilder.java index d47d68971505c05e7fbf10f8beb07c221ceee9f9..2bcf110bc707e6ba3a09f7b5c7e37736c2a9e6f8 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/KPrimAssessmentItemBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/KPrimAssessmentItemBuilder.java @@ -218,7 +218,7 @@ public class KPrimAssessmentItemBuilder extends AssessmentItemBuilder { } @Override - protected void buildResponseDeclaration() { + protected void buildResponseAndOutcomeDeclarations() { //need min. and max. score double maxScore = getMaxScoreBuilder().getScore(); diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/MultipleChoiceAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/MultipleChoiceAssessmentItemBuilder.java index fdcaa4ad3591e1a7d9d32eb7c24c67ad061997a7..2798ce968809f94bcffab422fb0b1c836cee1593 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/MultipleChoiceAssessmentItemBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/MultipleChoiceAssessmentItemBuilder.java @@ -163,7 +163,7 @@ public class MultipleChoiceAssessmentItemBuilder extends SimpleChoiceAssessmentI } @Override - protected void buildResponseDeclaration() { + protected void buildResponseAndOutcomeDeclarations() { ResponseDeclaration responseDeclaration = AssessmentItemFactory .createMultipleChoiceCorrectResponseDeclaration(assessmentItem, responseIdentifier, correctAnswers); if(scoreEvaluation == ScoreEvaluation.perAnswer) { diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/SimpleChoiceAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/SimpleChoiceAssessmentItemBuilder.java index fa60a4aeb100a5b434909b7440516ae9027a6a10..f3d506696cea2f8aaaff118a685d062daf60479c 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/SimpleChoiceAssessmentItemBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/SimpleChoiceAssessmentItemBuilder.java @@ -216,7 +216,7 @@ public abstract class SimpleChoiceAssessmentItemBuilder extends ChoiceAssessment } @Override - protected void buildModalFeedbacks(List<OutcomeDeclaration> outcomeDeclarations, List<ResponseRule> responseRules) { + protected void buildModalFeedbacksAndHints(List<OutcomeDeclaration> outcomeDeclarations, List<ResponseRule> responseRules) { if(correctFeedback != null || incorrectFeedback != null) { /* <responseCondition> @@ -288,7 +288,7 @@ public abstract class SimpleChoiceAssessmentItemBuilder extends ChoiceAssessment correctOutcomeValue.setExpression(correctValue); } - super.buildModalFeedbacks(outcomeDeclarations, responseRules); + super.buildModalFeedbacksAndHints(outcomeDeclarations, responseRules); } public enum ScoreEvaluation { diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/SingleChoiceAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/SingleChoiceAssessmentItemBuilder.java index e723a948798e2a2f2f9df96c4696ef06618681ac..4ac552b307f2b22ed7b1afdc61c6a7df2e6c39fc 100644 --- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/SingleChoiceAssessmentItemBuilder.java +++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/SingleChoiceAssessmentItemBuilder.java @@ -112,15 +112,17 @@ public class SingleChoiceAssessmentItemBuilder extends SimpleChoiceAssessmentIte public void extract() { super.extract(); - List<ResponseDeclaration> responseDeclarations = assessmentItem.getResponseDeclarations(); - if(responseDeclarations.size() == 1) { - CorrectResponse correctResponse = responseDeclarations.get(0).getCorrectResponse(); - if(correctResponse != null) { - List<FieldValue> values = correctResponse.getFieldValues(); - Value value = FieldValue.computeValue(Cardinality.SINGLE, values); - if(value instanceof IdentifierValue) { - IdentifierValue identifierValue = (IdentifierValue)value; - correctAnswer = identifierValue.identifierValue(); + if(choiceInteraction != null) { + ResponseDeclaration responseDeclaration = assessmentItem.getResponseDeclaration(choiceInteraction.getResponseIdentifier()); + if(responseDeclaration != null) { + CorrectResponse correctResponse = responseDeclaration.getCorrectResponse(); + if(correctResponse != null) { + List<FieldValue> values = correctResponse.getFieldValues(); + Value value = FieldValue.computeValue(Cardinality.SINGLE, values); + if(value instanceof IdentifierValue) { + IdentifierValue identifierValue = (IdentifierValue)value; + correctAnswer = identifierValue.identifierValue(); + } } } } @@ -141,7 +143,7 @@ public class SingleChoiceAssessmentItemBuilder extends SimpleChoiceAssessmentIte } @Override - protected void buildResponseDeclaration() { + protected void buildResponseAndOutcomeDeclarations() { ResponseDeclaration responseDeclaration = AssessmentItemFactory .createSingleChoiceCorrectResponseDeclaration(assessmentItem, responseIdentifier, correctAnswer); if(scoreEvaluation == ScoreEvaluation.perAnswer) { diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java index e79bef2af1b286d01ed9cb18e86ccd43ac0c1d1a..6213d5e2e63a3612d6e987d7971a24385d34954b 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java @@ -42,6 +42,9 @@ import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent; */ public class FeedbackEditorController extends FormBasicController { + private TextElement hintTitleEl; + private RichTextElement hintTextEl; + private TextElement feedbackCorrectTitleEl, feedbackIncorrectTitleEl, feedbackEmptyTitleEl; private RichTextElement feedbackCorrectTextEl, feedbackIncorrectTextEl, feedbackEmptyTextEl; @@ -62,6 +65,19 @@ public class FeedbackEditorController extends FormBasicController { @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + + ModalFeedbackBuilder hint = itemBuilder.getHint(); + String hintTitle = hint == null ? "" : hint.getTitle(); + hintTitleEl = uifactory.addTextElement("hintTitle", "form.imd.hint.title", -1, hintTitle, formLayout); + hintTitleEl.setUserObject(hint); + hintTitleEl.setEnabled(!restrictedEdit); + String hintText = hint == null ? "" : hint.getText(); + hintTextEl = uifactory.addRichTextElementForStringDataCompact("hintText", "form.imd.hint.text", hintText, 8, -1, null, + formLayout, ureq.getUserSession(), getWindowControl()); + hintTextEl.setEnabled(!restrictedEdit); + RichTextConfiguration hintConfig = hintTextEl.getEditorConfiguration(); + hintConfig.setFileBrowserUploadRelPath("media");// set upload dir to the media dir + //correct feedback if(correct) { ModalFeedbackBuilder correctFeedback = itemBuilder.getCorrectFeedback(); @@ -119,6 +135,19 @@ public class FeedbackEditorController extends FormBasicController { protected void formOK(UserRequest ureq) { if(restrictedEdit) return; + String hintTitle = hintTitleEl.getValue(); + String hintText = hintTextEl.getValue(); + if(StringHelper.containsNonWhitespace(FilterFactory.getHtmlTagsFilter().filter(hintText))) { + ModalFeedbackBuilder hintBuilder = itemBuilder.getHint(); + if(hintBuilder == null) { + hintBuilder = itemBuilder.createHint(); + } + hintBuilder.setTitle(hintTitle); + hintBuilder.setText(hintText); + } else { + itemBuilder.removeHint(); + } + if(correct) { String correctTitle = feedbackCorrectTitleEl.getValue(); String correctText = feedbackCorrectTextEl.getValue(); 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 e506533f0eca3882a8fd28ea22407029ba404df5..cd0e24cd7d4b571c9258383d5ac7a624cb738383 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 @@ -45,6 +45,8 @@ form.imd.correct.title=Titel form.imd.descr=Antwort form.imd.empty.text=Feedback bei Wahl keiner Antworten form.imd.empty.title=Titel +form.imd.hint.title=Hint Title +form.imd.hint.text=Hint form.imd.incorrect.text=Feedback bei Wahl einer falschen Antwort form.imd.incorrect.title=Titel form.imd.layout.horizontal=Horizontal 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 9a94b6b6634a0b7a40ff0a970e348be4918d463f..3b12de88a9db6213c24683de437a2780fee14c53 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 @@ -42,6 +42,8 @@ form.imd.background=Background form.imd.correct.kprim=$org.olat.ims.qti.editor\:questionform_correct_kprim form.imd.wrong.kprim=$org.olat.ims.qti.editor\:questionform_wrong_kprim form.imd.answer=Answer +form.imd.hint.title=Hint title +form.imd.hint.text=Hint form.imd.layout.horizontal=Horizontal form.imd.layout.vertical=Vertical form.imd.correct.title=Correct title diff --git a/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java b/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java index ac9a2f4122c1429fdb99d8b6159376daae7d8966..51bfb58fe5649254514bb0fe19b45821982b5136 100644 --- a/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java +++ b/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java @@ -185,7 +185,14 @@ public class AssessmentEntryDAO { .getResultList(); } - public int deleteEntryForReferenceEntry(RepositoryEntry entry) { + /** + * Delete all the entry where the specified repository entry is + * referenced as a test. + * + * @param entry + * @return + */ + public int deleteEntryForReferenceEntry(RepositoryEntryRef entry) { StringBuilder sb = new StringBuilder(); sb.append("delete from assessmententry data where data.referenceEntry.key=:referenceKey"); return dbInstance.getCurrentEntityManager() @@ -193,4 +200,20 @@ public class AssessmentEntryDAO { .setParameter("referenceKey", entry.getKey()) .executeUpdate(); } + + /** + * Delete all entries where the specified repository entry (typically + * a course) is linked to them. + * + * @param entry + * @return + */ + public int deleteEntryForRepositoryEntry(RepositoryEntryRef entry) { + StringBuilder sb = new StringBuilder(); + sb.append("delete from assessmententry data where data.repositoryEntry.key=:entryKey"); + return dbInstance.getCurrentEntityManager() + .createQuery(sb.toString()) + .setParameter("entryKey", entry.getKey()) + .executeUpdate(); + } } diff --git a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java index f1085a56707656351dd30a791c7487f89d2418d4..69260608a20f6d47db23df9bfa0f8eb504a3e884 100644 --- a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java +++ b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java @@ -60,6 +60,7 @@ import org.olat.core.util.vfs.VFSManager; import org.olat.course.assessment.manager.AssessmentModeDAO; import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.certificate.CertificatesManager; +import org.olat.modules.assessment.manager.AssessmentEntryDAO; import org.olat.modules.reminder.manager.ReminderDAO; import org.olat.repository.ErrorList; import org.olat.repository.RepositoryEntry; @@ -137,6 +138,8 @@ public class RepositoryServiceImpl implements RepositoryService { private PersistentTaskDAO persistentTaskDao; @Autowired private ReminderDAO reminderDao; + @Autowired + private AssessmentEntryDAO assessmentEntryDao; @Autowired private LifeFullIndexer lifeIndexer; @@ -360,7 +363,9 @@ public class RepositoryServiceImpl implements RepositoryService { // inform handler to do any cleanup work... handler must delete the // referenced resourceable a swell. handler.cleanupOnDelete(entry, resource); + dbInstance.commit(); + assessmentEntryDao.deleteEntryForRepositoryEntry(entry); dbInstance.commit(); if(debug) log.debug("deleteRepositoryEntry after reload entry=" + entry);