/** * <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; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.olat.basesecurity.IdentityRef; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.services.notifications.NotificationsManager; import org.olat.core.commons.services.notifications.SubscriptionContext; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.messages.MessageUIFactory; import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.Roles; import org.olat.core.logging.OLATRuntimeException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.ExportUtil; import org.olat.core.util.FileUtils; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.ZipUtil; import org.olat.core.util.io.ShieldOutputStream; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.filters.SystemItemFilter; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.archiver.ScoreAccountingHelper; import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.bulk.BulkAssessmentToolController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.PublishEvents; import org.olat.course.editor.StatusDescription; import org.olat.course.export.CourseEnvironmentMapper; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.gta.GTAManager; import org.olat.course.nodes.gta.GTAType; import org.olat.course.nodes.gta.Task; import org.olat.course.nodes.gta.TaskHelper; import org.olat.course.nodes.gta.TaskList; import org.olat.course.nodes.gta.model.TaskDefinition; import org.olat.course.nodes.gta.ui.BulkDownloadToolController; import org.olat.course.nodes.gta.ui.GTAAssessmentDetailsController; import org.olat.course.nodes.gta.ui.GTACoachedGroupListController; import org.olat.course.nodes.gta.ui.GTAEditController; import org.olat.course.nodes.gta.ui.GTAGroupAssessmentToolController; import org.olat.course.nodes.gta.ui.GTARunController; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.repository.RepositoryEntry; import org.olat.user.UserManager; /** * * Initial date: 23.02.2015<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class GTACourseNode extends AbstractAccessableCourseNode implements PersistentAssessableCourseNode { private final static OLog log = Tracing.createLoggerFor(GTACourseNode.class); private final static String PACKAGE_GTA = Util.getPackageName(GTAEditController.class); private static final long serialVersionUID = 1L; /** * Setting for group or individual task */ public static final String GTASK_TYPE = "grouptask.type"; public static final String GTASK_GROUPS = "grouptask.groups"; public static final String GTASK_AREAS = "grouptask.areas"; public static final String GTASK_ASSIGNMENT = "grouptask.assignement"; public static final String GTASK_ASSIGNMENT_DEADLINE = "grouptask.assignment.deadline"; public static final String GTASK_ASSIGNMENT_DEADLINE_RELATIVE = "grouptask.assignment.deadline.relative"; public static final String GTASK_ASSIGNMENT_DEADLINE_RELATIVE_TO = "grouptask.assignment.deadline.relative.to"; public static final String GTASK_COACH_ALLOWED_UPLOAD_TASKS = "grouptask.coach.allowed.upload.tasks"; public static final String GTASK_SUBMIT = "grouptask.submit"; public static final String GTASK_SUBMIT_DEADLINE = "grouptask.submit.deadline"; public static final String GTASK_SUBMIT_DEADLINE_RELATIVE = "grouptask.submit.deadline.relative"; public static final String GTASK_SUBMIT_DEADLINE_RELATIVE_TO = "grouptask.submit.deadline.relative.to"; public static final String GTASK_REVIEW_AND_CORRECTION = "grouptask.review.and.correction"; public static final String GTASK_REVISION_PERIOD = "grouptask.revision.period"; public static final String GTASK_SAMPLE_SOLUTION = "grouptask.solution"; public static final String GTASK_SAMPLE_SOLUTION_VISIBLE_AFTER = "grouptask.solution.visible.after"; public static final String GTASK_SAMPLE_SOLUTION_VISIBLE_AFTER_RELATIVE = "grouptask.solution.visible.after.relative"; public static final String GTASK_SAMPLE_SOLUTION_VISIBLE_AFTER_RELATIVE_TO = "grouptask.solution.visible.after.relative.to"; public static final String GTASK_GRADING = "grouptask.grading"; public static final String GTASK_TASKS = "grouptask.tasks"; public static final String GTASK_RELATIVE_DATES = "grouptask.rel.dates"; public static final String GTASK_ASSIGNEMENT_TYPE = "grouptask.assignement.type"; public static final String GTASK_ASSIGNEMENT_TYPE_AUTO = "auto"; public static final String GTASK_ASSIGNEMENT_TYPE_MANUAL = "manual"; public static final String GTASK_USERS_TEXT = "grouptask.users.text"; public static final String GTASK_PREVIEW = "grouptask.preview"; public static final String GTASK_SAMPLING = "grouptask.sampling"; public static final String GTASK_SAMPLING_REUSE = "reuse"; public static final String GTASK_SAMPLING_UNIQUE = "unique"; public static final String GTASK_EXTERNAL_EDITOR = "grouptask.external.editor"; public static final String GTASK_EMBBEDED_EDITOR = "grouptask.embbeded.editor"; public static final String GTASK_MAX_SUBMITTED_DOCS = "grouptask.max.submitted.docs"; public static final String GTASK_SUBMISSION_TEXT = "grouptask.submission.text"; public static final String GTASK_SUBMISSION_MAIL_CONFIRMATION = "grouptask.submission.mail.confirmation"; public static final String GTASK_SOLUTIONS = "grouptask.solutions"; public static final String TYPE_GROUP = "gta"; public static final String TYPE_INDIVIDUAL = "ita"; public GTACourseNode() { super(TYPE_GROUP); updateModuleConfigDefaults(true); } public GTACourseNode(String type) { super(type); updateModuleConfigDefaults(true); } @Override public boolean needsReferenceToARepositoryEntry() { return false; } @Override public RepositoryEntry getReferencedRepositoryEntry() { return null; } @Override public void updateModuleConfigDefaults(boolean isNewNode) { if(isNewNode) { //setup default configuration ModuleConfiguration config = getModuleConfiguration(); //group task if(getType().equals(TYPE_INDIVIDUAL)) { config.setStringValue(GTASK_TYPE, GTAType.individual.name()); } else { config.setStringValue(GTASK_TYPE, GTAType.group.name()); } //manual choice config.setStringValue(GTASK_ASSIGNEMENT_TYPE, GTASK_ASSIGNEMENT_TYPE_MANUAL); //all steps config.setBooleanEntry(GTASK_ASSIGNMENT, true); config.setBooleanEntry(GTASK_SUBMIT, true); config.setBooleanEntry(GTASK_REVIEW_AND_CORRECTION, true); config.setBooleanEntry(GTASK_REVISION_PERIOD, true); config.setBooleanEntry(GTASK_SAMPLE_SOLUTION, true); config.setBooleanEntry(GTASK_GRADING, true); //editors config.setBooleanEntry(GTACourseNode.GTASK_EXTERNAL_EDITOR, true); config.setBooleanEntry(GTACourseNode.GTASK_EMBBEDED_EDITOR, true); //reuse tasks config.setStringValue(GTACourseNode.GTASK_SAMPLING, GTACourseNode.GTASK_SAMPLING_REUSE); //configure grading config.set(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD, Boolean.FALSE); config.set(MSCourseNode.CONFIG_KEY_SCORE_MIN, new Float(0)); config.set(MSCourseNode.CONFIG_KEY_SCORE_MAX, new Float(0)); config.set(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD, Boolean.TRUE); config.set(MSCourseNode.CONFIG_KEY_HAS_COMMENT_FIELD, Boolean.TRUE); } } @Override public StatusDescription isConfigValid() { if (oneClickStatusCache != null && oneClickStatusCache.length > 0) { return oneClickStatusCache[0]; } List<StatusDescription> statusDescs = validateInternalConfiguration(null); if(statusDescs.isEmpty()) { statusDescs.add(StatusDescription.NOERROR); } oneClickStatusCache = StatusDescriptionHelper.sort(statusDescs); return oneClickStatusCache[0]; } @Override public StatusDescription[] isConfigValid(CourseEditorEnv cev) { oneClickStatusCache = null;//delete the cache List<StatusDescription> sds = isConfigValidWithTranslator(cev, PACKAGE_GTA, getConditionExpressions()); if(oneClickStatusCache != null && oneClickStatusCache.length > 0) { //isConfigValidWithTranslator add first sds.remove(oneClickStatusCache[0]); } sds.addAll(validateInternalConfiguration(cev)); oneClickStatusCache = StatusDescriptionHelper.sort(sds); return oneClickStatusCache; } private List<StatusDescription> validateInternalConfiguration(CourseEditorEnv cev) { List<StatusDescription> sdList = new ArrayList<>(5); ModuleConfiguration config = getModuleConfiguration(); boolean hasScoring = config.getBooleanSafe(GTASK_GRADING); if (hasScoring) { if(!config.getBooleanSafe(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD) && !config.getBooleanSafe(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD) && !config.getBooleanSafe(MSCourseNode.CONFIG_KEY_HAS_COMMENT_FIELD)) { addStatusErrorDescription("error.missing.score.config", GTAEditController.PANE_TAB_GRADING, sdList); } } if(GTAType.group.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE))) { List<Long> groupKeys = config.getList(GTACourseNode.GTASK_GROUPS, Long.class); List<Long> areaKeys = config.getList(GTACourseNode.GTASK_AREAS, Long.class); if(groupKeys.isEmpty() && areaKeys.isEmpty()) { addStatusErrorDescription("error.missing.group", GTAEditController.PANE_TAB_GRADING, sdList); } } //at least one step if(!config.getBooleanSafe(GTACourseNode.GTASK_ASSIGNMENT) && !config.getBooleanSafe(GTACourseNode.GTASK_SUBMIT) && !config.getBooleanSafe(GTACourseNode.GTASK_REVIEW_AND_CORRECTION) && !config.getBooleanSafe(GTACourseNode.GTASK_REVISION_PERIOD) && !config.getBooleanSafe(GTACourseNode.GTASK_SAMPLE_SOLUTION) && !config.getBooleanSafe(GTACourseNode.GTASK_GRADING)) { addStatusErrorDescription("error.select.atleastonestep", GTAEditController.PANE_TAB_WORKLOW, sdList); } if(cev != null) { //check assignment GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); RepositoryEntry courseRe = cev.getCourseGroupManager().getCourseEntry(); ICourse course = CourseFactory.loadCourse(courseRe); if(config.getBooleanSafe(GTACourseNode.GTASK_ASSIGNMENT)) { File taskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), this); if(!TaskHelper.hasDocuments(taskDirectory)) { if(config.getBooleanSafe(GTACourseNode.GTASK_COACH_ALLOWED_UPLOAD_TASKS, false)) { addStatusWarningDescription("error.missing.tasks", GTAEditController.PANE_TAB_ASSIGNMENT, sdList); } else { addStatusErrorDescription("error.missing.tasks", GTAEditController.PANE_TAB_ASSIGNMENT, sdList); } } else { List<TaskDefinition> taskList = gtaManager.getTaskDefinitions(course.getCourseEnvironment(), this); if(taskList == null || taskList.isEmpty()) { if(config.getBooleanSafe(GTACourseNode.GTASK_COACH_ALLOWED_UPLOAD_TASKS, false)) { addStatusWarningDescription("error.missing.tasks", GTAEditController.PANE_TAB_ASSIGNMENT, sdList); } else { addStatusErrorDescription("error.missing.tasks", GTAEditController.PANE_TAB_ASSIGNMENT, sdList); } } else { String[] filenames = taskDirectory.list(); for(TaskDefinition taskDef: taskList) { boolean found = false; for(String filename:filenames) { if(filename.equals(taskDef.getFilename())) { found = true; break; } } if(!found) { addStatusWarningDescription("error.missing.file", GTAEditController.PANE_TAB_ASSIGNMENT, sdList); } } } } } //check solutions if(config.getBooleanSafe(GTACourseNode.GTASK_SAMPLE_SOLUTION)) { File solutionDirectory = gtaManager.getSolutionsDirectory(course.getCourseEnvironment(), this); if(!TaskHelper.hasDocuments(solutionDirectory)) { if(config.getBooleanSafe(GTACourseNode.GTASK_COACH_ALLOWED_UPLOAD_TASKS, false)) { addStatusWarningDescription("error.missing.solutions", GTAEditController.PANE_TAB_SOLUTIONS, sdList); } else { addStatusErrorDescription("error.missing.solutions", GTAEditController.PANE_TAB_SOLUTIONS, sdList); } } } List<IdentityRef> participants = gtaManager.getDuplicatedMemberships(this); if(participants.size() > 0) { UserManager um = CoreSpringFactory.getImpl(UserManager.class); StringBuilder sb = new StringBuilder(); for(IdentityRef participant:participants) { String fullname = um.getUserDisplayName(participant.getKey()); if(sb.length() > 0) sb.append(", "); sb.append(fullname); } String[] params = new String[] { getShortTitle(), sb.toString() }; StatusDescription sd = new StatusDescription(StatusDescription.WARNING, "error.duplicate.memberships", "error.duplicate.memberships", params, PACKAGE_GTA); sd.setDescriptionForUnit(getIdent()); sd.setActivateableViewIdentifier(GTAEditController.PANE_TAB_WORKLOW); sdList.add(sd); } } return sdList; } private void addStatusErrorDescription(String key, String pane, List<StatusDescription> status) { String[] params = new String[] { getShortTitle() }; StatusDescription sd = new StatusDescription(StatusDescription.ERROR, key, key, params, PACKAGE_GTA); sd.setDescriptionForUnit(getIdent()); sd.setActivateableViewIdentifier(pane); status.add(sd); } private void addStatusWarningDescription(String key, String pane, List<StatusDescription> status) { String[] params = new String[] { getShortTitle() }; StatusDescription sd = new StatusDescription(StatusDescription.WARNING, key, key, params, PACKAGE_GTA); sd.setDescriptionForUnit(getIdent()); sd.setActivateableViewIdentifier(pane); status.add(sd); } /** * * The files are exported in export/{node ident}/tasks and export/{node ident}/solutions * */ @Override public void exportNode(File fExportDirectory, ICourse course) { File fNodeExportDir = new File(fExportDirectory, getIdent()); GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); // export the tasks File tasksExportDir = new File(fNodeExportDir, "tasks"); File taskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), this); fNodeExportDir.mkdirs(); FileUtils.copyDirContentsToDir(taskDirectory, tasksExportDir, false, "export task course node"); File taskDefinitions = new File(taskDirectory.getParentFile(), GTAManager.TASKS_DEFINITIONS); if(taskDefinitions.exists()) { File copyTaskDefinitions = new File(tasksExportDir.getParentFile(), GTAManager.TASKS_DEFINITIONS); FileUtils.copyFileToFile(taskDefinitions, copyTaskDefinitions, false); } //export the solutions File fSolExportDir = new File(fNodeExportDir, "solutions"); File solutionsDirectory = gtaManager.getSolutionsDirectory(course.getCourseEnvironment(), this); fSolExportDir.mkdirs(); FileUtils.copyDirContentsToDir(solutionsDirectory, fSolExportDir, false, "export task course node solutions"); File solutionDefinitions = new File(solutionsDirectory.getParentFile(), GTAManager.SOLUTIONS_DEFINITIONS); if(solutionDefinitions.exists()) { File copySolutionDefinitions = new File(fSolExportDir.getParentFile(), GTAManager.SOLUTIONS_DEFINITIONS); FileUtils.copyFileToFile(solutionDefinitions, copySolutionDefinitions, false); } } @Override public void importNode(File importDirectory, ICourse course, Identity owner, Locale locale, boolean withReferences) { File fNodeImportDir = new File(importDirectory, getIdent()); GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); //import tasks File tasksImportDir = new File(fNodeImportDir, "tasks"); File taskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), this); FileUtils.copyDirContentsToDir(tasksImportDir, taskDirectory, false, "import task course node"); File taskDefinitions = new File(tasksImportDir.getParentFile(), GTAManager.TASKS_DEFINITIONS); if(taskDefinitions.exists()) { File copyTaskDefinitions = new File(taskDirectory.getParentFile(), GTAManager.TASKS_DEFINITIONS); FileUtils.copyFileToFile(taskDefinitions, copyTaskDefinitions, false); } //import solutions File fSolImportDir = new File(fNodeImportDir, "solutions"); File solutionsDirectory = gtaManager.getSolutionsDirectory(course.getCourseEnvironment(), this); FileUtils.copyDirContentsToDir(fSolImportDir, solutionsDirectory, false, "import task course node solutions"); File solutionDefinitions = new File(fSolImportDir.getParentFile(), GTAManager.SOLUTIONS_DEFINITIONS); if(solutionDefinitions.exists()) { File copySolutionDefinitions = new File(solutionsDirectory.getParentFile(), GTAManager.SOLUTIONS_DEFINITIONS); FileUtils.copyFileToFile(solutionDefinitions, copySolutionDefinitions, false); } RepositoryEntry entry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); gtaManager.createIfNotExists(entry, this); } @Override public void postCopy(CourseEnvironmentMapper envMapper, Processing processType, ICourse course, ICourse sourceCourse) { super.postCopy(envMapper, processType, course, sourceCourse); //change groups and areas mapping postImportCopy(envMapper); GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); //copy tasks File sourceTaskDirectory = gtaManager.getTasksDirectory(sourceCourse.getCourseEnvironment(), this); File copyTaskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), this); FileUtils.copyDirContentsToDir(sourceTaskDirectory, copyTaskDirectory, false, "copy task course node"); File taskDefinitions = new File(sourceTaskDirectory.getParentFile(), GTAManager.TASKS_DEFINITIONS); if(taskDefinitions.exists()) { File copyTaskDefinitions = new File(copyTaskDirectory.getParentFile(), GTAManager.TASKS_DEFINITIONS); FileUtils.copyFileToFile(taskDefinitions, copyTaskDefinitions, false); } //copy solutions File sourceSolutionsDirectory = gtaManager.getSolutionsDirectory(sourceCourse.getCourseEnvironment(), this); File copySolutionsDirectory = gtaManager.getSolutionsDirectory(course.getCourseEnvironment(), this); FileUtils.copyDirContentsToDir(sourceSolutionsDirectory, copySolutionsDirectory, false, "copy task course node solutions"); File solutionDefinitions = new File(sourceSolutionsDirectory.getParentFile(), GTAManager.SOLUTIONS_DEFINITIONS); if(solutionDefinitions.exists()) { File copySolutionDefinitions = new File(copySolutionsDirectory.getParentFile(), GTAManager.SOLUTIONS_DEFINITIONS); FileUtils.copyFileToFile(solutionDefinitions, copySolutionDefinitions, false); } RepositoryEntry entry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); gtaManager.createIfNotExists(entry, this); } @Override public void postImport(File importDirectory, ICourse course, CourseEnvironmentMapper envMapper, Processing processType) { super.postImport(importDirectory, course, envMapper, processType); postImportCopy(envMapper); } private void postImportCopy(CourseEnvironmentMapper envMapper) { ModuleConfiguration mc = getModuleConfiguration(); List<Long> groupKeys = mc.getList(GTACourseNode.GTASK_GROUPS, Long.class); if(groupKeys != null) { groupKeys = envMapper.toGroupKeyFromOriginalKeys(groupKeys); } mc.set(GTACourseNode.GTASK_GROUPS, groupKeys); List<Long> areaKeys = mc.getList(GTACourseNode.GTASK_AREAS, Long.class); if(areaKeys != null) { areaKeys = envMapper.toAreaKeyFromOriginalKeys(areaKeys); } mc.set(GTACourseNode.GTASK_AREAS, areaKeys); } @Override public CourseNode createInstanceForCopy(boolean isNewTitle, ICourse course, Identity author) { GTACourseNode cNode = (GTACourseNode)super.createInstanceForCopy(isNewTitle, course, author); GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); //copy tasks File taskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), this); File copyTaskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), cNode); FileUtils.copyDirContentsToDir(taskDirectory, copyTaskDirectory, false, "copy task course node"); File taskDefinitions = new File(taskDirectory.getParentFile(), GTAManager.TASKS_DEFINITIONS); if(taskDefinitions.exists()) { File copyTaskDefinitions = new File(copyTaskDirectory.getParentFile(), GTAManager.TASKS_DEFINITIONS); FileUtils.copyFileToFile(taskDefinitions, copyTaskDefinitions, false); } //copy solutions File solutionsDirectory = gtaManager.getSolutionsDirectory(course.getCourseEnvironment(), this); File copySolutionsDirectory = gtaManager.getSolutionsDirectory(course.getCourseEnvironment(), cNode); FileUtils.copyDirContentsToDir(solutionsDirectory, copySolutionsDirectory, false, "copy task course node solutions"); File solutionDefinitions = new File(solutionsDirectory.getParentFile(), GTAManager.SOLUTIONS_DEFINITIONS); if(solutionDefinitions.exists()) { File copySolutionDefinitions = new File(copySolutionsDirectory.getParentFile(), GTAManager.SOLUTIONS_DEFINITIONS); FileUtils.copyFileToFile(solutionDefinitions, copySolutionDefinitions, false); } return cNode; } @Override public boolean archiveNodeData(Locale locale, ICourse course, ArchiveOptions options, ZipOutputStream exportStream, String charset) { final GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); final ModuleConfiguration config = getModuleConfiguration(); String prefix; if(GTAType.group.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE))) { prefix = "grouptask_"; } else { prefix = "ita_"; } String dirName = prefix + StringHelper.transformDisplayNameToFileSystemName(getShortName()) + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())); TaskList taskList = gtaManager.getTaskList(course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(), this); //save assessment datas List<Identity> users = null; if(config.getBooleanSafe(GTASK_GRADING)) { users = ScoreAccountingHelper.loadUsers(course.getCourseEnvironment(), options); String courseTitle = course.getCourseTitle(); String fileName = ExportUtil.createFileNameWithTimeStamp(courseTitle, "xlsx"); List<AssessableCourseNode> nodes = Collections.<AssessableCourseNode>singletonList(this); try { exportStream.putNextEntry(new ZipEntry(dirName + "/" + fileName)); ScoreAccountingHelper.createCourseResultsOverviewXMLTable(users, nodes, course, locale, new ShieldOutputStream(exportStream)); exportStream.closeEntry(); } catch (Exception e) { log.error("", e); } } //copy tasks if(taskList != null) { if(GTAType.group.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE))) { List<BusinessGroup> selectedGroups; if(options != null && options.getGroup() != null) { selectedGroups = Collections.singletonList(options.getGroup()); } else { selectedGroups = gtaManager.getBusinessGroups(this); } for(BusinessGroup businessGroup:selectedGroups) { archiveNodeData(course, businessGroup, taskList, dirName, exportStream); } } else { if(users == null) { users = ScoreAccountingHelper.loadUsers(course.getCourseEnvironment(), options); } Set<Identity> uniqueUsers = new HashSet<>(users); for(Identity user: uniqueUsers) { archiveNodeData(course, user, taskList, dirName, exportStream); } } } //copy solutions if(config.getBooleanSafe(GTACourseNode.GTASK_SAMPLE_SOLUTION)) { VFSContainer solutions = gtaManager.getSolutionsContainer(course.getCourseEnvironment(), this); if (solutions.exists()) { String solutionDirName = dirName + "/solutions"; for(VFSItem solution:solutions.getItems(new SystemItemFilter())) { ZipUtil.addToZip(solution, solutionDirName, exportStream); } } } return true; } private void archiveNodeData(ICourse course, Identity assessedIdentity, TaskList taskList, String dirName, ZipOutputStream exportStream) { ModuleConfiguration config = getModuleConfiguration(); GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); int flow = 0;//for beautiful ordering String userDirName = dirName + "/" + StringHelper.transformDisplayNameToFileSystemName(assessedIdentity.getName()) + "_" + assessedIdentity.getKey(); Task task = gtaManager.getTask(assessedIdentity, taskList); if(task != null && task.getTaskName() != null && config.getBooleanSafe(GTASK_ASSIGNMENT)) { File taskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), this); File taskFile = new File(taskDirectory, task.getTaskName()); if(taskFile.exists()) { String path = userDirName + "/" + (++flow) + "_task/" + taskFile.getName(); ZipUtil.addFileToZip(path, taskFile, exportStream); } } if(config.getBooleanSafe(GTASK_SUBMIT)) { File submitDirectory = gtaManager.getSubmitDirectory(course.getCourseEnvironment(), this, assessedIdentity); String submissionDirName = userDirName + "/" + (++flow) + "_submissions"; ZipUtil.addDirectoryToZip(submitDirectory.toPath(), submissionDirName, exportStream); } if(config.getBooleanSafe(GTACourseNode.GTASK_REVIEW_AND_CORRECTION)) { File correctionsDir = gtaManager.getCorrectionDirectory(course.getCourseEnvironment(), this, assessedIdentity); String correctionDirName = userDirName + "/" + (++flow) + "_corrections"; ZipUtil.addDirectoryToZip(correctionsDir.toPath(), correctionDirName, exportStream); } if(task != null && config.getBooleanSafe(GTACourseNode.GTASK_REVISION_PERIOD)) { int numOfIteration = task.getRevisionLoop(); for(int i=1; i<=numOfIteration; i++) { File revisionDirectory = gtaManager.getRevisedDocumentsDirectory(course.getCourseEnvironment(), this, i, assessedIdentity); String revisionDirName = userDirName + "/" + (++flow) + "_revisions_" + i; ZipUtil.addDirectoryToZip(revisionDirectory.toPath(), revisionDirName, exportStream); File correctionDirectory = gtaManager.getRevisedDocumentsCorrectionsDirectory(course.getCourseEnvironment(), this, i, assessedIdentity); String correctionDirName = userDirName + "/" + (++flow) + "_corrections_" + i; ZipUtil.addDirectoryToZip(correctionDirectory.toPath(), correctionDirName, exportStream); } } } private void archiveNodeData(ICourse course, BusinessGroup businessGroup, TaskList taskList, String dirName, ZipOutputStream exportStream) { ModuleConfiguration config = getModuleConfiguration(); GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); int flow = 0;//for beautiful ordering String groupDirName = dirName + "/" + StringHelper.transformDisplayNameToFileSystemName(businessGroup.getName()) + "_" + businessGroup.getKey(); Task task = gtaManager.getTask(businessGroup, taskList); if(task != null && task.getTaskName() != null && config.getBooleanSafe(GTASK_ASSIGNMENT)) { File taskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), this); File taskFile = new File(taskDirectory, task.getTaskName()); if(taskFile.exists()) { String path = groupDirName + "/" + (++flow) + "_task/" + taskFile.getName(); ZipUtil.addFileToZip(path, taskFile, exportStream); } } if(config.getBooleanSafe(GTASK_SUBMIT)) { File submitDirectory = gtaManager.getSubmitDirectory(course.getCourseEnvironment(), this, businessGroup); String submissionDirName = groupDirName + "/" + (++flow) + "_submissions"; ZipUtil.addDirectoryToZip(submitDirectory.toPath(), submissionDirName, exportStream); } if(config.getBooleanSafe(GTACourseNode.GTASK_REVIEW_AND_CORRECTION)) { File correctionsDir = gtaManager.getCorrectionDirectory(course.getCourseEnvironment(), this, businessGroup); String correctionDirName = groupDirName + "/" + (++flow) + "_corrections"; ZipUtil.addDirectoryToZip(correctionsDir.toPath(), correctionDirName, exportStream); } if(task != null && config.getBooleanSafe(GTACourseNode.GTASK_REVISION_PERIOD)) { int numOfIteration = task.getRevisionLoop(); for(int i=1; i<=numOfIteration; i++) { File revisionDirectory = gtaManager.getRevisedDocumentsDirectory(course.getCourseEnvironment(), this, i, businessGroup); String revisionDirName = groupDirName + "/" + (++flow) + "_revisions_" + i; ZipUtil.addDirectoryToZip(revisionDirectory.toPath(), revisionDirName, exportStream); File correctionDirectory = gtaManager.getRevisedDocumentsCorrectionsDirectory(course.getCourseEnvironment(), this, i, businessGroup); String correctionDirName = groupDirName + "/" + (++flow) + "_corrections_" + i; ZipUtil.addDirectoryToZip(correctionDirectory.toPath(), correctionDirName, exportStream); } } } @Override public void cleanupOnDelete(ICourse course) { GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class); //tasks File taskDirectory = gtaManager.getTasksDirectory(course.getCourseEnvironment(), this); FileUtils.deleteDirsAndFiles(taskDirectory, true, true); //solutions File solutionsDirectory = gtaManager.getSolutionsDirectory(course.getCourseEnvironment(), this); FileUtils.deleteDirsAndFiles(solutionsDirectory, true, true); //clean up database RepositoryEntry entry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); gtaManager.deleteTaskList(entry, this); //clean subscription SubscriptionContext subscriptionContext = gtaManager.getSubscriptionContext(course.getCourseEnvironment(), this); NotificationsManager.getInstance().delete(subscriptionContext); } @Override public boolean isAssessedBusinessGroups() { return GTAType.group.name().equals(getModuleConfiguration().getStringValue(GTACourseNode.GTASK_TYPE)); } @Override public boolean hasStatusConfigured() { return true; // Task Course node has always a status-field } @Override public Float getMaxScoreConfiguration() { if (!hasScoreConfigured()) { throw new OLATRuntimeException(TACourseNode.class, "getMaxScore not defined when hasScore set to false", null); } return getModuleConfiguration().getFloatEntry(MSCourseNode.CONFIG_KEY_SCORE_MAX); } @Override public Float getMinScoreConfiguration() { if (!hasScoreConfigured()) { throw new OLATRuntimeException(TACourseNode.class, "getMinScore not defined when hasScore set to false", null); } return getModuleConfiguration().getFloatEntry(MSCourseNode.CONFIG_KEY_SCORE_MIN); } @Override public Float getCutValueConfiguration() { if (!hasPassedConfigured()) { throw new OLATRuntimeException(TACourseNode.class, "getCutValue not defined when hasPassed set to false", null); } return getModuleConfiguration().getFloatEntry(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); } @Override public boolean hasScoreConfigured() { boolean hasGrading = getModuleConfiguration().getBooleanSafe(GTASK_GRADING); if (hasGrading) { Boolean score = (Boolean) getModuleConfiguration().get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD); if (score != null) { return score.booleanValue(); } } return false; } @Override public boolean hasPassedConfigured() { boolean hasGrading = getModuleConfiguration().getBooleanSafe(GTASK_GRADING); if (hasGrading) { Boolean passed = (Boolean)getModuleConfiguration().get(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD); if (passed != null) { return passed.booleanValue(); } } return false; } @Override public boolean hasCommentConfigured() { boolean hasGrading = getModuleConfiguration().getBooleanSafe(GTASK_GRADING); if (hasGrading) { Boolean comment = (Boolean) getModuleConfiguration().get(MSCourseNode.CONFIG_KEY_HAS_COMMENT_FIELD); if (comment != null) { return comment.booleanValue(); } } return false; } @Override public boolean hasAttemptsConfigured(){ return true; } @Override public boolean hasDetails() { ModuleConfiguration config = getModuleConfiguration(); return config.getBooleanSafe(GTASK_ASSIGNMENT) || config.getBooleanSafe(GTASK_SUBMIT) || config.getBooleanSafe(GTASK_REVIEW_AND_CORRECTION) || config.getBooleanSafe(GTASK_REVISION_PERIOD); } @Override public boolean isEditableConfigured() { return true; } @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, UserCourseEnvironment euce) { GTAEditController editCtrl = new GTAEditController(ureq, wControl, this, course, euce); CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); return new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, euce, editCtrl); } @Override public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { Controller controller; Roles roles = ureq.getUserSession().getRoles(); if (roles.isGuestOnly()) { Translator trans = Util.createPackageTranslator(GTACourseNode.class, ureq.getLocale()); String title = trans.translate("guestnoaccess.title"); String message = trans.translate("guestnoaccess.message"); controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); } else { controller = new GTARunController(ureq, wControl, this, userCourseEnv); } Controller ctrl = TitledWrapperHelper.getWrapper(ureq, wControl, controller, this, "o_gta_icon"); return new NodeRunConstructionResult(ctrl); } @Override public void updateOnPublish(Locale locale, ICourse course, Identity publisher, PublishEvents publishEvents) { RepositoryEntry re = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); CoreSpringFactory.getImpl(GTAManager.class).createIfNotExists(re, this); super.updateOnPublish(locale, course, publisher, publishEvents); } @Override public String getDetailsListViewHeaderKey() { if(getModuleConfiguration().getBooleanSafe(GTASK_ASSIGNMENT)) { return "table.header.details.gta"; } return null; } @Override public String getDetailsListView(UserCourseEnvironment userCourseEnvironment) { Identity assessedIdentity = userCourseEnvironment.getIdentityEnvironment().getIdentity(); RepositoryEntry entry = userCourseEnvironment.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); return CoreSpringFactory.getImpl(GTAManager.class).getDetails(assessedIdentity, entry, this); } @Override public Controller getDetailsEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, UserCourseEnvironment coachCourseEnv, UserCourseEnvironment assessedUsserCourseEnv) { return new GTAAssessmentDetailsController(ureq, wControl, coachCourseEnv, assessedUsserCourseEnv, this); } public GTACoachedGroupListController getCoachedGroupListController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, UserCourseEnvironment coachCourseEnv, boolean admin, List<BusinessGroup> coachedGroups) { List<BusinessGroup> groups; CourseGroupManager gm = coachCourseEnv.getCourseEnvironment().getCourseGroupManager(); if(admin) { groups = gm.getAllBusinessGroups(); } else { groups = coachedGroups; } groups = CoreSpringFactory.getImpl(GTAManager.class).filterBusinessGroups(groups, this); return new GTACoachedGroupListController(ureq, wControl, stackPanel, coachCourseEnv, this, groups); } @Override public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, UserCourseEnvironment coachCourseEnv, AssessmentToolOptions options) { ModuleConfiguration config = getModuleConfiguration(); CourseEnvironment courseEnv = coachCourseEnv.getCourseEnvironment(); List<Controller> tools = new ArrayList<>(2); if(GTAType.group.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE)) && (config.getBooleanSafe(GTASK_ASSIGNMENT) || config.getBooleanSafe(GTASK_SUBMIT) || config.getBooleanSafe(GTASK_REVIEW_AND_CORRECTION) || config.getBooleanSafe(GTASK_REVISION_PERIOD))) { if(options.getGroup() != null && !coachCourseEnv.isCourseReadOnly()) { tools.add(new GTAGroupAssessmentToolController(ureq, wControl, courseEnv, options.getGroup(), this)); } tools.add(new BulkDownloadToolController(ureq, wControl, courseEnv, options, this)); } else if(GTAType.individual.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE))) { if(!coachCourseEnv.isCourseReadOnly() && (config.getBooleanSafe(GTASK_REVIEW_AND_CORRECTION) || config.getBooleanSafe(GTASK_GRADING))){ tools.add(new BulkAssessmentToolController(ureq, wControl, courseEnv, this)); } if(config.getBooleanSafe(GTASK_ASSIGNMENT) || config.getBooleanSafe(GTASK_SUBMIT) || config.getBooleanSafe(GTASK_REVIEW_AND_CORRECTION) || config.getBooleanSafe(GTASK_REVISION_PERIOD)) { tools.add(new BulkDownloadToolController(ureq, wControl, courseEnv, options, this)); } } return tools; } @Override public AssessmentEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) { if(hasPassedConfigured() || hasScoreConfigured()) { return getUserScoreEvaluation(getUserAssessmentEntry(userCourseEnv)); } return AssessmentEvaluation.EMPTY_EVAL; } @Override public AssessmentEvaluation getUserScoreEvaluation(AssessmentEntry entry) { return AssessmentEvaluation.toAssessmentEvalutation(entry, this); } @Override public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity(); return am.getAssessmentEntry(this, mySelf); } @Override public String getUserUserComment(UserCourseEnvironment userCourseEnv) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); return am.getNodeComment(this, userCourseEnv.getIdentityEnvironment().getIdentity()); } @Override public String getUserCoachComment(UserCourseEnvironment userCourseEnv) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); return am.getNodeCoachComment(this, userCourseEnv.getIdentityEnvironment().getIdentity()); } @Override public String getUserLog(UserCourseEnvironment userCourseEnv) { UserNodeAuditManager am = userCourseEnv.getCourseEnvironment().getAuditManager(); return am.getUserNodeLog(this, userCourseEnv.getIdentityEnvironment().getIdentity()); } @Override public Integer getUserAttempts(UserCourseEnvironment userCourseEnv) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); return am.getNodeAttempts(this, assessedIdentity); } @Override public void updateUserScoreEvaluation(ScoreEvaluation scoreEvaluation, UserCourseEnvironment userCourseEnv, Identity coachingIdentity, boolean incrementAttempts) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); am.saveScoreEvaluation(this, coachingIdentity, assessedIdentity, new ScoreEvaluation(scoreEvaluation), userCourseEnv, incrementAttempts); } @Override public void updateUserUserComment(String userComment, UserCourseEnvironment userCourseEnv, Identity coachingIdentity) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); if (userComment != null) { am.saveNodeComment(this, coachingIdentity, assessedIdentity, userComment); } } @Override public void incrementUserAttempts(UserCourseEnvironment userCourseEnv) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); am.incrementNodeAttempts(this, assessedIdentity, userCourseEnv); } @Override public void updateUserAttempts(Integer userAttempts, UserCourseEnvironment userCourseEnv, Identity coachingIdentity) { if (userAttempts != null) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); am.saveNodeAttempts(this, coachingIdentity, assessedIdentity, userAttempts); } } @Override public void updateUserCoachComment(String coachComment, UserCourseEnvironment userCourseEnv) { AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); if (coachComment != null) { am.saveNodeCoachComment(this, assessedIdentity, coachComment); } } }