/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <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> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. */ package org.olat.course.assessment.ui.tool; import java.util.ArrayList; import java.util.List; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; import org.olat.core.gui.components.form.flexible.elements.FlexiTableFilter; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormEvent; import org.olat.core.gui.components.form.flexible.impl.elements.table.BooleanCellRenderer; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer; 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.generic.dtabs.Activateable2; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; import org.olat.core.logging.AssertException; import org.olat.core.util.Util; import org.olat.course.Structure; import org.olat.course.assessment.AssessmentHelper; import org.olat.course.assessment.AssessmentModule; import org.olat.course.assessment.IndentedNodeRenderer; import org.olat.course.assessment.bulk.PassedCellRenderer; import org.olat.course.assessment.model.AssessmentNodeData; import org.olat.course.assessment.ui.tool.IdentityAssessmentOverviewTableModel.NodeCols; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.CourseNode; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.modules.assessment.ui.AssessedIdentityListState; import org.olat.modules.assessment.ui.ScoreCellRenderer; /** * Description:<BR> * This controller provides an overview to the users course assessment. Two constructors are * available, one for the students read-only view and one for the coach/course-admins assessment * tool. In the second case a node can be selected which results in a EVENT_NODE_SELECTED event. * <BR> * Use the IdentityAssessmentEditController to edit the users assessment data instead of this one. * <P> * Initial Date: Oct 28, 2004 * * @author gnaegi */ public class IdentityAssessmentOverviewController extends FormBasicController implements Activateable2 { private static final String CMD_SELECT_NODE = "cmd.select.node"; /** Event fired when a node has been selected, meaning when a row in the table has been selected **/ public static final Event EVENT_NODE_SELECTED = new Event("event.node.selected"); private Structure runStructure; private boolean nodesSelectable; private boolean discardEmptyNodes; private boolean allowTableFiltering; private FlexiTableElement tableEl; private IdentityAssessmentOverviewTableModel tableModel; private boolean loadNodesFromCourse; private final boolean followUserResultsVisibility; private AssessableCourseNode selectedCourseNode; private List<AssessmentNodeData> preloadedNodesList; private UserCourseEnvironment userCourseEnvironment; /** * Constructor for the identity assessment overview controller to be used in the assessment tool or in the users * course overview page * @param ureq The user request * @param wControl * @param userCourseEnvironment The assessed identities user course environment * @param nodesSelectable configuration switch: true: user may select the nodes, e.g. to edit the nodes result, false: readonly view (user view) * @param discardEmptyNodes filtering default value: true: do not show nodes that have no value. false: show all assessable nodes * @param allowTableFiltering configuration switch: true: allow user to filter table all nodes/only nodes with data */ public IdentityAssessmentOverviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnvironment, boolean nodesSelectable, boolean discardEmptyNodes, boolean allowTableFiltering) { super(ureq, wControl, LAYOUT_BAREBONE); setTranslator(Util.createPackageTranslator(AssessmentModule.class, getLocale(), getTranslator())); this.runStructure = userCourseEnvironment.getCourseEnvironment().getRunStructure(); this.nodesSelectable = nodesSelectable; this.discardEmptyNodes = discardEmptyNodes; this.allowTableFiltering = allowTableFiltering; this.userCourseEnvironment = userCourseEnvironment; loadNodesFromCourse = true; followUserResultsVisibility = false; initForm(ureq); loadModel(); } /** * Internal constructor used by the efficiency statement: uses a precompiled list of node data information * instead of fetching everything from the database for each node * @param ureq * @param wControl * @param assessmentCourseNodes List of maps containing the node assessment data using the AssessmentManager keys */ public IdentityAssessmentOverviewController(UserRequest ureq, WindowControl wControl, List<AssessmentNodeData> assessmentCourseNodes) { super(ureq, wControl, LAYOUT_BAREBONE); setTranslator(Util.createPackageTranslator(AssessmentModule.class, getLocale(), getTranslator())); runStructure = null; nodesSelectable = false; discardEmptyNodes = true; allowTableFiltering = false; userCourseEnvironment = null; loadNodesFromCourse = false; followUserResultsVisibility = true; preloadedNodesList = assessmentCourseNodes; initForm(ureq); loadModel(); } public boolean isRoot(CourseNode node) { return node != null && node.getIdent().equals(runStructure.getRootNode().getIdent()); } public int getNumberOfNodes() { return tableModel.getRowCount(); } public CourseNode getNode(int row) { AssessmentNodeData data = tableModel.getObject(row); return getNodeByIdent(data.getIdent()); } public CourseNode getNodeByIdent(String ident) { return runStructure.getNode(ident); } public CourseNode getNextNode(CourseNode node) { int index = getIndexOf(node); String nodeIdent = null; if(index >= 0) { int nextIndex = index + 1;//next if(nextIndex >= 0 && nextIndex < tableModel.getRowCount()) { nodeIdent = tableModel.getObject(nextIndex).getIdent(); } else if(tableModel.getRowCount() > 0) { nodeIdent = tableModel.getObject(0).getIdent(); } } if(nodeIdent != null) { return runStructure.getNode(nodeIdent); } return null; } public CourseNode getPreviousNode(CourseNode node) { int index = getIndexOf(node); String nodeIdent = null; if(index >= 0) { int previousIndex = index - 1;//next if(previousIndex >= 0 && previousIndex < tableModel.getRowCount()) { nodeIdent = tableModel.getObject(previousIndex).getIdent(); } else if(tableModel.getRowCount() > 0) { nodeIdent = tableModel.getObject(tableModel.getRowCount() - 1).getIdent(); } } if(nodeIdent != null) { return runStructure.getNode(nodeIdent); } return null; } public int getIndexOf(CourseNode node) { for(int i=tableModel.getRowCount(); i-->0; ) { Object rowIdentityKey = tableModel.getObject(i).getIdent(); if(rowIdentityKey.equals(node.getIdent())) { return i; } } return -1; } /** * Returns the selected assessable course node. Call this method after getting the EVENT_NODE_SELECTED * to get the selected node * @return AssessableCourseNode */ public AssessableCourseNode getSelectedCourseNode() { if (selectedCourseNode == null) { throw new AssertException("Selected course node was null. Maybe getSelectedCourseNode called prior to EVENT_NODE_SELECTED has been fired?"); } return selectedCourseNode; } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(NodeCols.node, new IndentedNodeRenderer() { @Override public boolean isIndentationEnabled() { return tableEl.getOrderBy() == null || tableEl.getOrderBy().length == 0; } })); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(NodeCols.attempts)); if(!followUserResultsVisibility) { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(NodeCols.userVisibility, new UserVisibilityCellRenderer(getTranslator()))); } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(NodeCols.score, new ScoreCellRenderer())); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(NodeCols.min, new ScoreCellRenderer())); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(NodeCols.max, new ScoreCellRenderer())); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(NodeCols.passed, new PassedCellRenderer())); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(NodeCols.status, new AssessmentStatusCellRenderer(getLocale()))); if(nodesSelectable) { DefaultFlexiColumnModel selectCol = new DefaultFlexiColumnModel("select", NodeCols.select.ordinal(), CMD_SELECT_NODE, new BooleanCellRenderer(new StaticFlexiCellRenderer(translate("select"), CMD_SELECT_NODE), null)); selectCol.setExportable(false); columnsModel.addFlexiColumnModel(selectCol); } tableModel = new IdentityAssessmentOverviewTableModel(columnsModel); tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 250, false, getTranslator(), formLayout); tableEl.setExportEnabled(true); tableEl.setEmtpyTableMessageKey("nodesoverview.emptylist"); tableEl.setBordered(true); tableEl.setNumOfRowsEnabled(false); if (allowTableFiltering) { List<FlexiTableFilter> filters = new ArrayList<>(); filters.add(new FlexiTableFilter(translate("filter.showAll"), "showAll", true)); filters.add(FlexiTableFilter.SPACER); filters.add(new FlexiTableFilter(translate("filter.passed"), "passed")); filters.add(new FlexiTableFilter(translate("filter.failed"), "failed")); filters.add(new FlexiTableFilter(translate("filter.inProgress"), "inProgress")); filters.add(new FlexiTableFilter(translate("filter.inReview"), "inReview")); filters.add(new FlexiTableFilter(translate("filter.done"), "done")); tableEl.setFilters("", filters, false); } } @Override protected void doDispose() { // } protected void loadModel() { List<AssessmentNodeData> nodesTableList; if (loadNodesFromCourse) { // get list of course node and user data and populate table data model nodesTableList = AssessmentHelper.getAssessmentNodeDataList(userCourseEnvironment, followUserResultsVisibility, discardEmptyNodes, true); } else { // use list from efficiency statement nodesTableList = preloadedNodesList; } tableModel.setObjects(nodesTableList); tableEl.reset(true, true, true); } @Override public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { if(state instanceof AssessedIdentityListState) { AssessedIdentityListState listState = (AssessedIdentityListState)state; tableEl.setSelectedFilterKey(listState.getFilter()); loadModel(); } } @Override protected void formOK(UserRequest ureq) { // } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(tableEl == source) { if(event instanceof SelectionEvent) { SelectionEvent se = (SelectionEvent)event; String cmd = se.getCommand(); AssessmentNodeData nodeData = tableModel.getObject(se.getIndex()); if(CMD_SELECT_NODE.equals(cmd)) { CourseNode node = runStructure.getNode(nodeData.getIdent()); selectedCourseNode = (AssessableCourseNode)node; fireEvent(ureq, EVENT_NODE_SELECTED); } } } super.formInnerEvent(ureq, source, event); } }