package com.anjlab.ping.filters; import static com.google.appengine.api.datastore.KeyFactory.stringToKey; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.TimeZone; import javax.mail.MessagingException; import javax.mail.internet.MimeBodyPart; import javax.persistence.EntityTransaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.anjlab.ping.entities.Job; import com.anjlab.ping.entities.JobResult; import com.anjlab.ping.pages.job.EditJob; import com.anjlab.ping.services.Application; import com.anjlab.ping.services.JobResultCSVExporter; import com.anjlab.ping.services.JobResultsAnalyzer; import com.anjlab.ping.services.Mailer; import com.anjlab.ping.services.Utils; import com.google.appengine.api.datastore.Key; public class BackupJobResultsFilter extends AbstractFilter { private static final Logger logger = LoggerFactory.getLogger(BackupJobResultsFilter.class); @Override protected void processRequest(EntityTransaction tx) throws Exception { String encodedJobKey = globals.getHTTPServletRequest().getParameter(RunJobFilter.JOB_KEY_PARAMETER_NAME); if (Utils.isNullOrEmpty(encodedJobKey)) { return; } Key key = stringToKey(encodedJobKey); logger.debug("Running mail job: {}", key); Job job = jobDAO.find(key); if (job == null) { return; } List<JobResult> resultsBuffer = new ArrayList<JobResult>(Application.DEFAULT_NUMBER_OF_JOB_RESULTS); resultsBuffer.addAll(job.removeJobResultsExceptRecent(Application.DEFAULT_NUMBER_OF_JOB_RESULTS)); job.setLastBackupTimestamp(new Date()); if (resultsBuffer.size() > 0) { if (application.updateJob(job, false, true)) { if (job.isReceiveBackups() && !Utils.isNullOrEmpty(job.getReportEmail())) { sendResultsByMail(job, resultsBuffer, job.getReportEmail()); } } else { logger.error("Error saving job. Backup will not be sent to user this time."); } } } public void sendResultsByMail(Job job, List<JobResult> results, String reportRecipient) throws MessagingException, IOException, URISyntaxException { JobResult firstResult = (JobResult) results.get(0); JobResult lastResult = (JobResult) results.get(results.size() - 1); TimeZone timeZone = Application.UTC_TIME_ZONE; // TODO Use job specific time zone JobResultsAnalyzer analyzer = new JobResultsAnalyzer(results, true); StringBuilder report = analyzer.buildHtmlReport(timeZone); String subject = "Data Backup for " + job.getTitleFriendly() + ": " + analyzer.getAvailabilitySummary(); StringBuilder builder = new StringBuilder(); builder.append("For period: "); builder.append(Application.formatDate(Application.DATETIME_FORMAT, timeZone, firstResult.getTimestamp())); builder.append(" ("); builder.append(Utils.getTimeAgoUpToDays(firstResult.getTimestamp())); builder.append(") – "); builder.append(Application.formatDate(Application.DATETIME_FORMAT, timeZone, lastResult.getTimestamp())); builder.append(" ("); builder.append(Utils.getTimeAgoUpToDays(lastResult.getTimestamp())); builder.append(") = "); builder.append(Utils.formatMillisecondsToWordsUpToMinutes(lastResult.getTimestamp().getTime() - firstResult.getTimestamp().getTime())); builder.append("<br/># of records: "); builder.append(results.size()); builder.append("<br/>Time Zone: "); builder.append(timeZone.getDisplayName()); builder.append(" ("); builder.append(timeZone.getID()); builder.append(")"); builder.append(report); builder.append("<br/><br/>----"); builder.append("<br/>You can disable receiving statistics backups for the job here: "); String editJobLink = application.getJobUrl(job, EditJob.class); builder.append(editJobLink); builder.append("<br/><br/>Note:"); builder.append("<br/>Automatic Backups is a beta function, please use our <a href='http://ping-service.appspot.com/feedback'>feedback form</a> to provide a feedback on it."); builder.append("<br/>You will get approximately one email per week per job depending on job's cron string."); builder.append("<br/>Once you received an email with the statistics, this data will be deleted from Ping Service database."); builder.append("<br/>Ping Service will only store "); builder.append(Application.DEFAULT_NUMBER_OF_JOB_RESULTS); builder.append(" latest ping results per job."); builder.append("<br/>We're doing this to keep Ping Service free, since we're running out of free quota limit of Google App Engine infrastructure."); builder.append("<br/>We're sorry for any inconvenience you might get from this email."); builder.append("<br/>Thank you for understanding."); String message = builder.toString(); byte[] export = JobResultCSVExporter.export(timeZone, results); MimeBodyPart attachment = new MimeBodyPart(); attachment.setContent(new String(export), "text/csv"); // Set Content-Type explicitly since GAE ignores type passed to setContent(...) method attachment.setHeader("Content-Type", "text/csv"); attachment.setFileName( "job-" + job.getKey().getId() + "-results-" + Application.formatDateForFileName(firstResult.getTimestamp(), timeZone) + "-" + Application.formatDateForFileName(lastResult.getTimestamp(), timeZone) + ".csv"); application.getMailer().sendMail2("text/html", Mailer.PING_SERVICE_NOTIFY_GMAIL_COM, reportRecipient, subject, message, attachment); } }