/**
* <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.modules.scorm.assessment;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.olat.core.logging.OLATRuntimeException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.manager.BasicManager;
import org.olat.core.util.vfs.LocalFileImpl;
import org.olat.core.util.vfs.VFSConstants;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.filters.VFSItemFilter;
import org.olat.course.nodes.ScormCourseNode;
import org.olat.course.run.environment.CourseEnvironment;
import org.olat.modules.scorm.ScormDirectoryHelper;
import org.olat.modules.scorm.server.servermodels.ScoDocument;
import org.olat.modules.scorm.server.servermodels.SequencerModel;
/**
* <P>
* Initial Date: 13 august 2009 <br>
* @author srosse
*/
public class ScormAssessmentManager extends BasicManager {
public static final String RELOAD_SETTINGS_FILE = "reload-settings.xml";
private static final ScormAssessmentManager instance = new ScormAssessmentManager();
private static final OLog logger = Tracing.createLoggerFor(ScormAssessmentManager.class);
public static ScormAssessmentManager getInstance() {
return instance;
}
/**
* Load the SequencerModel
* @param username
* @param courseEnv
* @param node
* @return can be null if the user hasn't visited the course
*/
public SequencerModel getSequencerModel(String username, CourseEnvironment courseEnv, ScormCourseNode node) {
VFSContainer scoDirectory = ScormDirectoryHelper.getScoDirectory(username, courseEnv, node);
if(scoDirectory == null) return null;
VFSItem reloadSettingsFile = scoDirectory.resolve(RELOAD_SETTINGS_FILE);
if(reloadSettingsFile instanceof LocalFileImpl) {
LocalFileImpl fileImpl = (LocalFileImpl)reloadSettingsFile;
return new SequencerModel(fileImpl.getBasefile(), null);
} else if (reloadSettingsFile != null) {
throw new OLATRuntimeException(this.getClass(), "Programming error, SCORM results must be file based", null);
}
return null;
}
//fxdiff FXOLAT-108: reset SCORM test
public boolean deleteResults(String username, CourseEnvironment courseEnv, ScormCourseNode node) {
VFSContainer scoDirectory = ScormDirectoryHelper.getScoDirectory(username, courseEnv, node);
if(scoDirectory == null) return true; //nothing to reset -> ok
return (scoDirectory.delete() == VFSConstants.YES);
}
//<OLATCE-289>
/**
* Method to get a List of cmi datas for every xml file in the users directory.
* They are ordered in a Map with the lastModifiedDate of the file as Key.
* @param username
* @param courseEnv
* @param node
* @return
*/
public Map<Date, List<CmiData>> visitScoDatasMultiResults(String username, CourseEnvironment courseEnv, ScormCourseNode node) {
Map<Date, List<CmiData>> cmiDataObjects = new HashMap<Date, List<CmiData>>();
VFSContainer scoContainer = ScormDirectoryHelper.getScoDirectory(username, courseEnv, node);
if(scoContainer == null) {
return null;
}
Calendar cal = Calendar.getInstance();
List<VFSItem> contents = scoContainer.getItems(new XMLFilter());
for(VFSItem file:contents) {
List<CmiData> item = collectData(file);
if (item != null) {
//modified date
cal.setTimeInMillis(file.getLastModified());
Collections.sort(item, new CmiDataComparator());
cmiDataObjects.put(cal.getTime(), item);
}
}
return cmiDataObjects;
}
/**
* Collects the cmi data of the given Scorm-file.
* @param scoFile
* @return
*/
private List<CmiData> collectData(VFSItem scoFile) {
List<CmiData> datas = new ArrayList<CmiData>();
ScoDocument document = new ScoDocument(null);
try {
if(scoFile instanceof LocalFileImpl) {
document.loadDocument(((LocalFileImpl)scoFile).getBasefile());
}
else {
logger.warn("Cannot use this type of VSFItem to load a SCO Datamodel: " + scoFile.getClass().getName(), null);
return null;
}
String fileName = scoFile.getName();
String itemId = fileName.substring(0, fileName.length() - 4);
String[][] scoModel = document.getScoModel();
for(String[] line:scoModel) {
datas.add(new CmiData(itemId, line[0], line[1]));
}
} catch (Exception e) {
logger.error("Cannot load a SCO Datamodel", e);
}
return datas;
}
// </OLATCE-289>
public String getLastLessonStatus(String username, CourseEnvironment courseEnv, ScormCourseNode node) {
List<CmiData> scoDatas = visitScoDatas(username, courseEnv, node);
for(CmiData scoData:scoDatas) {
if("cmi.core.lesson_status".equals(scoData.getKey())) {
return scoData.getValue();
}
}
return null;
}
/**
* Return all the datas in the sco datamodels of a SCORM course
* @param username
* @param courseEnv
* @param node
* @return
*/
public List<CmiData> visitScoDatas(String username, CourseEnvironment courseEnv, ScormCourseNode node) {
VFSContainer scoContainer = ScormDirectoryHelper.getScoDirectory(username, courseEnv, node);
if(scoContainer == null) {
return Collections.emptyList();
}
List<VFSItem> contents = scoContainer.getItems(new XMLFilter());
if(contents.isEmpty()) {
return Collections.emptyList();
}
if(contents.size() > 1) {
Collections.sort(contents, new FileDateComparator());
}
VFSItem file = contents.get(0);
List<CmiData> datas = collectData(file);
return datas;
}
public class XMLFilter implements VFSItemFilter {
public boolean accept(VFSItem file) {
String name = file.getName();
if(name.endsWith(".xml") && !(name.equals(RELOAD_SETTINGS_FILE)))
{
return true;
}
return false;
}
}
public class FileDateComparator implements Comparator<VFSItem> {
@Override
public int compare(VFSItem f1, VFSItem f2) {
if(f1 == null) return -1;
if(f2 == null) return 1;
long l1 = f1.getLastModified();
long l2 = f2.getLastModified();
if(l1 < l2) {
return 1;
}
if (l1 == l2) {
return 0;
}
return -1;
}
}
}