/** * <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.upgrade; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.olat.core.commons.modules.bc.FolderConfig; import org.olat.core.commons.persistence.DB; import org.olat.core.id.Identity; import org.olat.core.id.Roles; import org.olat.core.util.StringHelper; import org.olat.core.util.nodes.INode; import org.olat.core.util.tree.TreeVisitor; import org.olat.core.util.tree.Visitor; import org.olat.course.CorruptedCourseException; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.TACourseNode; import org.olat.course.nodes.ta.DropboxController; import org.olat.course.nodes.ta.ReturnboxController; import org.olat.course.nodes.ta.TaskController; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.environment.CourseEnvironment; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.model.AssessmentEntryStatus; import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; import org.olat.repository.model.SearchRepositoryEntryParameters; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 26 janv. 2017<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class OLATUpgrade_11_2_1 extends OLATUpgrade { private static final int BATCH_SIZE = 50; private static final String STATUS_OLD_TASK_ELEMENT = "STATUS OLD TASK ELEMENT"; private static final String VERSION = "OLAT_11.2.1"; @Autowired private DB dbInstance; @Autowired private RepositoryManager repositoryManager; public OLATUpgrade_11_2_1() { super(); } @Override public String getVersion() { return VERSION; } @Override public boolean doPreSystemInitUpgrade(UpgradeManager upgradeManager) { return false; } @Override public boolean doPostSystemInitUpgrade(UpgradeManager upgradeManager) { UpgradeHistoryData uhd = upgradeManager.getUpgradesHistory(VERSION); if (uhd == null) { // has never been called, initialize uhd = new UpgradeHistoryData(); } else if (uhd.isInstallationComplete()) { return false; } boolean allOk = true; allOk &= upgradeStatus(upgradeManager, uhd); uhd.setInstallationComplete(allOk); upgradeManager.setUpgradesHistory(uhd, VERSION); if(allOk) { log.audit("Finished OLATUpgrade_11_2_1 successfully!"); } else { log.audit("OLATUpgrade_11_2_1 not finished, try to restart OpenOLAT!"); } return allOk; } private boolean upgradeStatus(UpgradeManager upgradeManager, UpgradeHistoryData uhd) { boolean allOk = true; if (!uhd.getBooleanDataValue(STATUS_OLD_TASK_ELEMENT)) { int counter = 0; final Roles roles = new Roles(true, false, false, false, false, false, false); final SearchRepositoryEntryParameters params = new SearchRepositoryEntryParameters(); params.setRoles(roles); params.setResourceTypes(Collections.singletonList("CourseModule")); List<RepositoryEntry> courses; do { courses = repositoryManager.genericANDQueryWithRolesRestriction(params, counter, 50, true); for(RepositoryEntry course:courses) { processCourse(course); } counter += courses.size(); log.audit("Course for checklist and deprecated tasks migration processed: " + courses.size() + ", total courses processed (" + counter + ")"); dbInstance.commitAndCloseSession(); } while(courses.size() == BATCH_SIZE); uhd.setBooleanDataValue(STATUS_OLD_TASK_ELEMENT, allOk); upgradeManager.setUpgradesHistory(uhd, VERSION); } return allOk; } private boolean processCourse(RepositoryEntry entry) { try { ICourse course = CourseFactory.loadCourse(entry); CourseNode rootNode = course.getRunStructure().getRootNode(); final List<TACourseNode> taskNodes = new ArrayList<>(); new TreeVisitor(new Visitor() { @Override public void visit(INode node) { if(node instanceof TACourseNode) { taskNodes.add((TACourseNode)node); } } }, rootNode, false).visitAll(); for(TACourseNode taskNode:taskNodes) { processTaskCourseNode(course, entry, taskNode); } return true; } catch(CorruptedCourseException e) { log.warn("Corrupted course: " + entry.getDisplayname() + " (" + entry.getKey() + ")", e); return true; } catch (Exception e) { log.error("", e); return true; } } private boolean processTaskCourseNode(ICourse course, RepositoryEntry entry, TACourseNode courseNode) { List<AssessmentEntry> assessmentEntries = getAssessmentEntries(entry, courseNode); if(assessmentEntries.size() > 0) { CourseEnvironment courseEnv = course.getCourseEnvironment(); CoursePropertyManager cpm = courseEnv.getCoursePropertyManager(); File dropbox = new File(FolderConfig.getCanonicalRoot(), DropboxController.getDropboxPathRelToFolderRoot(courseEnv, courseNode)); File returnBox = new File(FolderConfig.getCanonicalRoot(), ReturnboxController.getReturnboxPathRelToFolderRoot(courseEnv, courseNode)); for(AssessmentEntry assessmentEntry:assessmentEntries) { Identity assessedIdentity = assessmentEntry.getIdentity(); boolean changed = false; List<Property> properties = cpm.findCourseNodeProperties(courseNode, assessedIdentity, null, TaskController.PROP_ASSIGNED); if(properties != null && properties.size() > 0) { assessmentEntry.setAssessmentStatus(AssessmentEntryStatus.inProgress); } else { File identityDropbox = new File(dropbox, assessedIdentity.getName()); File identityReturnBox = new File(returnBox, assessedIdentity.getName()); if(hasBoxedFiles(identityDropbox, identityReturnBox)) { assessmentEntry.setAssessmentStatus(AssessmentEntryStatus.inProgress); } } if(changed) { courseEnv.getAssessmentManager().updateAssessmentEntry(assessmentEntry); } } dbInstance.commitAndCloseSession(); } return true; } private boolean hasBoxedFiles(File identityDropbox, File identityReturnBox) { if(identityDropbox.exists()) { String[] droppedFilenames = identityDropbox.list(new SystemFilter()); if(droppedFilenames.length > 0) { return true; } } if(identityReturnBox.exists()) { String[] returnededFilenames = identityReturnBox.list(new SystemFilter()); if(returnededFilenames.length > 0) { return true; } } return false; } /** * Return the list of not started assessment entries. * @param entry * @param courseNode * @return A list of assessment entries with status null or notStarted */ private List<AssessmentEntry> getAssessmentEntries(RepositoryEntry entry, CourseNode courseNode) { StringBuilder sb = new StringBuilder(); sb.append("select aentry from assessmententry aentry") .append(" inner join fetch aentry.identity as assessedIdentity") .append(" inner join fetch assessedIdentity.user as assessedUser") .append(" where aentry.repositoryEntry.key=:repoEntryKey and aentry.subIdent=:subIdent") .append(" and (aentry.status is null or aentry.status='").append(AssessmentEntryStatus.notStarted).append("')"); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), AssessmentEntry.class) .setParameter("repoEntryKey", entry.getKey()) .setParameter("subIdent", courseNode.getIdent()) .getResultList(); } private static class SystemFilter implements FilenameFilter { @Override public boolean accept(File dir, String name) { return StringHelper.containsNonWhitespace(name) && !name.startsWith("."); } } }