/** * <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.assessment; import java.io.File; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.olat.basesecurity.GroupRoles; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Controller; 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.id.Identity; import org.olat.course.archiver.ScoreAccountingHelper; import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.run.environment.CourseEnvironment; import org.olat.fileresource.FileResourceManager; import org.olat.group.BusinessGroupService; import org.olat.ims.qti21.AssessmentItemSession; import org.olat.ims.qti21.AssessmentTestHelper; import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.QTI21Service; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.AssessmentService; import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef; import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest; import uk.ac.ed.ph.jqtiplus.state.ItemSessionState; import uk.ac.ed.ph.jqtiplus.state.TestPlan; import uk.ac.ed.ph.jqtiplus.state.TestPlanNode; import uk.ac.ed.ph.jqtiplus.state.TestPlanNode.TestNodeType; 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: 16.08.2016<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class IdentitiesAssessmentTestCorrectionController extends BasicController { private final VelocityContainer mainVC; private final Link previousItemLink, currentItemLink, nextItemLink, overviewLink; private IdentitiesAssessmentTestOverviewController overviewCtrl; private IdentitiesAssessmentItemCorrectionController itemCorrectionCtrl; private final AssessmentToolOptions asOptions; private String subIdent; private RepositoryEntry testEntry; private RepositoryEntry courseEntry; private CourseEnvironment courseEnv; private AssessmentItemRef currentItemRef; private final List<AssessmentItemRef> itemRefs; private final ResolvedAssessmentTest resolvedAssessmentTest; private final AssessmentTestCorrection testCorrections; private final Map<Identity,AssessmentEntry> assessmentEntries; private final Map<Identity,AssessmentTestSession> lastSessions; @Autowired private QTI21Service qtiService; @Autowired private AssessmentService assessmentService; @Autowired private BusinessGroupService businessGroupService; public IdentitiesAssessmentTestCorrectionController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, AssessmentToolOptions asOptions, IQTESTCourseNode courseNode) { super(ureq, wControl); this.asOptions = asOptions; this.courseEnv = courseEnv; subIdent = courseNode.getIdent(); testEntry = courseNode.getReferencedRepositoryEntry(); courseEntry = courseEnv.getCourseGroupManager().getCourseEntry(); File fUnzippedDirRoot = FileResourceManager.getInstance() .unzipFileResource(testEntry.getOlatResource()); resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(fUnzippedDirRoot, false, false); lastSessions = getLastSessions(); assessmentEntries = getAssessmentEntries(lastSessions.keySet()); mainVC = createVelocityContainer("corrections"); previousItemLink = LinkFactory.createButton("previous.item", mainVC, this); previousItemLink.setIconLeftCSS("o_icon o_icon_previous"); currentItemLink = LinkFactory.createButton("current.item", mainVC, this); nextItemLink = LinkFactory.createButton("next.item", mainVC, this); nextItemLink.setIconRightCSS("o_icon o_icon_next"); overviewLink = LinkFactory.createButton("overview.tests", mainVC, this); itemRefs = calculateAssessmentItemToCorrect(); testCorrections = collectAssessedIdentityForItem(itemRefs); if(itemRefs.size() > 0) { currentItemRef = itemRefs.get(0); updatePreviousNext(); updateIdentitiesAssessmentItem(ureq); } putInitialPanel(mainVC); overviewCtrl = new IdentitiesAssessmentTestOverviewController(ureq, getWindowControl(), testCorrections); listenTo(overviewCtrl); } public int getNumberOfAssessedIdentities() { return lastSessions == null ? 0 : lastSessions.size(); } public AssessmentTestCorrection getTestCorrections() { return testCorrections; } private List<AssessmentItemRef> calculateAssessmentItemToCorrect() { return resolvedAssessmentTest.getAssessmentItemRefs(); } private void updatePreviousNext() { int index = itemRefs.indexOf(currentItemRef); if(index > 0) { AssessmentItemRef itemRef = itemRefs.get(index - 1); String itemTitle = AssessmentTestHelper.getAssessmentItemTitle(itemRef, resolvedAssessmentTest); previousItemLink.setCustomDisplayText(itemTitle); previousItemLink.setEnabled(true); } else { previousItemLink.setCustomDisplayText(translate("previous.item")); previousItemLink.setEnabled(false); } String currentItemTitle = AssessmentTestHelper.getAssessmentItemTitle(currentItemRef, resolvedAssessmentTest); currentItemLink.setCustomDisplayText(currentItemTitle); if(index + 1 < itemRefs.size()) { AssessmentItemRef itemRef = itemRefs.get(index + 1); String itemTitle = AssessmentTestHelper.getAssessmentItemTitle(itemRef, resolvedAssessmentTest); nextItemLink.setCustomDisplayText(itemTitle); nextItemLink.setEnabled(true); } else { nextItemLink.setCustomDisplayText(translate("next.item")); nextItemLink.setEnabled(false); } } private void updateIdentitiesAssessmentItem(UserRequest ureq) { removeAsListenerAndDispose(itemCorrectionCtrl); if(currentItemRef != null) { itemCorrectionCtrl = new IdentitiesAssessmentItemCorrectionController(ureq, getWindowControl(), testCorrections, currentItemRef, testEntry, resolvedAssessmentTest); int index = itemRefs.indexOf(currentItemRef); if(index + 1 == itemRefs.size()) { itemCorrectionCtrl.disableNext(); } listenTo(itemCorrectionCtrl); mainVC.put("itemCorrection", itemCorrectionCtrl.getInitialComponent()); } else if(itemCorrectionCtrl != null) { mainVC.remove(itemCorrectionCtrl.getInitialComponent()); itemCorrectionCtrl = null; mainVC.put("itemCorrection", overviewCtrl.getInitialComponent()); } } private AssessmentTestCorrection collectAssessedIdentityForItem(List<AssessmentItemRef> itemRefList) { AssessmentTestCorrection corrections = new AssessmentTestCorrection(assessmentEntries); for(AssessmentItemRef itemRef:itemRefList) { String itemRefIdentifier = itemRef.getIdentifier().toString(); List<AssessmentItemSession> itemSessions = qtiService.getAssessmentItemSessions(courseEntry, subIdent, testEntry, itemRefIdentifier); Map<AssessmentTestSession,AssessmentItemSession> testToItemSessions = new HashMap<>(); for(AssessmentItemSession itemSession:itemSessions) { testToItemSessions.put(itemSession.getAssessmentTestSession(), itemSession); } for(Map.Entry<Identity,AssessmentTestSession> entry:lastSessions.entrySet()) { AssessmentTestSession testSession = entry.getValue(); Identity assessedIdentity = testSession.getIdentity(); TestSessionState testSessionState = qtiService.loadTestSessionState(testSession); TestPlan testPlan = testSessionState.getTestPlan(); List<TestPlanNode> nodes = testPlan.getTestPlanNodeList(); for(TestPlanNode node:nodes) { TestNodeType testNodeType = node.getTestNodeType(); AssessmentItemSession itemSession = testToItemSessions.get(testSession); ItemSessionState itemSessionState = testSessionState.getItemSessionStates().get(node.getKey()); TestPlanNodeKey testPlanNodeKey = node.getKey(); if(testPlanNodeKey != null && testPlanNodeKey.getIdentifier() != null) { Identifier identifier = testPlanNodeKey.getIdentifier(); if(testNodeType == TestNodeType.ASSESSMENT_ITEM_REF && itemRef.getIdentifier().equals(identifier)) { AssessmentItemCorrection correction = new AssessmentItemCorrection(assessedIdentity, testSession, testSessionState, itemSession, itemSessionState, itemRef, node); corrections.add(correction); break; } } } } } return corrections; } private Map<Identity,AssessmentEntry> getAssessmentEntries(Set<Identity> identities) { List<AssessmentEntry> entries = assessmentService.loadAssessmentEntriesBySubIdent(courseEntry, subIdent); Map<Identity,AssessmentEntry> identityToAssessmentEntryMap = new HashMap<>(); for(AssessmentEntry assessmentEntry:entries) { if(identities.contains(assessmentEntry.getIdentity())) { identityToAssessmentEntryMap.put(assessmentEntry.getIdentity(), assessmentEntry); } } return identityToAssessmentEntryMap; } private Map<Identity,AssessmentTestSession> getLastSessions() { Set<Identity> identitiesSet; if(asOptions.getGroup() != null) { List<Identity> identities = businessGroupService.getMembers(asOptions.getGroup(), GroupRoles.participant.name()); identitiesSet = new HashSet<>(identities); } else if(asOptions.getIdentities() != null) { identitiesSet = new HashSet<>(asOptions.getIdentities()); } else { identitiesSet = new HashSet<>(ScoreAccountingHelper.loadUsers(courseEnv)); } List<AssessmentTestSession> sessions = qtiService.getAssessmentTestSessions(courseEntry, subIdent, testEntry); Map<Identity,AssessmentTestSession> identityToSessions = new HashMap<>(); for(AssessmentTestSession session:sessions) { //filter last session / user Identity assessedIdentity = session.getIdentity(); if(identitiesSet != null && !identitiesSet.contains(assessedIdentity)) { continue; } Date fDate = session.getFinishTime(); if(fDate == null) { //not terminated } else { if(identityToSessions.containsKey(assessedIdentity)) { AssessmentTestSession currentSession = identityToSessions.get(assessedIdentity); Date currentFDate = currentSession.getFinishTime(); if(fDate.after(currentFDate)) { identityToSessions.put(assessedIdentity, session); } } else { identityToSessions.put(assessedIdentity, session); } } } return identityToSessions; } @Override protected void doDispose() { // } @Override protected void event(UserRequest ureq, Controller source, Event event) { if(itemCorrectionCtrl == source) { if(event == Event.CANCELLED_EVENT) { fireEvent(ureq, event); } else if(event == Event.DONE_EVENT) { doNextAssessmentItem(ureq); } } else if(overviewCtrl == source) { fireEvent(ureq, event); } super.event(ureq, source, event); } @Override protected void event(UserRequest ureq, Component source, Event event) { if(previousItemLink == source) { doPreviousAssessmentItem(ureq); } else if(nextItemLink == source) { doNextAssessmentItem(ureq); } else if(currentItemLink == source) { updateIdentitiesAssessmentItem(ureq); } else if(overviewLink == source) { mainVC.put("itemCorrection", overviewCtrl.getInitialComponent()); } } private void doPreviousAssessmentItem(UserRequest ureq) { int previousIndex = itemRefs.indexOf(currentItemRef) - 1; if(previousIndex >= 0 && itemRefs.size() > previousIndex) { currentItemRef = itemRefs.get(previousIndex); updatePreviousNext(); updateIdentitiesAssessmentItem(ureq); } else if(itemRefs.size() > 0) { currentItemRef = itemRefs.get(0); } } private void doNextAssessmentItem(UserRequest ureq) { int nextIndex = itemRefs.indexOf(currentItemRef) + 1; if(nextIndex >= 0 && itemRefs.size() > nextIndex) { currentItemRef = itemRefs.get(nextIndex); updatePreviousNext(); updateIdentitiesAssessmentItem(ureq); } else if(itemRefs.size() > 0) { currentItemRef = itemRefs.get(itemRefs.size() - 1); } } }