/**
* 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) 2009 frentix GmbH, Switzerland<br>
* <p>
*/
package org.olat.modules.scorm.archiver;
import java.io.File;
import java.util.List;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.ExportUtil;
import org.olat.core.util.vfs.LocalFileImpl;
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.archiver.ScoreAccountingHelper;
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;
public class ScormExportManager {
private static final String CMI_OBJECTIVES = "cmi.objectives.";
private static final String CMI_INTERACTIONS = "cmi.interactions.";
private static final String CMI_RAW_SCORE = "cmi.core.score.raw";
private static final String CMI_LESSON_STATUS = "cmi.core.lesson_status";
private static final String CMI_COMMENTS = "cmi.comments";
private static final String CMI_TOTAL_TIME = "cmi.core.total_time";
private static final String CMI_ID = "id";
private static final String CMI_SCORE_RAW = "score.raw";
private static final String CMI_SCORE_MIN = "score.min";
private static final String CMI_SCORE_MAX = "score.max";
private static final String CMI_RESULT = "result";
private static final String CMI_STUDENT_RESPONSE = "student_response";
private static final String CMI_CORRECT_RESPONSE = "correct_responses.";
private static final String OBJECTIVES = "objectives.";
private static final String CMI_COUNT = "_count";
private static final OLog logger = Tracing.createLoggerFor(ScormExportManager.class);
private static final ScormExportManager instance = new ScormExportManager();
private ScormExportManager(){
//
}
public static ScormExportManager getInstance() {
return instance;
}
/**
* Export the results of a SCORM course
* @param courseEnv
* @param node
* @param translator
* @param exportDirectory
* @param charset
* @return the name of the file
*/
public String exportResults(CourseEnvironment courseEnv, ScormCourseNode node, Translator translator, File exportDirectory, String charset) {
ScormExportVisitor visitor = new ScormExportFormatter(translator);
visitScoDatas(courseEnv, node, visitor);
String content = visitor.toString();
String fileName = ExportUtil.createFileNameWithTimeStamp("SCORM_" + node.getShortTitle(), "xls");
ExportUtil.writeContentToFile(fileName, content, exportDirectory, charset);
return fileName;
}
/**
* Visit the scos user's datamodel of a SCORM course. The users must be in a group.
* @param courseEnv
* @param node
* @param visitor
*/
public void visitScoDatas(CourseEnvironment courseEnv, ScormCourseNode node, ScormExportVisitor visitor) {
Long courseId = courseEnv.getCourseResourceableId();
String scoDirectoryName = courseId.toString() + "-" + node.getIdent();
VFSContainer scormRoot = ScormDirectoryHelper.getScormRootFolder();
List<Identity> users = ScoreAccountingHelper.loadUsers(courseEnv);
for (Identity identity : users) {
String username = identity.getName();
VFSItem userFolder = scormRoot.resolve(username);
if(userFolder instanceof VFSContainer) {
VFSItem scosFolder = ((VFSContainer)userFolder).resolve(scoDirectoryName);
if(scosFolder instanceof VFSContainer) {
collectData(username, (VFSContainer)scosFolder, visitor);
}
}
}
}
private void collectData(String username, VFSContainer scoFolder, ScormExportVisitor visitor) {
List<VFSItem> contents = scoFolder.getItems(new XMLFilter());
for(VFSItem file:contents) {
ScoDocument document = new ScoDocument(null);
try {
if(file instanceof LocalFileImpl) {
document.loadDocument(((LocalFileImpl)file).getBasefile());
}
else {
logger.warn("Cannot use this type of VSFItem to load a SCO Datamodel: " + file.getClass().getName(), null);
continue;
}
String[][] scoModel = document.getScoModel();
ScoDatas parsedDatas = parseScoModel(file.getName(), username, scoModel);
visitor.visit(parsedDatas);
} catch (Exception e) {
logger.error("Cannot load a SCO Datamodel", e);
}
}
}
/**
* Parse the raw cmi datas in a java friendly object.
* @param scoId
* @param username
* @param scoModel
* @return
*/
private ScoDatas parseScoModel(String scoId, String username, String[][] scoModel) {
ScoDatas datas = new ScoDatas(scoId, username);
for(String[] line:scoModel) {
String key = null;
try {
key = line[0];
if(key == null) continue;
String value = line[1];
if(key.equals(CMI_RAW_SCORE)) {
datas.setRawScore(value);
}
else if(key.equals(CMI_LESSON_STATUS)) {
datas.setLessonStatus(value);
}
else if(key.equals(CMI_COMMENTS)) {
datas.setComments(value);
}
else if(key.equals(CMI_TOTAL_TIME)) {
datas.setTotalTime(value);
}
else if(key.startsWith(CMI_OBJECTIVES)) {
String endStr = key.substring(CMI_OBJECTIVES.length());
int nextPoint = endStr.indexOf('.');
if(nextPoint < 0) {
//cmi.objectives._count
continue;
}
String interactionNr = endStr.substring(0, nextPoint);
int nr = Integer.valueOf(interactionNr).intValue();
ScoObjective objective = datas.getObjective(nr);
String endKey = endStr.substring(nextPoint + 1);
if(CMI_ID.equals(endKey)) {
objective.setId(value);
}
if(CMI_SCORE_RAW.equals(endKey)) {
objective.setScoreRaw(value);
}
else if(CMI_SCORE_MIN.equals(endKey)) {
objective.setScoreMin(value);
}
else if(CMI_SCORE_MAX.equals(endKey)) {
objective.setScoreMax(value);
}
}
else if(key.startsWith(CMI_INTERACTIONS)) {
String endStr = key.substring(CMI_INTERACTIONS.length());
int nextPoint = endStr.indexOf('.');
if(nextPoint < 0) {
continue;
}
String interactionNr = endStr.substring(0, nextPoint);
int nr = Integer.valueOf(interactionNr).intValue();
ScoInteraction interaction = datas.getInteraction(nr);
String endKey = endStr.substring(nextPoint + 1);
if(CMI_ID.equals(endKey)) {
interaction.setInteractionId(value);
}
else if(CMI_RESULT.equals(endKey)) {
interaction.setResult(value);
}
else if(CMI_STUDENT_RESPONSE.equals(endKey)) {
interaction.setStudentResponse(value);
}
else if(endKey.startsWith(CMI_CORRECT_RESPONSE)) {
interaction.setCorrectResponse(value);
}
else if(endKey.indexOf(OBJECTIVES) >= 0 && endKey.indexOf(CMI_COUNT) < 0) {
interaction.getObjectiveIds().add(value);
}
}
}
catch(Exception ex) {
logger.debug("Error parse this cmi data: " + key);
}
}
return datas;
}
public class XMLFilter implements VFSItemFilter {
public boolean accept(VFSItem file) {
String name = file.getName();
if(name.endsWith(".xml") && !(name.equals("reload-settings.xml")))
{
return true;
}
return false;
}
}
}