package org.activityinfo.server.digest.activity;
import net.lightoze.gwt.i18n.server.LocaleProxy;
import org.activityinfo.i18n.shared.I18N;
import org.activityinfo.server.database.hibernate.entity.SiteHistory;
import org.activityinfo.server.digest.DigestModel;
import org.activityinfo.server.digest.DigestRenderer;
import org.activityinfo.server.digest.activity.ActivityDigestModel.ActivityMap;
import org.activityinfo.server.digest.activity.ActivityDigestModel.DatabaseModel;
import org.activityinfo.server.digest.activity.ActivityDigestModel.PartnerActivityModel;
import org.activityinfo.server.util.date.DateCalc;
import org.ocpsoft.prettytime.PrettyTime;
import java.io.IOException;
import java.util.*;
public class ActivityDigestRenderer implements DigestRenderer {
private static final int HEADERCELL_WIDTH = 230;
private static final int SPACERCELL_WIDTH = 20;
private static final int GRAPHCELL_WIDTH = 21;
private static final int ROW_HEIGHT = 21;
private static final boolean RENDER_INACTIVE_ROWS = false;
private static final int IA_DBS_HEADERCELL_WIDTH = 240;
private static final int IA_DBS_CONTENTCELL_WIDTH = 400;
@Override
public String renderHtml(DigestModel model) throws IOException {
assert (model instanceof ActivityDigestModel);
StringBuilder html = new StringBuilder();
html.append("<div id='act-digest' style='margin-top:20px'>");
renderHeader(html, (ActivityDigestModel) model);
renderActiveDatabases(html, (ActivityDigestModel) model);
renderInactiveDatabases(html, (ActivityDigestModel) model);
html.append("</div>");
return html.toString();
}
private void renderHeader(StringBuilder html, ActivityDigestModel model) {
html.append("<div class='act-header'>");
html.append(I18N.MESSAGES.activityDigestIntro(model.getUserDigest().getDays()));
html.append("<br/>");
html.append(I18N.MESSAGES.digestUnsubscribe(model.getUserDigest().getUnsubscribeLink()));
html.append("</div>");
}
private void renderActiveDatabases(StringBuilder html, ActivityDigestModel model) throws IOException {
html.append("<div class='act-active'>");
html.append("<table class='act-data' border='0' cellpadding='0' cellspacing='0' style='width:");
html.append(HEADERCELL_WIDTH + SPACERCELL_WIDTH + (model.getUserDigest().getDays() * GRAPHCELL_WIDTH));
html.append("px; border-collapse:collapse;'>");
renderTableHeader(html, model);
Collection<DatabaseModel> activeDatabases = model.getActiveDatabases();
for (DatabaseModel activeDatabase : activeDatabases) {
renderActiveDatabase(html, activeDatabase);
}
html.append("</table>");
html.append("</div>");
}
private void renderTableHeader(StringBuilder html, ActivityDigestModel model) {
html.append("<tr>");
html.append("<th style='width:").append(HEADERCELL_WIDTH).append("px;'> </th>");
html.append("<th style='width:").append(SPACERCELL_WIDTH).append("px;'> </th>");
for (int i = 0; i < model.getUserDigest().getDays(); i++) {
html.append("<th style='width:").append(GRAPHCELL_WIDTH).append("px;'> </th>");
}
html.append("</tr>");
}
private void renderActiveDatabase(StringBuilder html, DatabaseModel activeDatabase) {
renderDatabaseRow(html, 2 + activeDatabase.getModel().getUserDigest().getDays(), activeDatabase.getName());
renderUserRow(html, activeDatabase.getOwnerActivityMap());
for (PartnerActivityModel partnerModel : activeDatabase.getPartnerActivityModels()) {
renderPartnerRow(html, partnerModel);
for (ActivityMap activityMap : partnerModel.getActivityMaps()) {
renderUserRow(html, activityMap);
}
}
}
private void renderDatabaseRow(StringBuilder html, int colspan, String name) {
html.append("<tr>");
html.append("<td class='act-database-header' colspan='");
html.append(colspan);
html.append("' style='color: #67a639; font-weight:bold; padding-top: 13px'>");
html.append(name);
html.append("</td>");
html.append("</tr>");
}
private void renderPartnerRow(StringBuilder html, PartnerActivityModel partnerModel) {
if (partnerModel.hasActivity() || RENDER_INACTIVE_ROWS) {
// spacer
html.append("<tr><td class='act-partner-spacer' colspan='");
html.append(2 + partnerModel.getDatabaseModel().getModel().getUserDigest().getDays());
html.append("' style='height:8px'>");
html.append("</td></tr>");
// partner row
html.append("<tr>");
html.append("<td class='act-partner-header' style='padding-left: 12px; color: black; font-weight:bold;'>");
html.append(partnerModel.getPartner().getName());
html.append("</td>");
html.append("<td> </td>");
renderGraph(html, partnerModel.getDatabaseModel(), partnerModel.getTotalActivityMap());
html.append("</tr>");
}
}
private void renderUserRow(StringBuilder html, ActivityMap activityMap) {
if (activityMap.hasActivity() || RENDER_INACTIVE_ROWS) {
html.append("<tr class='act-user-row' style='height:").append(ROW_HEIGHT).append("px;'>");
html.append("<td class='act-user-header' style='padding-left: 25px;'>");
html.append("<a href=\"mailto:").append(activityMap.getUser().getEmail()).append("\">");
html.append(activityMap.getUser().getName());
html.append("</a>");
html.append("</td>");
html.append("<td> </td>");
renderGraph(html, activityMap.getDatabaseModel(), activityMap.getMap());
html.append("</tr>");
}
}
private void renderGraph(StringBuilder html, DatabaseModel databaseModel, Map<Integer, Integer> activityMap) {
List<Integer> list = new ArrayList<Integer>(activityMap.values());
Collections.reverse(list);
for (int i = 0; i < list.size(); i++) {
int value = list.get(i);
html.append("<td class='act-graphcell' style='");
html.append("width:").append(GRAPHCELL_WIDTH).append("px; ");
html.append("height:").append(ROW_HEIGHT).append("px; ");
html.append("border: 1px solid black; ");
html.append("background-color:#").append(determineColor(value)).append("; ");
html.append("text-align:center; vertical-align:middle' ");
html.append("title='").append(determineTitle(databaseModel, value, i)).append("'>");
html.append((value != 0) ? value : " ");
html.append("</td>");
}
}
private String determineColor(int value) {
String R = "85";
String B = "55";
String background = "f0f0f0";
int maxbound = 15;
int min = 0xb0;
int max = 0xff;
if (value <= 0) {
return background;
}
if (value > maxbound) {
return R + Integer.toHexString(max) + B;
}
int range = max - min;
int step = range / maxbound;
return R + Integer.toHexString(min + value * step) + B;
}
private String determineTitle(DatabaseModel databaseModel, int updates, int dayIndex) {
Date today = databaseModel.getModel().getUserDigest().getDate();
int totalDays = databaseModel.getModel().getUserDigest().getDays();
Date date = DateCalc.daysAgo(today, (totalDays - dayIndex - 1));
return I18N.MESSAGES.activityDigestGraphTooltip(updates, date);
}
private void renderInactiveDatabases(StringBuilder html, ActivityDigestModel model) {
Collection<DatabaseModel> inactiveDatabases = model.getInactiveDatabases();
if (!inactiveDatabases.isEmpty()) {
html.append("<div class='act-inactive' style='margin-top:35px;'>");
html.append(I18N.MESSAGES.activityDigestInactiveDatabases(model.getUserDigest().getDays()));
html.append("<br>");
html.append("<table class='act-data' border='0' cellpadding='0' cellspacing='0' ");
html.append("style='width:").append(IA_DBS_HEADERCELL_WIDTH + IA_DBS_CONTENTCELL_WIDTH).append("px; ");
html.append("border-collapse:collapse;'>");
html.append("<tr>");
html.append("<th style='width:").append(IA_DBS_HEADERCELL_WIDTH).append("px;'> </th>");
html.append("<th style='width:").append(IA_DBS_CONTENTCELL_WIDTH).append("px;'> </th>");
html.append("</tr>");
for (DatabaseModel db : inactiveDatabases) {
html.append("<tr>");
html.append("<td class='act-database-header' style='color: #67a639; font-weight:bold'>");
html.append(db.getName());
html.append("</td>");
html.append("<td class='act-lastedit' style='font-style: italic'>");
html.append(formatLastEdited(db));
html.append("</td>");
html.append("</tr>");
}
html.append("</table>");
html.append("</div>");
}
}
private String formatLastEdited(DatabaseModel db) {
SiteHistory lastEdit = db.getLastEdit();
if (lastEdit == null) {
return I18N.CONSTANTS.lastEditUnknown();
} else {
return I18N.CONSTANTS.lastEdit() + " " +
new PrettyTime(LocaleProxy.getLocale()).format(new Date(lastEdit.getTimeCreated()));
// return DateCalc.daysBeforeMidnight(new Date(), lastEdit.getTimeCreated()) + " day(s) ago";
}
}
}