package org.activityinfo.server.digest.geo; import com.google.common.annotations.VisibleForTesting; import com.google.inject.Inject; import com.google.inject.Provider; import org.activityinfo.i18n.shared.I18N; import org.activityinfo.legacy.shared.command.GetSites; import org.activityinfo.legacy.shared.command.result.SiteResult; import org.activityinfo.legacy.shared.model.ActivityDTO; import org.activityinfo.legacy.shared.model.ActivityFormDTO; import org.activityinfo.legacy.shared.model.SiteDTO; import org.activityinfo.legacy.shared.reports.content.BubbleMapMarker; import org.activityinfo.legacy.shared.reports.content.MapMarker; import org.activityinfo.server.command.DispatcherSync; 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.geo.GeoDigestModel.DatabaseModel; import org.activityinfo.server.util.date.DateCalc; import javax.persistence.EntityManager; import javax.persistence.Query; import java.io.IOException; import java.util.*; import java.util.logging.Logger; public class GeoDigestRenderer implements DigestRenderer { private static final String BUBBLE_COLOR = "67a639"; private static final Logger LOGGER = Logger.getLogger(GeoDigestRenderer.class.getName()); private final Provider<EntityManager> entityManager; private final DispatcherSync dispatcher; @Inject public GeoDigestRenderer(Provider<EntityManager> entityManager, DispatcherSync dispatcher) { this.entityManager = entityManager; this.dispatcher = dispatcher; } @Override public String renderHtml(DigestModel model) throws IOException { assert (model instanceof GeoDigestModel); StringBuilder html = new StringBuilder(); html.append("<div id='geo-digest' style='margin-top:20px'>"); renderHeader(html, (GeoDigestModel) model); renderDatabases(html, (GeoDigestModel) model); html.append("</div>"); return html.toString(); } private void renderHeader(StringBuilder html, GeoDigestModel model) { html.append("<div class='geo-header'>"); html.append(I18N.MESSAGES.geoDigestIntro(model.getUserDigest().getDays() * 24)); html.append("<br/>"); html.append(I18N.MESSAGES.digestUnsubscribe(model.getUserDigest().getUnsubscribeLink())); html.append("</div>"); } private void renderDatabases(StringBuilder html, GeoDigestModel model) throws IOException { html.append("<div class='geo-data' style='margin-top:20px'>"); Collection<DatabaseModel> databases = model.getDatabases(); for (DatabaseModel database : databases) { if (database.isRenderable()) { renderDatabase(html, database); } } html.append("</div>"); } private void renderDatabase(StringBuilder html, DatabaseModel databaseModel) throws IOException { html.append("<div class='geo-database' style='margin-top:20px'>"); html.append("<span class='geo-header' style='font-weight:bold; color: #" + BUBBLE_COLOR + ";'>"); html.append(databaseModel.getName()); html.append("</span><br>"); html.append("<img class='geo-graph' width=\"450px\" src=\""); html.append(databaseModel.getUrl()); html.append("\" /><br><br>"); for (MapMarker marker : databaseModel.getContent().getMarkers()) { String label = ((BubbleMapMarker) marker).getLabel(); html.append("<span class='geo-marker-header' style='color: #" + BUBBLE_COLOR + "; font-weight:bold;'>"); html.append(label); html.append(":</span><br>"); LOGGER.finest(marker.getSiteIds().size() + " sites for marker " + label + ": " + marker.getSiteIds()); renderSites(html, databaseModel, marker.getSiteIds()); } if (!databaseModel.getContent().getUnmappedSites().isEmpty()) { html.append("<br><span class='geo-unmapped-header' style='color:black; font-weight:bold;'>"); html.append(I18N.MESSAGES.geoDigestUnmappedSites()); html.append(":</span><br>"); LOGGER.finest(databaseModel.getContent().getUnmappedSites().size() + " unmapped sites"); renderSites(html, databaseModel, databaseModel.getContent().getUnmappedSites()); } html.append("</div>"); } private void renderSites(StringBuilder html, DatabaseModel databaseModel, Collection<Integer> siteIds) { if (!siteIds.isEmpty()) { for (Integer siteId : siteIds) { SiteResult siteResult = dispatcher.execute(GetSites.byId(siteId)); SiteDTO siteDTO = siteResult.getData().get(0); ActivityDTO activityDTO = databaseModel.getModel() .getSchemaDTO() .getActivityById(siteDTO.getActivityId()); List<SiteHistory> histories = findSiteHistory(siteId, databaseModel.getModel().getUserDigest().getFrom()); for (SiteHistory history : histories) { html.append("<span class='geo-site' style='margin-left:10px;'>• "); html.append(I18N.MESSAGES.geoDigestSiteMsg(history.getUser().getEmail(), history.getUser().getName(), activityDTO.getName(), siteDTO.getLocationName())); Date targetDate = databaseModel.getModel().getUserDigest().getDate(); Date creationDate = new Date(history.getTimeCreated()); if (DateCalc.isOnToday(targetDate, creationDate)) { html.append(I18N.MESSAGES.geoDigestSiteMsgDateToday(creationDate)); } else if (DateCalc.isOnYesterday(targetDate, creationDate)) { html.append(I18N.MESSAGES.geoDigestSiteMsgDateYesterday(creationDate)); } else { html.append(I18N.MESSAGES.geoDigestSiteMsgDateOther(creationDate)); } html.append("</span><br>"); } } } } /** * @param siteId * @param from * @param from * @return the sitehistory edited since the specified timestamp (milliseconds) and linked to the specified database * and user. The resulting list is grouped by user, keeping the last created sitehistory entry per user. */ @VisibleForTesting @SuppressWarnings("unchecked") List<SiteHistory> findSiteHistory(Integer siteId, long from) { Query query = entityManager.get().createQuery("select distinct h from SiteHistory h " + "where h.site.id = :siteId and h.timeCreated >= :from " + "order by h.timeCreated"); query.setParameter("siteId", siteId); query.setParameter("from", from); List<SiteHistory> list = query.getResultList(); if (list.isEmpty()) { return list; } Map<Integer, SiteHistory> map = new HashMap<Integer, SiteHistory>(); for (SiteHistory siteHistory : list) { SiteHistory old = map.get(siteHistory.getUser().getId()); if (old == null || old.getTimeCreated() <= siteHistory.getTimeCreated()) { map.put(siteHistory.getUser().getId(), siteHistory); } } return new ArrayList<SiteHistory>(map.values()); } }