/**
* 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;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.olat.core.commons.services.taskexecutor.TaskExecutorManager;
import org.olat.core.configuration.AbstractSpringModule;
import org.olat.core.gui.control.Event;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.StringHelper;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.event.GenericEventListener;
import org.olat.course.CourseFactory;
import org.olat.course.CourseModule;
import org.olat.course.ICourse;
import org.olat.course.Structure;
import org.olat.course.assessment.manager.UpdateEfficiencyStatementsWorker;
import org.olat.course.editor.PublishEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* Description:<br>
* This is a PublishEvent listener, and triggers the update of the EfficiencyStatements
* for the published course. It only considers the events from the same JVM.
*
* <P>
* Initial Date: 11.08.2006 <br>
*
* @author patrickb
*/
@Service("assessmentModule")
public class AssessmentModule extends AbstractSpringModule implements GenericEventListener {
private static final OLog log = Tracing.createLoggerFor(AssessmentModule.class);
private List<Long> upcomingWork;
@Value("${assessment.mode:enabled}")
private String assessmentModeEnabled;
@Autowired
private CourseModule courseModule;
@Autowired
private TaskExecutorManager taskExecutorManager;
@Autowired
public AssessmentModule(CoordinatorManager coordinatorManager) {
super(coordinatorManager);
}
@Override
protected void initFromChangedProperties() {
updateProperties();
}
/**
* @see org.olat.core.configuration.OLATModule#init(com.anthonyeden.lib.config.Configuration)
*/
@Override
public void init() {
updateProperties();
upcomingWork = new ArrayList<Long>();
/*
* always last step, register for course events
*/
courseModule.registerForCourseType(this, null);
/*
* no more code after here!
*/
}
private void updateProperties() {
String enabledObj = getStringPropertyValue("assessment.mode", true);
if(StringHelper.containsNonWhitespace(enabledObj)) {
assessmentModeEnabled = enabledObj;
}
}
@Override
public void destroy() {
/*
* first step in destroy, deregister for course events
*/
//no longer listen to changes
courseModule.deregisterForCourseType(this);
/*
* no other code before here!
*/
//check that working queue is empty
if(upcomingWork.size()>0){
//hanging work!!
log.warn("still some Efficiency Statement recalculations open!!");
}
}
/**
* Called at course publish.
* @see org.olat.core.util.event.GenericEventListener#event(org.olat.core.gui.control.Event)
*/
@Override
public void event(Event event) {
if (event instanceof PublishEvent) {
PublishEvent pe = (PublishEvent) event;
if (pe.getState() == PublishEvent.PRE_PUBLISH && pe.isEventOnThisNode()) {
// PRE PUBLISH -> check node for changes
addToUpcomingWork(pe);
return;
} else if (pe.getState() == PublishEvent.PUBLISH && pe.isEventOnThisNode()) {
// a publish event, check if it matches a previous checked
prepareUpdate(pe.getPublishedCourseResId());
}
} else if (event instanceof EfficiencyStatementEvent) {
if(EfficiencyStatementEvent.CMD_RECALCULATE.equals(event.getCommand())) {
EfficiencyStatementEvent esEvent = (EfficiencyStatementEvent)event;
//force recalculate
upcomingWork.add(esEvent.getCourseResourceId());
prepareUpdate(esEvent.getCourseResourceId());
}
}
}
private void prepareUpdate(Long resId) {
boolean recalc = false;
synchronized (upcomingWork) { //o_clusterOK by:ld synchronized OK - only one cluster node must update the EfficiencyStatements (the course is locked for editing) (same as e.g. file indexer)
recalc = upcomingWork.contains(resId);
if (recalc) {
for(; upcomingWork.remove(resId); ) {
//remove all with the same res id
}
}
}
if (recalc) {
ICourse pubCourse = CourseFactory.loadCourse(resId);
UpdateEfficiencyStatementsWorker worker = new UpdateEfficiencyStatementsWorker(pubCourse);
taskExecutorManager.execute(worker);
}
}
/**
* @param pe
*/
private void addToUpcomingWork(PublishEvent pe) {
ICourse course = CourseFactory.loadCourse(pe.getPublishedCourseResId());
boolean courseEfficiencyEnabled = course.getCourseEnvironment().getCourseConfig().isEfficencyStatementEnabled();
if (!courseEfficiencyEnabled) {
// no efficiency enabled, stop here.
return;
}
// deleted + inserted + modified node ids -> changedNodeIds
Set<String> changedNodeIds = pe.getDeletedCourseNodeIds();
changedNodeIds.addAll(pe.getInsertedCourseNodeIds());
changedNodeIds.addAll(pe.getModifiedCourseNodeIds());
//
boolean courseAssessmentChanged = false;
Structure courseRun = course.getRunStructure();
for (Iterator<String> iter = changedNodeIds.iterator(); iter.hasNext();) {
String nodeId = iter.next();
boolean wasNodeAsessable = AssessmentHelper.checkIfNodeIsAssessable(courseRun.getNode(nodeId));
boolean isNodeAssessable = AssessmentHelper.checkIfNodeIsAssessable(course.getEditorTreeModel().getCourseNode(nodeId));
//if node was or became assessable
if (wasNodeAsessable || isNodeAssessable) {
courseAssessmentChanged = true;
break;
}
}
if (!courseAssessmentChanged) {
// assessment changes detected, stop here
return;
}
synchronized (upcomingWork) { //o_clusterOK by:ld synchronized OK - only one cluster node must update the EfficiencyStatements (the course is locked for editing)
upcomingWork.add(course.getResourceableId());
}
return;
}
public boolean isAssessmentModeEnabled() {
return "enabled".equals(assessmentModeEnabled);
}
public void setAssessmentModeEnabled(boolean enabled) {
assessmentModeEnabled = enabled ? "enabled" : "disabled";
setStringProperty("assessment.mode", assessmentModeEnabled, true);
}
}