package model.manager.exports;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.List;
import model.manager.excel.conversion.exceptions.ReportException;
import model.nonPersistent.EntitySet;
import org.apache.log4j.Logger;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.MethodExceptionEventHandler;
import org.celllife.idart.commonobjects.iDartProperties;
import org.eclipse.core.runtime.IProgressMonitor;
import org.hibernate.Session;
public final class DataExporter {
private int PAGE_SIZE = 100;
private final Session session;
private static Logger log = Logger.getLogger(DataExporter.class);
private IProgressMonitor monitor;
public DataExporter(Session session) {
this.session = session;
}
public DataExporter(Session session, IProgressMonitor monitor) {
super();
this.monitor = monitor;
this.session = session;
}
public void setPageSize(int pageSize) {
PAGE_SIZE = pageSize;
}
/**
* Generates a data export file given a data export (columns) and patient
* set (rows).
*
* @param sess
* Hibernate Session
* @param dataExport
* DataExportObject to export
* @param patientSet
* set of patients to export data for or null if all patients
* @param separator
* column separator character to use
* @param subProgressMonitor
* @return
* @throws Exception
*/
public File generateExport(ReportObject dataExport,
String outputFileName) throws ReportException {
// Set up functions used in the report ( $!{fn:...} )
DataExportFunctions functions = new DataExportFunctions();
functions.setSeparator(',');
return generateExport(dataExport, functions, outputFileName);
}
/**
* Generates a data export file given a data export (columns) and patient
* set (rows).
*
* @param sess
* Hibernate Session
* @param dataExport
* DataExportObject to export
* @param patientSet
* set of patients to export data for or null if all patients
* @param separator
* column separator character to use
* @param subProgressMonitor
* @throws Exception
*/
public void generateExport(ReportObject dataExport, char separator,
String outputFileName) throws ReportException {
// Set up functions used in the report ( $!{fn:...} )
DataExportFunctions functions = new DataExportFunctions();
functions.setSeparator(separator);
generateExport(dataExport, functions, outputFileName);
}
/**
*
* @param sess
* @param dataExport
* @param functions
* @throws ReportException
* if any errors occur.
*/
public File generateExport(ReportObject dataExport,
DataExportFunctions functions, String outputFileName)
throws ReportException {
functions.setSession(session);
// defining log file here to attempt to reduce memory consumption
VelocityEngine velocityEngine = new VelocityEngine();
try {
velocityEngine.init();
} catch (Exception e) {
log.error("Error initializing Velocity engine", e);
}
// Check if file has .csv extension
if (!outputFileName.endsWith(".csv")) {
outputFileName += ".csv";
}
File exportFile = new File(outputFileName);
PrintWriter report;
try {
report = new PrintWriter(exportFile);
} catch (FileNotFoundException e1) {
System.out.println(" TEST " + e1.getMessage());
throw new ReportException("Unable to open file for exporting data.");
}
EntitySet entitySet = generatePatientSet(dataExport);
int page = 0;
int total = entitySet.size();
int pages = total / PAGE_SIZE;
if (monitor != null)
monitor.beginTask("Exporting data", total);
try {
while (!isCancelled()) {
// Set up list of patients if one wasn't passed into this method
EntitySet pagedEntitySet = entitySet.getPage(page
* PAGE_SIZE, PAGE_SIZE);
if (pagedEntitySet.size() == 0) {
break;
}
log.debug("Starting data export page " + page + " of " + pages);
updateMonitorMessage("Processed " + page * PAGE_SIZE
+ " patients of " + total);
VelocityContext velocityContext = new VelocityContext();
// add the error handler
EventCartridge ec = new EventCartridge();
ec.addEventHandler(new VelocityExceptionHandler());
velocityContext.attachEventCartridge(ec);
functions.setAllPatients(false);
// Set up velocity utils
velocityContext.put("fn", functions);
velocityContext.put("patientSet", pagedEntitySet);
String template;
if (page == 0) {
template = dataExport.generateTemplate();
} else {
template = dataExport.generateDataTemplate();
}
if (log.isDebugEnabled()) {
log.debug("Template: "
+ template.substring(0,
template.length() < 3500 ? template
.length() : 3500) + "...");
}
try {
velocityEngine.evaluate(velocityContext, report,
DataExporter.class.getName(), template);
} catch (Exception e) {
log.error("Error evaluating data export "
+ dataExport.getReportObjectId(), e);
log.error("Template: "
+ template.substring(0,
template.length() < 3500 ? template
.length() : 3500) + "...");
report.print("\n\nError exporting data");
throw new ReportException("Error running data export.", e);
} finally {
log.debug("Completed data export page " + page + " of "
+ pages);
updateMonitorStatus(PAGE_SIZE);
page++;
report.flush();
velocityContext.remove("fn");
velocityContext.remove("patientSet");
velocityContext = null;
// reset the ParserPool to something else now?
// using this to get to RuntimeInstance.init();
try {
velocityEngine.init();
} catch (Exception e) {
// do nothing
}
pagedEntitySet = null;
functions.clear();
template = null;
log.debug("Clearing hibernate session");
session.clear();
// clear out the excess objects
System.gc();
System.gc();
}
}
} catch (ReportException e) {
throw e;
} finally {
report.close();
entitySet = null;
dataExport = null;
velocityEngine = null;
session.close();
}
return exportFile;
}
private void updateMonitorStatus(int work) {
if (monitor != null) {
monitor.worked(work);
}
}
private void updateMonitorMessage(String message) {
if (monitor != null) {
monitor.subTask(message);
}
}
private boolean isCancelled() {
if (monitor != null)
return monitor.isCanceled();
return false;
}
/**
* Generate the patientSet according to this report's characteristics
*
* @return patientSet to be used with report template
*/
@SuppressWarnings("unchecked")
private EntitySet generatePatientSet(ReportObject dataExport) {
EntitySet patientSet = dataExport.getPatientSet();
if (patientSet == null || patientSet.size() == 0) {
List<Integer> ids = session.createQuery("select id from Patient")
.list();
patientSet = new EntitySet();
patientSet.setEntityIds(ids);
dataExport.setAllPatients(false);
}
return patientSet;
}
/**
* Returns the path and name of the generated file
*
* @param dataExport
* @return
*/
public File getGeneratedFile(ReportObject dataExport) {
File dir = new File(iDartProperties.exportDir);
dir.mkdirs();
String filename = dataExport.getName().replace(" ", "_");
File file = new File(dir, filename);
return file;
}
/**
* Private class used for velocity error masking
*/
public static class VelocityExceptionHandler implements
MethodExceptionEventHandler {
/**
* When a user-supplied method throws an exception, the
* MethodExceptionEventHandler is invoked with the Class, method name
* and thrown Exception. The handler can either return a valid Object to
* be used as the return value of the method call, or throw the
* passed-in or new Exception, which will be wrapped and propogated to
* the user as a MethodInvocationException
*
* @see org.apache.velocity.app.event.MethodExceptionEventHandler#methodException(java.lang.Class,
* java.lang.String, java.lang.Exception)
*/
@SuppressWarnings("rawtypes")
@Override
public Object methodException(Class claz, String method, Exception e)
throws Exception {
log.debug("Claz: " + claz.getName() + " method: " + method, e);
// if formatting a date (and probably getting an
// "IllegalArguementException")
if ("format".equals(method))
return null;
// keep the default behaviour
throw e;
}
}
}