ActionNotificationsBase.java
/*
* Copyright 2024 Global Crop Diversity Trust
*
* Licensed 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.
*/
package org.gringlobal.notification;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.gringlobal.model.AbstractAction;
import org.gringlobal.model.Cooperator;
import org.gringlobal.model.SysUser;
import org.gringlobal.model.notification.NotificationSchedule;
import org.gringlobal.service.ActionService;
import org.gringlobal.service.ActionService.ActionRequest;
import org.gringlobal.service.ActionService.ActionScheduleFilter;
import org.gringlobal.service.filter.ActionFilter;
import org.gringlobal.service.AppResourceService;
import org.gringlobal.service.EMailService;
import org.gringlobal.service.TemplatingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.i18n.LocaleContextHolder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class ActionNotificationsBase<T extends AbstractAction<T>, F extends ActionFilter<F, T>, R extends ActionRequest, ASF extends ActionScheduleFilter<ASF, T>> {
@Autowired
protected AppResourceService appResourceService;
@Autowired
protected EMailService emailService;
@Autowired
protected TemplatingService templatingService;
@Value("${frontend.url}")
protected String frontendUrl;
protected abstract String getFrontendUrl();
protected abstract String getCodeValueGroup();
protected abstract ActionService<T, F, R, ASF> getService();
protected void generateNotification(NotificationSchedule schedule, List<SysUser> recipients, ASF overviewFilter) throws Exception {
log.info("Generating notifications");
// Recipients emails
var sysUserRecipients = recipients.stream()
.map(SysUser::getCooperator) // To cooperator
.filter(Objects::nonNull)
.peek(cooperator -> log.debug("Notifiying {}", cooperator))
.map(Cooperator::getEmail) // To email
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
if (sysUserRecipients.isEmpty()) return;
var overview = getService().actionScheduleOverview(overviewFilter);
ActionTemplateData actionTemplateData = new ActionTemplateData();
actionTemplateData.scheduled = overview.scheduled.entrySet();
actionTemplateData.added = overview.added.entrySet();
actionTemplateData.created = overview.created.entrySet();
actionTemplateData.completed = overview.completed.entrySet();
actionTemplateData.canceled = overview.canceled.entrySet();
actionTemplateData.inProgress = overview.inProgress.entrySet();
actionTemplateData.overdue = overview.overdue.entrySet();
// Count sum for each action category
actionTemplateData.scheduledSum = overview.scheduled.values().stream().map(number -> (long) number).reduce(0L, Long::sum);
actionTemplateData.addedSum = overview.added.values().stream().map(number -> (long) number).reduce(0L, Long::sum);
actionTemplateData.createdSum = overview.created.values().stream().map(number -> (long) number).reduce(0L, Long::sum);
actionTemplateData.completedSum = overview.completed.values().stream().map(number -> (long) number).reduce(0L, Long::sum);
actionTemplateData.canceledSum = overview.canceled.values().stream().map(number -> (long) number).reduce(0L, Long::sum);
actionTemplateData.inProgressSum = overview.inProgress.values().stream().map(number -> (long) number).reduce(0L, Long::sum);
actionTemplateData.overdueSum = overview.overdue.values().stream().map(number -> (long) number).reduce(0L, Long::sum);
var count = actionTemplateData.scheduledSum + actionTemplateData.addedSum + actionTemplateData.createdSum + actionTemplateData.completedSum + actionTemplateData.canceledSum + actionTemplateData.inProgressSum + actionTemplateData.overdueSum;
if (count == 0) {
log.info("Nothing interesting to send! Skipping.");
return;
}
Map<String, Object> templateParams = new HashMap<>();
templateParams.put("data", actionTemplateData);
templateParams.put("fromInstant", overviewFilter.fromInclusive);
templateParams.put("toInstant", overviewFilter.toExclusive);
templateParams.put("actionOverviewURL", getFrontendUrl());
templateParams.put("title", schedule.getTitle());
templateParams.put("actionCodeGroup", getCodeValueGroup());
var resource = appResourceService.getResource(AppResourceService.APP_NAME_GGCE, "notification/action-schedule.mustache", LocaleContextHolder.getLocale());
String template;
if (resource != null) {
template = resource.getDisplayMember();
} else {
log.info("AppResource 'notification/action-schedule.mustache' not found, using bundled template");
template = Files.readString(Path.of(getClass().getResource("/notification/action-schedule.mustache").getPath()));
}
var messageBody = templatingService.fillTemplate(template, templateParams);
for (var emailTo : sysUserRecipients) {
emailService.sendMail(schedule.getTitle(), messageBody, emailTo);
}
}
@Getter
@Setter
private static class ActionTemplateData {
public Set<Map.Entry<String, Number>> scheduled;
public Set<Map.Entry<String, Number>> added;
public Set<Map.Entry<String, Number>> created;
public Set<Map.Entry<String, Number>> completed;
public Set<Map.Entry<String, Number>> canceled;
public Set<Map.Entry<String, Number>> inProgress;
public Set<Map.Entry<String, Number>> overdue;
public Long scheduledSum;
public Long addedSum;
public Long createdSum;
public Long completedSum;
public Long canceledSum;
public Long inProgressSum;
public Long overdueSum;
}
}