/**
* <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.course.assessment.bulk;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.olat.NewControllerFactory;
import org.olat.core.commons.services.taskexecutor.Task;
import org.olat.core.commons.services.taskexecutor.TaskExecutorManager;
import org.olat.core.commons.services.taskexecutor.ui.TaskStatusRenderer;
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.FormLink;
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.DefaultFlexiColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer;
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.components.form.flexible.impl.elements.table.TextFlexiCellRenderer;
import org.olat.core.gui.components.link.Link;
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.modal.DialogBoxController;
import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
import org.olat.core.gui.control.generic.wizard.Step;
import org.olat.core.gui.control.generic.wizard.StepRunnerCallback;
import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
import org.olat.core.gui.control.generic.wizard.StepsRunContext;
import org.olat.core.id.Identity;
import org.olat.course.CourseFactory;
import org.olat.course.ICourse;
import org.olat.course.Structure;
import org.olat.course.assessment.bulk.TaskDataModel.Cols;
import org.olat.course.assessment.manager.BulkAssessmentTask;
import org.olat.course.assessment.model.BulkAssessmentDatas;
import org.olat.course.assessment.model.BulkAssessmentFeedback;
import org.olat.course.nodes.AssessableCourseNode;
import org.olat.course.nodes.CourseNode;
import org.olat.repository.RepositoryEntry;
import org.olat.user.UserManager;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Initial date: 18.11.2013<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class BulkAssessmentOverviewController extends FormBasicController {
private FormLink newButton;
private TaskDataModel taskModel;
private FlexiTableElement taskListEl;
private DialogBoxController confirmDeleteCtrl;
private StepsMainRunController bulkAssessmentCtrl;
private DialogBoxController errorCtrl;
private final RepositoryEntry courseEntry;
@Autowired
private UserManager userManager;
@Autowired
private TaskExecutorManager taskManager;
private Task editedTask;
public BulkAssessmentOverviewController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry) {
super(ureq, wControl, "overview");
this.courseEntry = courseEntry;
initForm(ureq);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
newButton = uifactory.addFormLink("new.bulk", formLayout, Link.BUTTON);
newButton.setElementCssClass("o_sel_assessment_tool_new_bulk_assessment");
FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.taskStatus", Cols.taskStatus.ordinal(),
new TaskStatusRenderer()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.scheduledDate", Cols.scheduledDate.ordinal()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.node", Cols.courseNode.ordinal(),
new CourseNodeRenderer()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.score", Cols.score.ordinal()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.passed", Cols.status.ordinal()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.comment", Cols.comment.ordinal()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.returnFiles", Cols.returnFile.ordinal()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.numOfAssessedUsers", Cols.numOfAssessedUsers.ordinal()));
FlexiCellRenderer renderer = new StaticFlexiCellRenderer("select-owner", new TextFlexiCellRenderer());
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.owner", Cols.owner.ordinal(), "select-owner", renderer));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("edit", translate("edit"), "edit"));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("delete", translate("delete"), "delete"));
taskModel = new TaskDataModel(Collections.<TaskData>emptyList(), columnsModel);
taskListEl = uifactory.addTableElement(getWindowControl(), "taskList", taskModel, getTranslator(), formLayout);
reloadTaskModel();
}
private void reloadTaskModel() {
List<Task> tasks = taskManager.getTasks(courseEntry.getOlatResource());
List<TaskData> taskDatas = new ArrayList<TaskData>(tasks.size());
ICourse course = CourseFactory.loadCourse(courseEntry);
Structure structure = course.getRunStructure();
for(Task task:tasks) {
String fullName = null;
if(task.getCreator() != null) {
fullName = userManager.getUserDisplayName(task.getCreator());
}
BulkAssessmentTask runnable = taskManager.getPersistedRunnableTask(task, BulkAssessmentTask.class);
AssessableCourseNode courseNode = (AssessableCourseNode)structure.getNode(runnable.getCourseNodeIdent());
taskDatas.add(new TaskData(task, runnable, courseNode, fullName));
}
taskModel.setObjects(taskDatas);
taskListEl.reset();
flc.contextPut("hasScheduledTasks", Boolean.valueOf(taskDatas.size()>0));
}
@Override
protected void doDispose() {
if(editedTask != null) {//only for security purpose
taskManager.returnTaskAfterEdition(editedTask, null);
}
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(newButton == source) {
doNewBulkAssessment(ureq);
} else if(taskListEl == source) {
if(event instanceof SelectionEvent) {
SelectionEvent se = (SelectionEvent)event;
switch(se.getCommand()) {
case "edit": {
TaskData data = taskModel.getObject(se.getIndex());
doEditBulkAssessment(ureq, data);
break;
}
case "delete": {
TaskData data = taskModel.getObject(se.getIndex());
doConfirmDelete(ureq, data);
break;
}
case "select-owner": {
TaskData data = taskModel.getObject(se.getIndex());
Identity creator = data.getTask().getCreator();
doOpenCard(ureq, creator);
break;
}
}
}
}
}
@Override
protected void formOK(UserRequest ureq) {
//
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if(bulkAssessmentCtrl == source) {
if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
getWindowControl().pop();
Feedback feedback = (Feedback)bulkAssessmentCtrl.getRunContext().get("feedback");
removeAsListenerAndDispose(bulkAssessmentCtrl);
bulkAssessmentCtrl = null;
if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
reloadTaskModel();
doBulkAssessmentSynchronous(ureq, feedback);
}
}
} else if (confirmDeleteCtrl == source) {
if(DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) {
TaskData data = (TaskData)confirmDeleteCtrl.getUserObject();
doDelete(data);
reloadTaskModel();
}
}
super.event(ureq, source, event);
}
private void doOpenCard(UserRequest ureq, Identity creator) {
String businessPath = "[Identity:" + creator.getKey() + "]";
NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl());
}
private void doConfirmDelete(UserRequest ureq, TaskData data) {
String title = translate("delete.task");
String text = translate("delete.task.confirm", new String[]{ data.toString() });
confirmDeleteCtrl = activateOkCancelDialog(ureq, title, text, confirmDeleteCtrl);
confirmDeleteCtrl.setUserObject(data);
}
private void doDelete(TaskData data) {
taskManager.delete(data.getTask());
showInfo("task.deleted");
}
private void doBulkAssessmentSynchronous(UserRequest ureq, Feedback feedback) {
if(feedback.isSynchronous()) {
List<BulkAssessmentFeedback> feedbacks = feedback.getFeedbacks();
if(feedbacks.isEmpty()) {
showInfo("bulk.assessment.done");
} else {
String text = BulkAssessmentTask.renderFeedback(feedbacks, getTranslator());
List<String> buttonLabels = Collections.singletonList(translate("ok"));
String title = translate("bulk.assessment.error.title");
String translatedText = translate("bulk.assessment.error.feedback", new String[]{ text });
errorCtrl = activateGenericDialog(ureq, title, translatedText, buttonLabels, errorCtrl);
}
} else {
showInfo("bulk.assessment.enlisted");
}
}
private void doNewBulkAssessment(UserRequest ureq) {
removeAsListenerAndDispose(bulkAssessmentCtrl);
List<AssessableCourseNode> nodes = new ArrayList<>();
ICourse course = CourseFactory.loadCourse(courseEntry);
collectBulkAssessableCourseNode(course.getRunStructure().getRootNode(), nodes);
Step start;
if(nodes.size() > 1) {
start = new BulkAssessment_1_SelectCourseNodeStep(ureq, courseEntry);
} else if(nodes.size() == 1){
start = new BulkAssessment_2_DatasStep(ureq, nodes.get(0));
} else {
showWarning("bulk.action.no.coursenodes");
return;
}
StepRunnerCallback finish = new StepRunnerCallback() {
@Override
public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
Date scheduledDate = (Date)runContext.get("scheduledDate");
AssessableCourseNode courseNode = (AssessableCourseNode)runContext.get("courseNode");
BulkAssessmentDatas datas = (BulkAssessmentDatas)runContext.get("datas");
Feedback feedback = doBulkAssessment(courseNode, scheduledDate, datas);
runContext.put("feedback", feedback);
return StepsMainRunController.DONE_MODIFIED;
}
};
bulkAssessmentCtrl = new StepsMainRunController(ureq, getWindowControl(), start, finish, null,
translate("bulk.wizard.title"), "o_sel_bulk_assessment_wizard");
listenTo(bulkAssessmentCtrl);
getWindowControl().pushAsModalDialog(bulkAssessmentCtrl.getInitialComponent());
}
private void doEditBulkAssessment(UserRequest ureq, TaskData data) {
removeAsListenerAndDispose(bulkAssessmentCtrl);
if(editedTask != null) {//only for security purpose
taskManager.returnTaskAfterEdition(editedTask, null);
}
AssessableCourseNode courseNode = data.getCourseNode();
final Task editableTask = taskManager.pickTaskForEdition(data.getTask());
editedTask = editableTask;
if(editableTask == null) {
showWarning("task.edited");
} else {
BulkAssessmentTask runnable = taskManager.getPersistedRunnableTask(editableTask, BulkAssessmentTask.class);
BulkAssessmentDatas datas = runnable.getDatas();
Step start = new BulkAssessment_2_DatasStep(ureq, courseNode, datas, editableTask);
StepRunnerCallback finish = new StepRunnerCallback() {
@Override
public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
Task task = (Task)runContext.get("task");
Date scheduledDate = (Date)runContext.get("scheduledDate");
AssessableCourseNode assessableCourseNode = (AssessableCourseNode)runContext.get("courseNode");
BulkAssessmentDatas bulkDatas = (BulkAssessmentDatas)runContext.get("datas");
Feedback feedback = doUpdateBulkAssessment(task, assessableCourseNode, scheduledDate, bulkDatas);
runContext.put("feedback", feedback);
editedTask = null;
return StepsMainRunController.DONE_MODIFIED;
}
};
StepRunnerCallback cancel = new StepRunnerCallback() {
@Override
public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
taskManager.returnTaskAfterEdition(editableTask, null);
editedTask = null;
return Step.NOSTEP;
}
};
bulkAssessmentCtrl = new StepsMainRunController(ureq, getWindowControl(), start, finish, cancel,
translate("bulk.wizard.title"), "o_sel_bulk_assessment_wizard");
listenTo(bulkAssessmentCtrl);
getWindowControl().pushAsModalDialog(bulkAssessmentCtrl.getInitialComponent());
}
}
private void collectBulkAssessableCourseNode(CourseNode courseNode, List<AssessableCourseNode> nodes) {
for (int i=courseNode.getChildCount(); i-->0; ) {
collectBulkAssessableCourseNode((CourseNode)courseNode.getChildAt(i), nodes);
}
if(BulkAssessmentTask.isBulkAssessable(courseNode)) {
nodes.add((AssessableCourseNode)courseNode);
}
}
private Feedback doUpdateBulkAssessment(Task task, AssessableCourseNode node, Date scheduledDate, BulkAssessmentDatas datas) {
BulkAssessmentTask runnable = new BulkAssessmentTask(courseEntry.getOlatResource(), node, datas, getIdentity().getKey());
Feedback feedback;
if(scheduledDate == null) {
List<BulkAssessmentFeedback> feedbacks = runnable.process();
feedback = new Feedback(true, feedbacks);
if(task != null) {
taskManager.delete(task);
}
} else {
taskManager.updateAndReturn(task, runnable, getIdentity(), scheduledDate);
feedback = new Feedback(false, null);
}
return feedback;
}
private Feedback doBulkAssessment(AssessableCourseNode node, Date scheduledDate, BulkAssessmentDatas datas) {
BulkAssessmentTask task = new BulkAssessmentTask(courseEntry.getOlatResource(), node, datas, getIdentity().getKey());
Feedback feedback;
if(scheduledDate == null) {
List<BulkAssessmentFeedback> feedbacks = task.process();
feedback = new Feedback(true, feedbacks);
} else {
taskManager.execute(task, getIdentity(), courseEntry.getOlatResource(), node.getIdent(), scheduledDate);
feedback = new Feedback(false, null);
}
return feedback;
}
private static class Feedback {
private final boolean synchronous;
private final List<BulkAssessmentFeedback> feedbacks;
public Feedback(boolean synchronous, List<BulkAssessmentFeedback> feedbacks) {
this.synchronous = synchronous;
this.feedbacks = feedbacks;
}
public boolean isSynchronous() {
return synchronous;
}
public List<BulkAssessmentFeedback> getFeedbacks() {
return feedbacks;
}
}
}