/** * <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.components; import java.io.File; import java.net.URI; import java.nio.file.Path; import java.util.HashMap; import java.util.List; import java.util.Map; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.ims.qti21.ui.components.AssessmentObjectComponentRenderer.RenderingRequest; 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.Interaction; import uk.ac.ed.ph.jqtiplus.node.result.SessionStatus; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; import uk.ac.ed.ph.jqtiplus.node.test.TestPart; import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem; import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest; import uk.ac.ed.ph.jqtiplus.running.ItemProcessingContext; import uk.ac.ed.ph.jqtiplus.running.ItemSessionController; import uk.ac.ed.ph.jqtiplus.running.TestSessionController; import uk.ac.ed.ph.jqtiplus.state.ItemSessionState; import uk.ac.ed.ph.jqtiplus.state.TestPlanNode; import uk.ac.ed.ph.jqtiplus.state.TestPlanNodeKey; import uk.ac.ed.ph.jqtiplus.state.TestSessionState; import uk.ac.ed.ph.jqtiplus.types.Identifier; /** * * Initial date: 10.12.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class AssessmentTestComponent extends AssessmentObjectComponent { private static final AssessmentTestComponentRenderer VELOCITY_RENDERER = new AssessmentTestComponentRenderer(); private static final OLog log = Tracing.createLoggerFor(AssessmentTestComponent.class); private TestSessionController testSessionController; private ResolvedAssessmentTest resolvedAssessmentTest; private boolean renderNavigation; private boolean showTitles; private boolean personalNotes; private final AssessmentTestFormItem qtiItem; private final Map<String,Interaction> responseIdentifiersMap = new HashMap<>(); public AssessmentTestComponent(String name, AssessmentTestFormItem qtiItem) { super(name); this.qtiItem = qtiItem; } public AssessmentTestFormItem getQtiItem() { return qtiItem; } @Override public boolean isSilentlyDynamicalCmp() { return true; } public boolean isRenderNavigation() { return renderNavigation; } public void setRenderNavigation(boolean renderNavigation) { this.renderNavigation = renderNavigation; } public boolean isShowTitles() { return showTitles; } public void setShowTitles(boolean showTitles) { this.showTitles = showTitles; } public boolean isPersonalNotes() { return personalNotes; } public void setPersonalNotes(boolean personalNotes) { this.personalNotes = personalNotes; } public TestSessionController getTestSessionController() { return testSessionController; } public void setTestSessionController(TestSessionController testSessionController) { this.testSessionController = testSessionController; } @Override public String getResponseUniqueIdentifier(ItemSessionState itemSessionState, Interaction interaction) { TestPlanNodeKey tpnk = null; for(Map.Entry<TestPlanNodeKey, ItemSessionState> entry:testSessionController.getTestSessionState().getItemSessionStates().entrySet()) { if(entry.getValue() == itemSessionState) { tpnk = entry.getKey(); break; } } String id = "oo" + (tpnk.toString().replace(":", "_")) + "_" + interaction.getResponseIdentifier().toString(); responseIdentifiersMap.put(id, interaction); return id; } @Override public Interaction getInteractionOfResponseUniqueIdentifier(String responseUniqueId) { return responseIdentifiersMap.get(responseUniqueId); } public ResolvedAssessmentTest getResolvedAssessmentTest() { return resolvedAssessmentTest; } public void setResolvedAssessmentTest(ResolvedAssessmentTest resolvedAssessmentTest) { this.resolvedAssessmentTest = resolvedAssessmentTest; } /** * Check if the assessment item will show some * form of feedback like feedbackElement, modalFeedback * or message as invalid or bad response. * * @param itemNode * @return */ public boolean willShowFeedbacks(TestPlanNode itemNode) { try { URI itemSystemId = itemNode.getItemSystemId(); ResolvedAssessmentItem resolvedAssessmentItem = getResolvedAssessmentTest() .getResolvedAssessmentItemBySystemIdMap().get(itemSystemId); AssessmentItem assessmentItem = resolvedAssessmentItem.getRootNodeLookup().extractIfSuccessful(); if(assessmentItem.getAdaptive()) { return true; } ItemSessionState itemSessionState = getItemSessionState(itemNode.getKey()); if(!itemSessionState.isResponded()) { return true; } ItemProcessingContext itemContext = getTestSessionController().getItemProcessingContext(itemNode); if(itemContext instanceof ItemSessionController) { ItemSessionController itemSessionController = (ItemSessionController)itemContext; List<Interaction> interactions = itemSessionController.getInteractions(); for(Interaction interaction:interactions) { if(AssessmentRenderFunctions.isBadResponse(itemSessionState, interaction.getResponseIdentifier())) { return true; } if(AssessmentRenderFunctions.isInvalidResponse(itemSessionState, interaction.getResponseIdentifier())) { return true; } } } if(assessmentItem.getItemBody().willShowFeedback(itemContext)) { return true; } List<ModalFeedback> modalFeedbacks = assessmentItem.getModalFeedbacks(); for(ModalFeedback modalFeedback:modalFeedbacks) { if(isFeedback(modalFeedback, itemSessionState)) { return true; } } } catch (Exception e) { log.error("", e); } return false; } @Override public String relativePathTo(ResolvedAssessmentItem resolvedAssessmentItem) { if(resolvedAssessmentItem == null) { return "/"; } else { URI itemUri = resolvedAssessmentItem.getItemLookup().getSystemId(); File itemFile = new File(itemUri); URI testUri = resolvedAssessmentTest.getTestLookup().getSystemId(); File testFile = new File(testUri); Path relativePath = testFile.toPath().getParent().relativize(itemFile.toPath().getParent()); String relativePathString = relativePath.toString(); if(relativePathString.isEmpty()) { return relativePathString; } else if(relativePathString.endsWith("/")) { return relativePathString; } return relativePathString.concat("/"); } } public AssessmentTest getAssessmentTest() { return getResolvedAssessmentTest().getRootNodeLookup().extractIfSuccessful(); } public boolean hasMultipleTestParts() { AssessmentTest assessmentTest = getAssessmentTest(); if(assessmentTest.getTestParts().size() > 1) { return true; } return false; } public TestPlanNode getCurrentTestPartNode() { TestSessionState sessionState = getTestSessionController().getTestSessionState(); TestPlanNodeKey testPlanNodeKey = sessionState.getCurrentTestPartKey(); return sessionState.getTestPlan().getNode(testPlanNodeKey); } public TestPart getTestPart(Identifier identifier) { List<TestPart> testParts = getAssessmentTest().getTestParts(); for(TestPart testPart:testParts) { if(testPart.getIdentifier().equals(identifier)) { return testPart; } } return null; } public AssessmentSection getAssessmentSection(Identifier identifier) { List<TestPart> testParts = getAssessmentTest().getTestParts(); for(TestPart testPart:testParts) { List<AssessmentSection> sections = testPart.getAssessmentSections(); for(AssessmentSection section:sections) { if(section.getIdentifier().equals(identifier)) { return section; } } } return null; } public ItemSessionState getItemSessionState(TestPlanNodeKey nodeKey) { TestSessionState sessionState = getTestSessionController().getTestSessionState(); return sessionState.getItemSessionStates().get(nodeKey); } //<xsl:if test="$itemFeedbackAllowed and $sessionStatus='final'"> //<xsl:variable name="itemFeedbackAllowed" as="xs:boolean" // select="if ($reviewMode) // then (/qti:assessentItem/@adaptive='true' or $showFeedback) // else (not($solutionMode))"/> public boolean isItemFeedbackAllowed(TestPlanNode itemNode, AssessmentItem assessmentItem, RenderingRequest options) { ItemSessionState itemSessionState = getItemSessionState(itemNode.getKey()); boolean itemFeedbackAllowed = false; if(itemSessionState.getSessionStatus() == SessionStatus.FINAL) { if(options.isReviewMode()) { if(assessmentItem.getAdaptive() || itemNode.getEffectiveItemSessionControl().isShowFeedback()) { itemFeedbackAllowed = true; } } else if(!options.isSolutionMode()) { itemFeedbackAllowed = true; } } return itemFeedbackAllowed; } @Override public AssessmentTestComponentRenderer getHTMLRendererSingleton() { return VELOCITY_RENDERER; } }