Repository: incubator-eagle Updated Branches: refs/heads/master f430f521f -> 3082b5e62
[EAGLE-834] Improve and beautify JobReportTemplate * Improve and beautify JobReportTemplate.vm * Fix ratio as `double` instead of `String` http://issues.apache.org/jira/browse/EAGLE-834 Author: Hao Chen <h...@apache.org> Closes #745 from haoch/ImproveJobReportTmpl. Project: http://git-wip-us.apache.org/repos/asf/incubator-eagle/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-eagle/commit/3082b5e6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-eagle/tree/3082b5e6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-eagle/diff/3082b5e6 Branch: refs/heads/master Commit: 3082b5e6264eb0051a60e7929cc5a78390cb7ac4 Parents: f430f52 Author: Hao Chen <h...@apache.org> Authored: Thu Dec 15 15:38:53 2016 +0800 Committer: Zhao, Qingwen <qingwz...@apache.org> Committed: Thu Dec 15 15:38:53 2016 +0800 ---------------------------------------------------------------------- .../eagle/common/mail/AlertEmailConstants.java | 1 + .../eagle/common/mail/AlertEmailSender.java | 3 +- .../mr/history/MRHistoryJobDailyReporter.java | 9 +- .../src/main/resources/JobReportTemplate.vm | 587 +++++++++++++++---- .../src/main/resources/JobReportTemplatePure.vm | 141 +++++ .../history/MRHistoryJobDailyReporterTest.java | 10 +- .../src/test/resources/JobReportTemplate.vm | 141 ----- 7 files changed, 628 insertions(+), 264 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/3082b5e6/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailConstants.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailConstants.java b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailConstants.java index 140d306..4a90c45 100644 --- a/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailConstants.java +++ b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailConstants.java @@ -41,6 +41,7 @@ public class AlertEmailConstants { public static final String ALERT_EMAIL_COUNT_PROPERTY = "count"; public static final String ALERT_EMAIL_ALERTLIST_PROPERTY = "alertList"; public static final String ALERT_EMAIL_ORIGIN_PROPERTY = "alertEmailOrigin"; + public static final String VERSION = "version"; public static final String EAGLE_APPLICATION_EMAIL_SERVICE = "application.mailService"; public static final String EAGLE_COORDINATOR_EMAIL_SERVICE = "coordinator.mailService"; http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/3082b5e6/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailSender.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailSender.java b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailSender.java index ffc249d..7cf2ebd 100644 --- a/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailSender.java +++ b/eagle-core/eagle-common/src/main/java/org/apache/eagle/common/mail/AlertEmailSender.java @@ -17,6 +17,7 @@ package org.apache.eagle.common.mail; import org.apache.eagle.common.DateTimeUtil; +import org.apache.eagle.common.Version; import org.apache.velocity.VelocityContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,6 +107,6 @@ public class AlertEmailSender { context.put(AlertEmailConstants.ALERT_EMAIL_COUNT_PROPERTY, alertContexts.size()); context.put(AlertEmailConstants.ALERT_EMAIL_ALERTLIST_PROPERTY, alertContexts); context.put(AlertEmailConstants.ALERT_EMAIL_ORIGIN_PROPERTY, origin); + context.put(AlertEmailConstants.VERSION, Version.version); } - } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/3082b5e6/eagle-jpm/eagle-jpm-mr-history/src/main/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporter.java ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-mr-history/src/main/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporter.java b/eagle-jpm/eagle-jpm-mr-history/src/main/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporter.java index 42f2b29..6b38244 100644 --- a/eagle-jpm/eagle-jpm-mr-history/src/main/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporter.java +++ b/eagle-jpm/eagle-jpm-mr-history/src/main/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporter.java @@ -32,7 +32,6 @@ import org.apache.eagle.metadata.service.ApplicationEntityService; import org.apache.eagle.service.client.EagleServiceClientException; import org.apache.eagle.service.client.IEagleServiceClient; import org.apache.eagle.service.client.impl.EagleServiceClientImpl; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -176,7 +175,7 @@ public class MRHistoryJobDailyReporter extends AbstractScheduledService { data.putAll(buildFinishedJobInfo(site, startTime, endTime)); data.put(NUM_TOP_USERS_KEY, numTopUsers); data.put(JOB_OVERTIME_LIMIT_KEY, jobOvertimeLimit); - data.put(ALERT_TITLE_KEY, String.format("%s Daily Job Report", site.toUpperCase())); + data.put(ALERT_TITLE_KEY, String.format("[%s] Daily Job Report", site.toUpperCase())); data.put(REPORT_RANGE_KEY, String.format("%s ~ %s %s", DateTimeUtil.millisecondsToHumanDateWithSeconds(startTime), DateTimeUtil.millisecondsToHumanDateWithSeconds(endTime), @@ -238,7 +237,7 @@ public class MRHistoryJobDailyReporter extends AbstractScheduledService { JobSummeryInfo summeryInfo = new JobSummeryInfo(); summeryInfo.status = entry.getKey(); summeryInfo.numOfJobs = entry.getValue(); - summeryInfo.ratio = String.format("%.2f", entry.getValue() * 1d / totalJobs.get()); + summeryInfo.ratio = Double.parseDouble(String.format("%.2f", entry.getValue() * 1d / totalJobs.get())); statusCount.add(summeryInfo); } data.put(SUMMARY_INFO_KEY, statusCount); @@ -300,7 +299,7 @@ public class MRHistoryJobDailyReporter extends AbstractScheduledService { public static class JobSummeryInfo { public String status; public long numOfJobs; - public String ratio; + public double ratio; public String getStatus() { return status; @@ -310,7 +309,7 @@ public class MRHistoryJobDailyReporter extends AbstractScheduledService { return numOfJobs; } - public String getRatio() { + public double getRatio() { return ratio; } } http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/3082b5e6/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplate.vm ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplate.vm b/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplate.vm index fef83da..424306f 100644 --- a/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplate.vm +++ b/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplate.vm @@ -16,126 +16,491 @@ --> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> - #set ( $alert = $alertList[0] ) +#set ($alert = $alertList[0] ) +#set ($alertColor = "#337ab7") + +<html xmlns="http://www.w3.org/1999/xhtml" + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta name="viewport" content="width=device-width"/> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>$alert["alertTitle"]</title> + <style type="text/css"> + img { + max-width: 100%; + } + + body { + -webkit-font-smoothing: antialiased; + -webkit-text-size-adjust: none; + width: 100% !important; + height: 100%; + line-height: 1.6em; + } + + body { + background-color: #f6f6f6; + } + + @media only screen and (max-width: 640px) { + body { + padding: 0 !important; + } + + h1 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + + h2 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + + h3 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + + h4 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + + h1 { + font-size: 22px !important; + } + + h2 { + font-size: 18px !important; + } + + h3 { + font-size: 16px !important; + } + + .container { + padding: 0 !important; + width: 100% !important; + } + + .content { + padding: 0 !important; + } + + .content-wrap { + padding: 10px !important; + } + + .invoice { + width: 100% !important; + } + } + </style> </head> -<body> -<table class="body"> - <tr> - <td align="center" valign="top"> - <!-- Eagle Body --> - <table width="580"> - <tr> - <!-- Title --> - <td align="center"> - <h2>$alert["alertTitle"]</h2> - </td> - </tr> - - <!-- Basic Information --> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p><b>Summery ($alert["reportRange"])</b></p> - </td> - </tr> - <tr> - <td> - <table class="tableBordered" width="580"> - <tr> - <th>Metrics</th> - <th>Number of Jobs</th> - <th>Ratio</th> - </tr> - #foreach($item in $alert["summaryInfo"]) - <tr> - <td>$item.status</td> - <td>$item.numOfJobs</td> - <td>$item.ratio</td> + +<body itemscope itemtype="http://schema.org/EmailMessage" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" + bgcolor="#f6f6f6"> + +<table class="body-wrap" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" + bgcolor="#f6f6f6"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" + valign="top"></td> + <td class="container" width="600" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" + valign="top"> + <div class="content" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"> + <table class="main" width="100%" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;" + bgcolor="#fff"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="alert alert-warning" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: $alertColor; margin: 0; padding: 20px;" + align="center" bgcolor="" valign="top"> + <strong>$alert["alertTitle"]</strong> + </td> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-wrap" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" + valign="top"> + <table width="100%" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <th class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" + valign="top"> + Job Summary + </th> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" + valign="top"> + <i><strong>Time:</strong> $alert["reportRange"]</i> + </td> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block aligncenter" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; text-align: center; margin: 0; padding: 0 0 10px;" + align="center" valign="top"> + <table class="invoice" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: left; width: 100%; margin: 10px auto;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + <table class="invoice-items" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <th style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + Status + </th> + <th style="text-align: center;font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + Number of Jobs + </th> + <th style="text-align: center;font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + Ratio + </th> + </tr> + #foreach($item in $alert["summaryInfo"]) + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top"> + $item.status + </td> + <td style="text-align: center;font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top"> + $item.numOfJobs + </td> + <td style="text-align: center;font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top"> + #set ($ratio_percentage = $item.ratio * 100) + $ratio_percentage % + </td> + </tr> + #end + </table> + </td> + </tr> + </table> + </td> + </tr> + + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <th class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" + valign="top"> +## Top $alert["numTopUsers"] Users (Order by Number of Failed Jobs) + Top $alert["numTopUsers"] Failed Job Users + </th> </tr> - #end - </table> - </td> - </tr> - - <!-- Top Users for Failed Jobs --> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p><b>Top $alert["numTopUsers"] Users (Order by Number of Failed Jobs)</b></p> - </td> - </tr> - <tr> - <td> - <table class="tableBordered" width="580"> - <tr> - <th>Name</th> - <th>Number of Failed Jobs</th> - </tr> - #foreach($userItem in $alert["failedJobUsers"].entrySet()) - <tr> - <td>$userItem.key</td> - <td>$userItem.value</td> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block aligncenter" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; text-align: center; margin: 0; padding: 0 0 10px;" + align="center" valign="top"> + <table class="invoice" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: left; width: 100%; margin: 10px auto;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + <table class="invoice-items" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <th style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + User Name + </th> + <th style="text-align: center;font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + Number of Failed Jobs + </th> + </tr> + #foreach($userItem in $alert["failedJobUsers"].entrySet()) + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top"> + $userItem.key + </td> + <td style="text-align: center;font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top">$userItem.value + </td> + </tr> + #end + </table> + </td> + </tr> + </table> + </td> </tr> - #end - </table> - </td> - </tr> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p>View more job information on <a href="$alert["eagleJobLink"]">Eagle</a></p> - </td> - </tr> - - <!-- Top Users for Failed Jobs --> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p><b>Top $alert["numTopUsers"] Users (Order by Number of Succeeded Jobs Running over $alert["jobOvertimeLimit"] Hours)</b></p> - </td> - </tr> - <tr> - <td> - <table class="tableBordered" width="580"> - <tr> - <th>Name</th> - <th>Number of Jobs Runing over $alert["jobOvertimeLimit"] hrs</th> - </tr> - #foreach($userItem in $alert["succeededJobUsers"].entrySet()) - <tr> - <td>$userItem.key</td> - <td>$userItem.value</td> + + + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <th class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" + valign="top"> +## Top $alert["numTopUsers"] Users (Order by Number of Succeeded Jobs Running over $alert["jobOvertimeLimit"] Hours) + Top $alert["numTopUsers"] Succeeded Job Users (Over $alert["jobOvertimeLimit"] Hours ) + </th> </tr> - #end - </table> - </td> - </tr> - - <!-- Top Users for All Jobs --> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p><b>Top $alert["numTopUsers"] Users (Order by Number of Finished Jobs)</b></p> - </td> - </tr> - <tr> - <td> - <table class="tableBordered" width="580"> - <tr> - <th>Name</th> - <th>Number of Finished Jobs</th> - </tr> - #foreach($userItem in $alert["finishedJobUsers"].entrySet()) - <tr> - <td>$userItem.key</td> - <td>$userItem.value</td> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block aligncenter" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; text-align: center; margin: 0; padding: 0 0 10px;" + align="center" valign="top"> + <table class="invoice" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: left; width: 100%; margin: 10px auto;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + <table class="invoice-items" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <th style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + User Name + </th> + <th style="text-align: center;font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + Number of Succeeded Jobs +## Over $alert["jobOvertimeLimit"] Hours + </th> + </tr> + #foreach($userItem in $alert["succeededJobUsers"].entrySet()) + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top"> + $userItem.key + </td> + <td style="text-align: center;font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top">$userItem.value + </td> + </tr> + #end + </table> + </td> + </tr> + </table> + </td> </tr> - #end - </table> - </td> - </tr> - </table> + + + + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <th class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" + valign="top"> + Top $alert["numTopUsers"] Finished Job Users + </th> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block aligncenter" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; text-align: center; margin: 0; padding: 0 0 10px;" + align="center" valign="top"> + <table class="invoice" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: left; width: 100%; margin: 10px auto;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + <table class="invoice-items" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <th style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + User Name + </th> + <th style="text-align:center; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + Number of Finished Jobs + </th> + </tr> + #foreach($userItem in $alert["finishedJobUsers"].entrySet()) + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top"> + $userItem.key + </td> + <td style="text-align:center; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top"> + $userItem.value + </td> + </tr> + #end + </table> + </td> + </tr> + </table> + </td> + </tr> + + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" + valign="top"> + <a href="$alert["eagleJobLink"]" class="btn-primary" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: $alertColor; margin: 0; border-color: $alertColor; border-style: solid; border-width: 10px 20px;"> + View job details on Eagle + </a> + </td> + </tr> + </table> + </td> + </tr> + </table> + <div class="footer" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> + <table width="100%" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="aligncenter content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" + align="center" valign="top"> + Powered by <a href="http://eagle.incubator.apache.org" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">Apache + Eagle</a> (version: $version) + </td> + </tr> + </table> + </div> + </div> </td> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" + valign="top"></td> </tr> </table> </body> -</html> \ No newline at end of file +</html> + +##<body itemscope itemtype="http://schema.org/EmailMessage" +## style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" +## bgcolor="#f6f6f6"> +##<table class="body-wrap" +## style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" +## bgcolor="#f6f6f6"> +## <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> +## <td class="container" width="600" +## style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" +## valign="top"> +## <div class="content" +## style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"> +## <!-- Eagle Body --> +## <table class="invoice-items" cellpadding="0" cellspacing="0" +## style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; margin: 0;"> +## <tr> +## <!-- Title --> +## <td align="center"> +## <h2>$alert["alertTitle"]</h2> +## </td> +## </tr> +## <!-- Basic Information --> +## <tr> +## <td style="padding: 20px 0 10px 0;"> +## <p><b>Summery ($alert["reportRange"])</b></p> +## </td> +## </tr> +## <tr> +## <td> +## <table class="tableBordered" width="580"> +## <tr> +## <th>Metrics</th> +## <th>Number of Jobs</th> +## <th>Ratio</th> +## </tr> +## #foreach($item in $alert["summaryInfo"]) +## <tr> +## <td>$item.status</td> +## <td>$item.numOfJobs</td> +## <td>$item.ratio</td> +## </tr> +## #end +## </table> +## </td> +## </tr> +## +## <!-- Top Users for Failed Jobs --> +## <tr> +## <td style="padding: 20px 0 10px 0;"> +## <p><b>Top $alert["numTopUsers"] Users (Order by Number of Failed Jobs)</b></p> +## </td> +## </tr> +## <tr> +## <td> +## <table class="tableBordered" width="580"> +## <tr> +## <th>Name</th> +## <th>Number of Failed Jobs</th> +## </tr> +## #foreach($userItem in $alert["failedJobUsers"].entrySet()) +## <tr> +## <td>$userItem.key</td> +## <td>$userItem.value</td> +## </tr> +## #end +## </table> +## </td> +## </tr> +## <tr> +## <td style="padding: 20px 0 10px 0;"> +## <p>View more job information on <a href="$alert["eagleJobLink"]">Eagle</a></p> +## </td> +## </tr> +## +## <!-- Top Users for Failed Jobs --> +## <tr> +## <td style="padding: 20px 0 10px 0;"> +## <p><b>Top $alert["numTopUsers"] Users (Order by Number of Succeeded Jobs Running +## over $alert[ +## "jobOvertimeLimit"] Hours)</b></p> +## </td> +## </tr> +## <tr> +## <td> +## <table class="tableBordered" width="580"> +## <tr> +## <th>Name</th> +## <th>Number of Jobs Runing over $alert["jobOvertimeLimit"] hrs</th> +## </tr> +## #foreach($userItem in $alert["succeededJobUsers"].entrySet()) +## <tr> +## <td>$userItem.key</td> +## <td>$userItem.value</td> +## </tr> +## #end +## </table> +## </td> +## </tr> +## +## <!-- Top Users for All Jobs --> +## <tr> +## <td style="padding: 20px 0 10px 0;"> +## <p><b>Top $alert["numTopUsers"] Users (Order by Number of Finished Jobs)</b></p> +## </td> +## </tr> +## <tr> +## <td> +## <table class="tableBordered" width="580"> +## <tr> +## <th>Name</th> +## <th>Number of Finished Jobs</th> +## </tr> +## #foreach($userItem in $alert["finishedJobUsers"].entrySet()) +## <tr> +## <td>$userItem.key</td> +## <td>$userItem.value</td> +## </tr> +## #end +## </table> +## </td> +## </tr> +## </table> +## </div> +## </td> +## </tr> +##</table> +##</body> +##</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/3082b5e6/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplatePure.vm ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplatePure.vm b/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplatePure.vm new file mode 100644 index 0000000..fef83da --- /dev/null +++ b/eagle-jpm/eagle-jpm-mr-history/src/main/resources/JobReportTemplatePure.vm @@ -0,0 +1,141 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + #set ( $alert = $alertList[0] ) +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <meta name="viewport" content="width=device-width"/> + <title>$alert["alertTitle"]</title> +</head> +<body> +<table class="body"> + <tr> + <td align="center" valign="top"> + <!-- Eagle Body --> + <table width="580"> + <tr> + <!-- Title --> + <td align="center"> + <h2>$alert["alertTitle"]</h2> + </td> + </tr> + + <!-- Basic Information --> + <tr> + <td style="padding: 20px 0 10px 0;"> + <p><b>Summery ($alert["reportRange"])</b></p> + </td> + </tr> + <tr> + <td> + <table class="tableBordered" width="580"> + <tr> + <th>Metrics</th> + <th>Number of Jobs</th> + <th>Ratio</th> + </tr> + #foreach($item in $alert["summaryInfo"]) + <tr> + <td>$item.status</td> + <td>$item.numOfJobs</td> + <td>$item.ratio</td> + </tr> + #end + </table> + </td> + </tr> + + <!-- Top Users for Failed Jobs --> + <tr> + <td style="padding: 20px 0 10px 0;"> + <p><b>Top $alert["numTopUsers"] Users (Order by Number of Failed Jobs)</b></p> + </td> + </tr> + <tr> + <td> + <table class="tableBordered" width="580"> + <tr> + <th>Name</th> + <th>Number of Failed Jobs</th> + </tr> + #foreach($userItem in $alert["failedJobUsers"].entrySet()) + <tr> + <td>$userItem.key</td> + <td>$userItem.value</td> + </tr> + #end + </table> + </td> + </tr> + <tr> + <td style="padding: 20px 0 10px 0;"> + <p>View more job information on <a href="$alert["eagleJobLink"]">Eagle</a></p> + </td> + </tr> + + <!-- Top Users for Failed Jobs --> + <tr> + <td style="padding: 20px 0 10px 0;"> + <p><b>Top $alert["numTopUsers"] Users (Order by Number of Succeeded Jobs Running over $alert["jobOvertimeLimit"] Hours)</b></p> + </td> + </tr> + <tr> + <td> + <table class="tableBordered" width="580"> + <tr> + <th>Name</th> + <th>Number of Jobs Runing over $alert["jobOvertimeLimit"] hrs</th> + </tr> + #foreach($userItem in $alert["succeededJobUsers"].entrySet()) + <tr> + <td>$userItem.key</td> + <td>$userItem.value</td> + </tr> + #end + </table> + </td> + </tr> + + <!-- Top Users for All Jobs --> + <tr> + <td style="padding: 20px 0 10px 0;"> + <p><b>Top $alert["numTopUsers"] Users (Order by Number of Finished Jobs)</b></p> + </td> + </tr> + <tr> + <td> + <table class="tableBordered" width="580"> + <tr> + <th>Name</th> + <th>Number of Finished Jobs</th> + </tr> + #foreach($userItem in $alert["finishedJobUsers"].entrySet()) + <tr> + <td>$userItem.key</td> + <td>$userItem.value</td> + </tr> + #end + </table> + </td> + </tr> + </table> + </td> + </tr> +</table> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/3082b5e6/eagle-jpm/eagle-jpm-mr-history/src/test/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporterTest.java ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-mr-history/src/test/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporterTest.java b/eagle-jpm/eagle-jpm-mr-history/src/test/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporterTest.java index 13c3350..472d602 100644 --- a/eagle-jpm/eagle-jpm-mr-history/src/test/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporterTest.java +++ b/eagle-jpm/eagle-jpm-mr-history/src/test/java/org/apache/eagle/jpm/mr/history/MRHistoryJobDailyReporterTest.java @@ -69,11 +69,11 @@ public class MRHistoryJobDailyReporterTest { MRHistoryJobDailyReporter.JobSummeryInfo summeryInfo1 = new MRHistoryJobDailyReporter.JobSummeryInfo(); summeryInfo1.status = "failed"; summeryInfo1.numOfJobs = 10; - summeryInfo1.ratio = "0.1"; + summeryInfo1.ratio = 0.1; MRHistoryJobDailyReporter.JobSummeryInfo summeryInfo2 = new MRHistoryJobDailyReporter.JobSummeryInfo(); summeryInfo2.status = "succeeded"; summeryInfo2.numOfJobs = 90; - summeryInfo2.ratio = "0.9"; + summeryInfo2.ratio = 0.9; summeryInfos.add(summeryInfo1); summeryInfos.add(summeryInfo2); alertData.put(SUMMARY_INFO_KEY, summeryInfos); @@ -83,7 +83,6 @@ public class MRHistoryJobDailyReporterTest { failedJobUsers.put("bob", 97d); alertData.put(FAILED_JOB_USERS_KEY, failedJobUsers); - Map<String,Double> succeededJobUsers = new TreeMap<>(); succeededJobUsers.put("alice1", 100d); succeededJobUsers.put("bob1", 97d); @@ -95,15 +94,14 @@ public class MRHistoryJobDailyReporterTest { finishedJobUsers.put("bob2", 97d); alertData.put(FINISHED_JOB_USERS_KEY, finishedJobUsers); - alertData.put(ALERT_TITLE_KEY, "Daily Job Report"); + alertData.put(ALERT_TITLE_KEY, "[TEST_CLUSTER] Daily Job Report"); alertData.put(NUM_TOP_USERS_KEY, 2); alertData.put(JOB_OVERTIME_LIMIT_KEY, 6); long currentTimestamp = System.currentTimeMillis(); alertData.put(REPORT_RANGE_KEY, String.format(" %s ~ %s %s", DateTimeUtil.millisecondsToHumanDateWithSeconds(currentTimestamp - 6 * DateTimeUtil.ONEHOUR), DateTimeUtil.millisecondsToHumanDateWithSeconds(currentTimestamp), DateTimeUtil.CURRENT_TIME_ZONE.getID())); - alertData.put("joblink", "http://localhost:9090"); + alertData.put(EAGLE_JOB_LINK_KEY, "http://localhost:9090/#/site/sandbox/jpm/statistics"); return alertData; } - } http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/3082b5e6/eagle-jpm/eagle-jpm-mr-history/src/test/resources/JobReportTemplate.vm ---------------------------------------------------------------------- diff --git a/eagle-jpm/eagle-jpm-mr-history/src/test/resources/JobReportTemplate.vm b/eagle-jpm/eagle-jpm-mr-history/src/test/resources/JobReportTemplate.vm deleted file mode 100644 index fef83da..0000000 --- a/eagle-jpm/eagle-jpm-mr-history/src/test/resources/JobReportTemplate.vm +++ /dev/null @@ -1,141 +0,0 @@ -<!-- - ~ Licensed to the Apache Software Foundation (ASF) under one or more - ~ contributor license agreements. See the NOTICE file distributed with - ~ this work for additional information regarding copyright ownership. - ~ The ASF licenses this file to You under the Apache License, Version 2.0 - ~ (the "License"); you may not use this file except in compliance with - ~ the License. You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> - #set ( $alert = $alertList[0] ) -<head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> - <meta name="viewport" content="width=device-width"/> - <title>$alert["alertTitle"]</title> -</head> -<body> -<table class="body"> - <tr> - <td align="center" valign="top"> - <!-- Eagle Body --> - <table width="580"> - <tr> - <!-- Title --> - <td align="center"> - <h2>$alert["alertTitle"]</h2> - </td> - </tr> - - <!-- Basic Information --> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p><b>Summery ($alert["reportRange"])</b></p> - </td> - </tr> - <tr> - <td> - <table class="tableBordered" width="580"> - <tr> - <th>Metrics</th> - <th>Number of Jobs</th> - <th>Ratio</th> - </tr> - #foreach($item in $alert["summaryInfo"]) - <tr> - <td>$item.status</td> - <td>$item.numOfJobs</td> - <td>$item.ratio</td> - </tr> - #end - </table> - </td> - </tr> - - <!-- Top Users for Failed Jobs --> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p><b>Top $alert["numTopUsers"] Users (Order by Number of Failed Jobs)</b></p> - </td> - </tr> - <tr> - <td> - <table class="tableBordered" width="580"> - <tr> - <th>Name</th> - <th>Number of Failed Jobs</th> - </tr> - #foreach($userItem in $alert["failedJobUsers"].entrySet()) - <tr> - <td>$userItem.key</td> - <td>$userItem.value</td> - </tr> - #end - </table> - </td> - </tr> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p>View more job information on <a href="$alert["eagleJobLink"]">Eagle</a></p> - </td> - </tr> - - <!-- Top Users for Failed Jobs --> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p><b>Top $alert["numTopUsers"] Users (Order by Number of Succeeded Jobs Running over $alert["jobOvertimeLimit"] Hours)</b></p> - </td> - </tr> - <tr> - <td> - <table class="tableBordered" width="580"> - <tr> - <th>Name</th> - <th>Number of Jobs Runing over $alert["jobOvertimeLimit"] hrs</th> - </tr> - #foreach($userItem in $alert["succeededJobUsers"].entrySet()) - <tr> - <td>$userItem.key</td> - <td>$userItem.value</td> - </tr> - #end - </table> - </td> - </tr> - - <!-- Top Users for All Jobs --> - <tr> - <td style="padding: 20px 0 10px 0;"> - <p><b>Top $alert["numTopUsers"] Users (Order by Number of Finished Jobs)</b></p> - </td> - </tr> - <tr> - <td> - <table class="tableBordered" width="580"> - <tr> - <th>Name</th> - <th>Number of Finished Jobs</th> - </tr> - #foreach($userItem in $alert["finishedJobUsers"].entrySet()) - <tr> - <td>$userItem.key</td> - <td>$userItem.value</td> - </tr> - #end - </table> - </td> - </tr> - </table> - </td> - </tr> -</table> -</body> -</html> \ No newline at end of file