package fr.ippon.tatami.service;
import fr.ippon.tatami.domain.DigestType;
import fr.ippon.tatami.domain.Domain;
import fr.ippon.tatami.domain.Group;
import fr.ippon.tatami.domain.User;
import fr.ippon.tatami.repository.DomainRepository;
import fr.ippon.tatami.repository.MailDigestRepository;
import fr.ippon.tatami.repository.UserRepository;
import fr.ippon.tatami.service.dto.StatusDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.inject.Inject;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
/**
* This service generates digest emails for subscribed users.
*
* @author Pierre Rust
*/
@Service
public class MailDigestService {
private static final Logger log = LoggerFactory.getLogger(MailDigestService.class);
private final static int MAX_STATUS_DAILY_DIGEST = 10;
private final static int MAX_STATUS_WEEKLY_DIGEST = 10;
@Inject
private MailDigestRepository mailDigestRepository;
@Inject
private DomainRepository domainRepository;
@Inject
private UserRepository userRepository;
@Inject
private TimelineService timelineService;
@Inject
private MailService mailService;
@Inject
private SuggestionService suggestionService;
/**
* Sends daily digest. Must be run every day
*/
@Scheduled(cron = "0 0 22 * * ?")
public void dailyDigest() {
log.info("Starting Daily digest mail process ");
Set<Domain> domains = domainRepository.getAllDomains();
String day = String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_WEEK));
for (Domain d : domains) {
log.info("Sending daily digest for domain {} and day {}", d, day);
int pagination = 0;
List<String> logins;
do {
logins = mailDigestRepository.getLoginsRegisteredToDigest(
DigestType.DAILY_DIGEST, d.getName(), day, pagination);
pagination = pagination + logins.size();
for (String login : logins) {
try {
handleDailyDigestPageForLogin(login);
} catch (Exception e) {
log.warn("An error has occured when generating daily digest for user " + login + ": " + e.getMessage());
StringWriter stack = new StringWriter();
PrintWriter pw = new PrintWriter(stack);
e.printStackTrace(pw);
log.debug("{}", stack.toString());
}
}
} while (logins.size() > 0);
}
}
/**
* Sends weekly digest.
* <p/>
* Will run every mondayday and send mails to all registered users
* <p/>
* If this is too many mails, this method could be tuned to
* distribute the load on every day of the week by only sending
* mails to users who have subscribed that same day.
*/
@Scheduled(cron = "0 0 01 ? * MON")
public void weeklyDigest() {
log.info("Starting Weekly digest mail process ");
Set<Domain> domains = domainRepository.getAllDomains();
// sent digest for all domains
// for users that have register any day of the week
for (int i = 1; i < 8; ++i) {
String day = String.valueOf(i);
for (Domain d : domains) {
log.info("Sending weekly digest for domain {} and day {}", d, i);
int pagination = 0;
List<String> logins;
do {
logins = mailDigestRepository.getLoginsRegisteredToDigest(
DigestType.WEEKLY_DIGEST, d.getName(),
day, pagination);
pagination = pagination + logins.size();
for (String login : logins) {
try {
handleWeeklyDigestPageForLogin(login);
} catch (Exception e) {
log.warn("An error has occured when generating weekly digest for user " + login + ": " + e.getMessage());
StringWriter stack = new StringWriter();
PrintWriter pw = new PrintWriter(stack);
e.printStackTrace(pw);
log.debug("{}", stack.toString());
}
}
} while (logins.size() > 0);
}
}
}
/**
* Fetch all necessary info for a daily digest mail
* and delegate the sending operation to mailService.
*/
private void handleDailyDigestPageForLogin(String login) {
log.info("Preparing weekly digest for user " + login);
User user = userRepository.findUserByLogin(login);
// we want statuses for the past 24 hours
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1);
Date yesterday = cal.getTime();
List<StatusDTO> digestStatuses = new ArrayList<StatusDTO>(MAX_STATUS_DAILY_DIGEST);
int nbStatusTotal = getStatusesForDigest(user, yesterday, MAX_STATUS_DAILY_DIGEST, digestStatuses);
Collection<User> suggestedUsers = suggestionService.suggestUsers(user.getLogin());
// TODO : we could look for popular messages
// especially if the user
// does not have anything in it's timeline and there are no suggested users for him
mailService.sendDailyDigestEmail(user, digestStatuses, nbStatusTotal, suggestedUsers);
}
/**
* Fetch all necessary info for a weekly digest mail
* and delegate the sending operation to mailService.
*/
private void handleWeeklyDigestPageForLogin(String login) {
log.info("Preparing weekly digest for user " + login);
User user = userRepository.findUserByLogin(login);
// we want statuses for the past week
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -7);
Date lastWeek = cal.getTime();
List<StatusDTO> digestStatuses = new ArrayList<StatusDTO>(MAX_STATUS_WEEKLY_DIGEST);
int nbStatusTotal = getStatusesForDigest(user, lastWeek, MAX_STATUS_WEEKLY_DIGEST, digestStatuses);
Collection<User> suggestedUsers = suggestionService.suggestUsers(user.getLogin());
Collection<Group> suggestedGroups = suggestionService.suggestGroups(user.getLogin());
mailService.sendWeeklyDigestEmail(user, digestStatuses, nbStatusTotal,
suggestedUsers, suggestedGroups);
}
/**
* Build a list containing an extract of the status from an user timeline,
* except its own, since a given date.
*
* @param user the user
* @param since_date date since
* @param nbStatus number of status to include in the extract
* @param digestStatuses selected status will be added to this list (ordered by date)
* @return the number of statuses
*/
private int getStatusesForDigest(final User user, final Date since_date,
int nbStatus, List<StatusDTO> digestStatuses) {
String finish = null;
boolean dateReached = false;
List<StatusDTO> allStatuses = new ArrayList<StatusDTO>(50);
// collect all statuses since 'since_date' from the timeline
while (!dateReached) {
Collection<StatusDTO> statuses = timelineService.getUserTimeline(user.getLogin(), 200, null, finish);
statuses.size();
int count = 0;
if (statuses.isEmpty()) {
dateReached = true;
}
for (StatusDTO status : statuses) {
if (status.getStatusDate().before(since_date)) {
dateReached = true;
break;
} else {
// Do not includes user's own status in digest
if (!status.getUsername().equals(user.getUsername())) {
allStatuses.add(status);
}
}
count++;
if (count == statuses.size() && !dateReached) {
finish = status.getStatusId();
}
}
}
int nbStatusTotal = allStatuses.size();
if (nbStatusTotal > 0) {
// now select some of theses statuses
if (allStatuses.size() > nbStatus) {
Collections.shuffle(allStatuses);
digestStatuses.addAll(allStatuses.subList(0, nbStatus));
Collections.sort(digestStatuses, new Comparator<StatusDTO>() {
@Override
public int compare(StatusDTO statusDTO, StatusDTO statusDTO2) {
return statusDTO.getStatusDate().compareTo(statusDTO2.getStatusDate());
}
});
} else {
digestStatuses.addAll(allStatuses);
}
}
return nbStatusTotal;
}
}