/**
* This file is part of lavagna.
*
* lavagna is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* lavagna is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with lavagna. If not, see <http://www.gnu.org/licenses/>.
*/
package io.lavagna.service.calendarutils;
import io.lavagna.model.*;
import io.lavagna.service.CardDataService;
import io.lavagna.service.UserRepository;
import net.fortuna.ical4j.model.*;
import net.fortuna.ical4j.model.component.VAlarm;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.parameter.Cn;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.property.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static org.apache.commons.lang3.ObjectUtils.firstNonNull;
public class CalendarVEventHandler implements CalendarEventHandler {
private final Map<Integer, UserDescription> usersCache = new HashMap<>();
private final String applicationUrl;
private final CardDataService cardDataService;
private final UserRepository userRepository;
private final List<VEvent> events;
public CalendarVEventHandler(String applicationUrl, CardDataService cardDataService, UserRepository userRepository,
List<VEvent> events) {
this.applicationUrl = applicationUrl;
this.cardDataService = cardDataService;
this.userRepository = userRepository;
this.events = events;
}
private long getLong(int x, int y) {
return (((long) x) << 32) | (y & 0xffffffffL);
}
private String getEventName(CardFullWithCounts card) {
return String.format("%s-%s %s (%s)", card.getBoardShortName(), card.getSequence(), card.getName(),
card.getColumnDefinition());
}
private UserDescription getUserDescription(int userId, Map<Integer, UserDescription> cache) {
if (!cache.containsKey(userId)) {
User u = userRepository.findById(userId);
String name = firstNonNull(u.getDisplayName(), u.getEmail(), u.getUsername());
String email = String.format("MAILTO:%s", firstNonNull(u.getEmail(), "unknown@unknown.com"));
cache.put(userId, new UserDescription(name, email));
}
return cache.get(userId);
}
private VAlarm createReminder(String name) {
final VAlarm reminder = new VAlarm(new Dur(0, 0, 0, 0));
reminder.getProperties().add(Action.DISPLAY);
reminder.getProperties().add(new Description(name));
return reminder;
}
public void addMilestoneEvent(String projectShortName, java.util.Date date, LabelListValueWithMetadata m,
SearchResults cards) throws URISyntaxException {
URI uri = new URI(String.format("%s%s/milestones/", applicationUrl, projectShortName));
double closed = 0;
double total = 0;
StringBuilder descBuilder = new StringBuilder();
for (CardFullWithCounts card : cards.getFound()) {
if (card.getColumnDefinition() == ColumnDefinition.CLOSED) {
closed++;
}
total++;
descBuilder.append(getEventName(card));
descBuilder.append("\n");
}
final String name = String.format("%s - %s (%.0f%%)", projectShortName, m.getValue(),
total > 0 ? 100 * closed / total : 100);
final UUID id = new UUID(getLong(m.getCardLabelId(), m.getId()), getLong(m.getOrder(), 0));
DateTime dueDate = new DateTime(date.getTime());
dueDate.setUtc(true);
final VEvent event = new VEvent(dueDate, name);
event.getProperties().getProperty(Property.DTSTART).getParameters().add(Value.DATE_TIME);
event.getProperties().add(new Description(descBuilder.toString()));
event.getProperties().add(new Uid(id.toString()));
// Reminder on milestone's date
if (!m.getMetadata().containsKey("status") || !m.getMetadata().get("status").equals("CLOSED")) {
event.getAlarms().add(createReminder(name));
}
// Url
event.getProperties().add(new Url(uri));
events.add(event);
}
public void addCardEvent(CardFullWithCounts card, LabelAndValue lav) throws URISyntaxException {
URI uri = new URI(String.format("%s%s/%s-%s", applicationUrl, card.getProjectShortName(),
card.getBoardShortName(), card.getSequence()));
CardDataHistory cardDesc = cardDataService.findLatestDescriptionByCardId(card.getId());
String name = getEventName(card);
final UUID id = new UUID(getLong(card.getColumnId(), card.getId()), getLong(lav.getLabelId(),
lav.getLabelValueId()));
// Organizer
UserDescription ud = getUserDescription(card.getCreationUser(), usersCache);
DateTime dueDate = new DateTime(lav.getLabelValueTimestamp());
dueDate.setUtc(true);
final VEvent event = new VEvent(dueDate, name);
event.getProperties().getProperty(Property.DTSTART).getParameters().add(Value.DATE_TIME);
event.getProperties().add(new Created(new DateTime(card.getCreationDate())));
event.getProperties().add(new LastModified(new DateTime(card.getLastUpdateTime())));
event.getProperties().add(new Uid(id.toString()));
// Reminder on label's date
if (card.getColumnDefinition() != ColumnDefinition.CLOSED) {
event.getAlarms().add(createReminder(name));
}
// Organizer
Organizer organizer = new Organizer(URI.create(ud.getEmail()));
organizer.getParameters().add(new Cn(ud.getName()));
event.getProperties().add(organizer);
// Url
event.getProperties().add(new Url(uri));
// Description
if (cardDesc != null) {
event.getProperties().add(new Description(cardDesc.getContent()));
}
events.add(event);
}
private class UserDescription {
private String name;
private String email;
@java.beans.ConstructorProperties({ "name", "email" }) public UserDescription(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return this.name;
}
public String getEmail() {
return this.email;
}
}
}