/**
* <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.nodes.gta.ui;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.olat.basesecurity.BaseSecurityModule;
import org.olat.basesecurity.GroupRoles;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
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.FormLayoutContainer;
import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiColumnModel;
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.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.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.generic.closablewrapper.CloseableCalloutWindowController;
import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
import org.olat.core.id.Identity;
import org.olat.core.id.Roles;
import org.olat.core.id.UserConstants;
import org.olat.core.util.CodeHelper;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.course.assessment.AssessmentHelper;
import org.olat.course.assessment.AssessmentManager;
import org.olat.course.assessment.bulk.PassedCellRenderer;
import org.olat.course.highscore.ui.HighScoreRunController;
import org.olat.course.nodes.GTACourseNode;
import org.olat.course.nodes.MSCourseNode;
import org.olat.course.nodes.gta.GTAManager;
import org.olat.course.nodes.gta.Task;
import org.olat.course.nodes.gta.TaskList;
import org.olat.course.nodes.gta.TaskProcess;
import org.olat.course.nodes.gta.ui.GroupAssessmentModel.Cols;
import org.olat.course.nodes.ms.MSCourseNodeRunController;
import org.olat.course.run.environment.CourseEnvironment;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.group.BusinessGroup;
import org.olat.group.BusinessGroupService;
import org.olat.modules.ModuleConfiguration;
import org.olat.modules.assessment.AssessmentEntry;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Initial date: 18.03.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class GTACoachedGroupGradingController extends FormBasicController {
protected static final String USER_PROPS_ID = GTACoachedGroupGradingController.class.getCanonicalName();
protected static final int USER_PROPS_OFFSET = 500;
private FlexiTableElement table;
private GroupAssessmentModel model;
private FormLink assessmentFormButton, reopenButton;
private CloseableModalController cmc;
private GroupAssessmentController assessmentCtrl;
private CloseableCalloutWindowController commentCalloutCtrl;
private TaskList taskList;
private Task assignedTask;
private final GTACourseNode gtaNode;
private final BusinessGroup assessedGroup;
private final CourseEnvironment courseEnv;
private final AssessmentManager assessmentManager;
private final UserCourseEnvironment coachCourseEnv;
private final boolean withScore, withPassed, withComment;
private final boolean isAdministrativeUser;
private final List<UserPropertyHandler> userPropertyHandlers;
@Autowired
private GTAManager gtaManager;
@Autowired
private UserManager userManager;
@Autowired
private BaseSecurityModule securityModule;
@Autowired
private BusinessGroupService businessGroupService;
public GTACoachedGroupGradingController(UserRequest ureq, WindowControl wControl,
UserCourseEnvironment coachCourseEnv, CourseEnvironment courseEnv, GTACourseNode gtaNode,
BusinessGroup assessedGroup, TaskList taskList, Task assignedTask) {
super(ureq, wControl, "coach_group_grading");
setTranslator(Util.createPackageTranslator(MSCourseNodeRunController.class, getLocale(), getTranslator()));
this.gtaNode = gtaNode;
this.taskList = taskList;
this.assessedGroup = assessedGroup;
this.courseEnv = courseEnv;
this.assignedTask = assignedTask;
this.coachCourseEnv = coachCourseEnv;
assessmentManager = courseEnv.getAssessmentManager();
withScore = gtaNode.hasScoreConfigured();
withPassed = gtaNode.hasPassedConfigured();
withComment = gtaNode.hasCommentConfigured();
Roles roles = ureq.getUserSession().getRoles();
isAdministrativeUser = securityModule.isUserAllowedAdminProps(roles);
userPropertyHandlers = userManager.getUserPropertyHandlersFor(GTACoachedGroupGradingController.USER_PROPS_ID, isAdministrativeUser);
setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
initForm(ureq);
loadMembers();
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
assessmentFormButton = uifactory.addFormLink("coach.assessment", "coach.assessment", null, formLayout, Link.BUTTON);
assessmentFormButton.setCustomEnabledLinkCSS("btn btn-primary");
assessmentFormButton.setIconLeftCSS("o_icon o_icon o_icon_submit");
assessmentFormButton.setElementCssClass("o_sel_course_gta_assessment_button");
assessmentFormButton.setVisible(!coachCourseEnv.isCourseReadOnly() && (assignedTask == null || assignedTask.getTaskStatus() != TaskProcess.graded));
reopenButton = uifactory.addFormLink("coach.reopen", "coach.reopen", null, formLayout, Link.BUTTON);
reopenButton.setElementCssClass("o_sel_course_gta_reopen_button");
reopenButton.setVisible(!coachCourseEnv.isCourseReadOnly() && assignedTask != null && assignedTask.getTaskStatus() == TaskProcess.graded);
if(formLayout instanceof FormLayoutContainer) {
ModuleConfiguration config = gtaNode.getModuleConfiguration();
FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
layoutCont.contextPut(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD, config.get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD));
layoutCont.contextPut(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD, config.get(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD));
layoutCont.contextPut(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE, AssessmentHelper.getRoundedScore(config.getFloatEntry(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE)));
layoutCont.contextPut(MSCourseNode.CONFIG_KEY_SCORE_MIN, AssessmentHelper.getRoundedScore(config.getFloatEntry(MSCourseNode.CONFIG_KEY_SCORE_MIN)));
layoutCont.contextPut(MSCourseNode.CONFIG_KEY_SCORE_MAX, AssessmentHelper.getRoundedScore(config.getFloatEntry(MSCourseNode.CONFIG_KEY_SCORE_MAX)));
if (config.getBooleanSafe(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD,false)){
HighScoreRunController highScoreCtr = new HighScoreRunController(ureq, getWindowControl(), coachCourseEnv, gtaNode);
if (highScoreCtr.isViewHighscore()) {
Component highScoreComponent = highScoreCtr.getInitialComponent();
layoutCont.put("highScore", highScoreComponent);
}
}
}
FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
if(isAdministrativeUser) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.username.i18nKey(), Cols.username.ordinal()));
}
int i=0;
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
int colIndex = GTACoachedGroupGradingController.USER_PROPS_OFFSET + i++;
if (userPropertyHandler == null) continue;
String propName = userPropertyHandler.getName();
boolean visible = userManager.isMandatoryUserProperty(GTACoachedGroupGradingController.USER_PROPS_ID , userPropertyHandler);
if(visible) {
FlexiColumnModel col;
if(UserConstants.FIRSTNAME.equals(propName)
|| UserConstants.LASTNAME.equals(propName)) {
col = new DefaultFlexiColumnModel(userPropertyHandler.i18nColumnDescriptorLabelKey(),
colIndex, userPropertyHandler.getName(), true, propName,
new StaticFlexiCellRenderer(userPropertyHandler.getName(), new TextFlexiCellRenderer()));
} else {
col = new DefaultFlexiColumnModel(true, userPropertyHandler.i18nColumnDescriptorLabelKey(), colIndex, true, propName);
}
columnsModel.addFlexiColumnModel(col);
}
}
if(withPassed) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.passedVal.i18nKey(), Cols.passedVal.ordinal(),
new PassedCellRenderer()));
}
if(withScore) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.scoreVal.i18nKey(), Cols.scoreVal.ordinal()));
}
if(withComment) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.commentVal.i18nKey(), Cols.commentVal.ordinal()));
}
model = new GroupAssessmentModel(gtaNode, userPropertyHandlers, getLocale(), columnsModel);
table = uifactory.addTableElement(getWindowControl(), "group-list", model, getTranslator(), formLayout);
table.setCustomizeColumns(true);
table.setAndLoadPersistedPreferences(ureq, "gtagroup-assessment");
}
private void loadMembers() {
//load participants, load datas
List<Identity> identities = businessGroupService.getMembers(assessedGroup, GroupRoles.participant.name());
Map<Identity, AssessmentEntry> identityToEntryMap = new HashMap<>();
List<AssessmentEntry> assessmentEntries = assessmentManager.getAssessmentEntries(assessedGroup, gtaNode);
for(AssessmentEntry entry : assessmentEntries) {
identityToEntryMap.put(entry.getIdentity(), entry);
}
List<AssessmentRow> rows = new ArrayList<>(identities.size());
for(Identity identity:identities) {
AssessmentEntry entry = identityToEntryMap.get(identity);
AssessmentRow row = new AssessmentRow(identity, false);
rows.add(row);
if(withScore && entry != null) {
String pointVal = AssessmentHelper.getRoundedScore(entry.getScore());
row.setScore(pointVal);
}
if(withPassed && entry != null) {
row.setPassed(entry.getPassed());
}
if(withComment) {
FormLink commentLink = null;
String comment = null;
if(entry != null && StringHelper.containsNonWhitespace(entry.getComment())) {
comment = entry.getComment();
commentLink = uifactory.addFormLink("comment-" + CodeHelper.getRAMUniqueID(), "comment", "comment", null, flc, Link.LINK);
commentLink.setIconLeftCSS("o_icon o_icon_comments");
commentLink.setUserObject(row);
}
row.setComment(comment);
row.setCommentTooltipLink(commentLink);
}
}
model.setObjects(rows);
table.reset();
}
@Override
protected void doDispose() {
//
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if(assessmentCtrl == source) {
if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
loadMembers();
} else if(event == Event.CLOSE_EVENT) {
doGrading();
loadMembers();
fireEvent(ureq, Event.CHANGED_EVENT);
}
cmc.deactivate();
cleanUp();
} else if(cmc == source) {
cleanUp();
}
super.event(ureq, source, event);
}
private void cleanUp() {
removeAsListenerAndDispose(assessmentCtrl);
removeAsListenerAndDispose(cmc);
assessmentCtrl = null;
cmc = null;
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(assessmentFormButton == source) {
doOpenAssessmentForm(ureq);
} else if(reopenButton == source) {
doReopenAssessment(ureq);
} else if(source instanceof FormLink) {
FormLink link = (FormLink)source;
if("comment".equals(link.getCmd())) {
AssessmentRow row = (AssessmentRow)link.getUserObject();
doComment(ureq, row);
}
}
super.formInnerEvent(ureq, source, event);
}
@Override
protected void formOK(UserRequest ureq) {
//
}
private void doGrading() {
if(assignedTask == null) {
assignedTask = gtaManager.createTask(null, taskList, TaskProcess.graded, assessedGroup, null, gtaNode);
} else {
assignedTask = gtaManager.updateTask(assignedTask, TaskProcess.graded, gtaNode);
}
}
private void doReopenAssessment(UserRequest ureq) {
assignedTask = gtaManager.updateTask(assignedTask, TaskProcess.grading, gtaNode);
fireEvent(ureq, Event.CHANGED_EVENT);
}
private void doOpenAssessmentForm(UserRequest ureq) {
removeAsListenerAndDispose(assessmentCtrl);
assessmentCtrl = new GroupAssessmentController(ureq, getWindowControl(), courseEnv, gtaNode, assessedGroup);
listenTo(assessmentCtrl);
String title = translate("grading");
cmc = new CloseableModalController(getWindowControl(), "close", assessmentCtrl.getInitialComponent(), true, title, true);
listenTo(cmc);
cmc.activate();
}
private void doComment(UserRequest ureq, AssessmentRow row) {
removeAsListenerAndDispose(commentCalloutCtrl);
VelocityContainer descriptionVC = createVelocityContainer("comment_readonly_callout");
descriptionVC.contextPut("comment", row.getComment());
commentCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(),
descriptionVC, row.getCommentTooltipLink().getFormDispatchId(), "", true, "");
listenTo(commentCalloutCtrl);
commentCalloutCtrl.activate();
}
}