/**
* 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.statistic;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.translator.Translator;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.manager.BasicManager;
import org.olat.core.util.ExportUtil;
import org.olat.core.util.Util;
import org.olat.core.util.WebappHelper;
import org.olat.core.util.ZipUtil;
import org.olat.core.util.i18n.I18nManager;
import org.olat.core.util.mail.MailBundle;
import org.olat.core.util.mail.MailManager;
import org.olat.core.util.vfs.LocalFileImpl;
import org.olat.core.util.vfs.LocalFolderImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.course.statistic.export.ICourseLogExporter;
/**
*
* Description:<br>
* The Manager exports the course log files and statistic file as zip archive.
* <P>
* Initial Date: 19.11.2009 <br>
* @author bja
*/
public class ExportManager extends BasicManager {
/** the logging object used in this class **/
private static final OLog log_ = Tracing.createLoggerFor(ExportManager.class);
/** ExportManager is a singleton, configured by spring **/
private static ExportManager INSTANCE;
/**
* filename used to store courseauthor's activities (personalized)
*/
private static final String FILENAME_ADMIN_LOG = "course_admin_log.csv";
/**
* filename used to store all user's activities (personalized) in the course
* only visible for OLAT-admins
*/
private static final String FILENAME_USER_LOG = "course_user_log.csv";
/**
* filename used to store all user's activities (anonymized) in the course
*/
private static final String FILENAME_STATISTIC_LOG = "course_statistic_log.csv";
/**
* zip filename substring (archive log files)
*/
public static final String COURSE_LOG_FILES = "CourseLogFiles";
/**
* zip filename substring (statistic)
*/
public static final String COURSE_STATISTIC = "CourseStatistic";
/** injected via spring **/
private ICourseLogExporter courseLogExporter;
/** created via spring **/
private ExportManager() {
INSTANCE = this;
}
/** injected via spring **/
public void setCourseLogExporter(ICourseLogExporter courseLogExporter) {
this.courseLogExporter = courseLogExporter;
}
/**
* @return Singleton.
*/
public static final ExportManager getInstance() {
if (INSTANCE==null) {
throw new IllegalStateException("ExportManager bean not created via spring. Configuration error!");
}
return INSTANCE;
}
/**
* Archives the course log files
* @param oresID
* @param exportDir
* @param begin
* @param end
* @param adminLog
* @param userLog
* @param statisticLog
* @param charset
* @param locale
* @param email
*/
public void archiveCourseLogFiles(Long oresID, String exportDir, Date begin, Date end, boolean adminLog, boolean userLog, boolean statisticLog, String charset, Locale locale, String email){
String zipName = ExportUtil.createFileNameWithTimeStamp(ExportManager.COURSE_LOG_FILES, "zip");
Date date = new Date();
String tmpDirName = oresID + "-" + date.getTime();
final VFSContainer tmpDirVFSContainer = new LocalFolderImpl(new File(WebappHelper.getTmpDir(), tmpDirName));
final File tmpDir = new File(WebappHelper.getTmpDir(), tmpDirName);
List<VFSItem> logFiles = new ArrayList<VFSItem>();
if (adminLog) {
logFiles.add(createLogFile(oresID, begin, end, charset, tmpDirVFSContainer, tmpDir, FILENAME_ADMIN_LOG, true, false));
}
if (userLog) {
logFiles.add(createLogFile(oresID, begin, end, charset, tmpDirVFSContainer, tmpDir, FILENAME_USER_LOG, false, false));
}
if (statisticLog) {
logFiles.add(createLogFile(oresID, begin, end, charset, tmpDirVFSContainer, tmpDir, FILENAME_STATISTIC_LOG, false, true));
}
saveFile(exportDir, zipName, tmpDirVFSContainer, logFiles, email, "email.archive", locale);
}
/**
* Create the actual log file.
* <p>
* Note: vfsContainer and dir must point to the very same directory. This is necessary to allow
* converting of the resulting file into a VFSLeaf (which is required by the zip util class)
* and to have the absolute path name of the vfsContainer for the sql export (there's no
* getter on the VFSContainer for the absolute path - this is core reason why).
* This 'hack' is not very nice but we'll live with it for the moment.
* <p>
* @param oresID
* @param begin
* @param end
* @param charset
* @param vfsContainer
* @param dir
* @param filename
* @param resourceAdminAction
* @param anonymize
* @return
*/
private VFSItem createLogFile(Long oresID, Date begin, Date end, String charset, VFSContainer vfsContainer, File dir, String filename, boolean resourceAdminAction, boolean anonymize) {
File outFile = new File(dir, filename);
// trigger the course log exporter - it will store the file to outFile
log_.info("createLogFile: start exporting course log file "+outFile.getAbsolutePath());
courseLogExporter.exportCourseLog(outFile, charset, oresID, begin, end, resourceAdminAction, anonymize);
log_.info("createLogFile: finished exporting course log file "+outFile.getAbsolutePath());
VFSItem logFile = vfsContainer.resolve(filename);
if (logFile==null) {
log_.warn("createLogFile: could not resolve "+filename);
}
return logFile;
}
private void saveFile(String targetDir, String fileName, VFSContainer tmpDir, List<VFSItem> files, String email, String emailI18nSubkey, Locale locale) {
File file = new File(targetDir, fileName);
VFSLeaf exportFile = new LocalFileImpl(file);
if (!ZipUtil.zip(files, exportFile, true)) {
// cleanup zip file
exportFile.delete();
} else {
// success
sendEMail(email, locale, emailI18nSubkey);
}
removeTemporaryFolder(tmpDir);
}
private void sendEMail(String email, Locale locale, String emailI18nSubkey) {
if(email == null || email.length() == 0) {
return;
}
if (locale == null) {
locale = I18nManager.getInstance().getCurrentThreadLocale();
}
Translator translator = Util.createPackageTranslator(ExportManager.class, locale);
try {
MailBundle bundle = new MailBundle();
bundle.setFrom(WebappHelper.getMailConfig("mailReplyTo"));
bundle.setTo(email);
bundle.setContent(translator.translate(emailI18nSubkey + ".subject"),
translator.translate(emailI18nSubkey+".body"));
CoreSpringFactory.getImpl(MailManager.class).sendMessage(bundle);
} catch (Exception e) {
log_.error("Error sending information email to user that file was saved successfully.", e);
}
}
/* private VFSItem createCourseStatisticFile(Long resourceableId, VFSContainer tmpDir, Date begin, Date end, List<ExtendedCondition> conditions, String groupByKey, boolean hasANDConnection, String charset, Locale locale) {
VFSLeaf statisticFile = tmpDir.createChildLeaf(FILENAME_COURSE_STATISTIC);
if(statisticFile == null) {
statisticFile = (VFSLeaf) tmpDir.resolve(FILENAME_COURSE_STATISTIC);
}
// Translator
Translator translator = new PackageTranslator(this.getClass().getPackage().getName(), locale);
IStatistic simpleCourseStatistic = new SimpleCourseStatistic(begin, end, conditions, groupByKey, hasANDConnection, translator, resourceableId);
FileUtils.save(statisticFile.getOutputStream(true), simpleCourseStatistic.getResult(), charset);
return statisticFile;
}
public void createCourseStatistic(Long resourceableId, String targetDir, Date begin, Date end, List<ExtendedCondition> conditions, String groupByKey, boolean hasANDConnection, String charset, Locale locale, String email) {
String zipName = ExportUtil.createFileNameWithTimeStamp(ExportManager.COURSE_STATISTIC, "zip");
Date date = new Date();
String tmpDirName = resourceableId + "-" + date.getTime();
VFSContainer tmpDir = new OlatRootFolderImpl(FolderConfig.getRelativeTmpDir() + File.separator + tmpDirName, null);
List<VFSItem> statisticFiles = new ArrayList<VFSItem>();
statisticFiles.add(createCourseStatisticFile(resourceableId, tmpDir, begin, end, conditions, groupByKey, hasANDConnection, charset, locale));
saveFile(targetDir, zipName, tmpDir, statisticFiles, email, "email.statistic", locale);
} */
public File getLatestCourseStatisticFile(String targetDir) {
File courseStatisticsDir = new File(targetDir);
File[] exportedCourseStatisticZipFiles = courseStatisticsDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith(ExportManager.COURSE_STATISTIC) && name.endsWith(".zip");
}
});
if (exportedCourseStatisticZipFiles==null || exportedCourseStatisticZipFiles.length==0) {
return null;
}
if (exportedCourseStatisticZipFiles.length==1) {
return exportedCourseStatisticZipFiles[0];
}
// we have more than one - return the newest
File newestFile = exportedCourseStatisticZipFiles[0];
for (int i = 0; i < exportedCourseStatisticZipFiles.length; i++) {
File file = exportedCourseStatisticZipFiles[i];
if (file.lastModified()>newestFile.lastModified()) {
newestFile = file;
}
}
return newestFile;
}
/**
* remove temporary folder
* @param tmpDir
*/
private void removeTemporaryFolder(VFSContainer tmpDir) {
tmpDir.delete();
}
}