/** * 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; import io.lavagna.model.BoardColumn.BoardColumnLocation; import io.lavagna.model.*; import io.lavagna.model.Event.EventType; import org.apache.commons.lang3.builder.CompareToBuilder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; @Service @Transactional(readOnly = true) public class CardService { private final EventRepository eventRepository; private final CardRepository cardRepository; private final CardDataRepository cardDataRepository; private final CardDataService cardDataService; private final CardLabelRepository cardLabelRepository; public CardService(CardRepository cardRepository, CardDataRepository cardDataRepository, EventRepository eventRepository, CardDataService cardDataService, CardLabelRepository cardLabelRepository) { this.cardRepository = cardRepository; this.eventRepository = eventRepository; this.cardDataRepository = cardDataRepository; this.cardDataService = cardDataService; this.cardLabelRepository = cardLabelRepository; } private static List<Integer> fetchIds(List<CardFull> cards) { List<Integer> r = new ArrayList<>(cards.size()); for (CardFull c : cards) { r.add(c.getId()); } return r; } public boolean existCardWith(String boardShortName, int seqNumber) { return cardRepository.existCardWith(boardShortName, seqNumber); } public CardFullWithCounts findFullBy(int cardId) { CardFull card = cardRepository.findFullBy(cardId); return fetchCardFull(Collections.singletonList(card)).get(0); } public CardFullWithCounts findFullBy(String boardShortName, int seqNumber) { CardFull card = cardRepository.findFullBy(boardShortName, seqNumber); return fetchCardFull(Collections.singletonList(card)).get(0); } public List<CardFullWithCounts> fetchAllInColumn(int columnId) { List<CardFull> cards = cardRepository.findAllByColumnId(columnId); if (cards.isEmpty()) { return Collections.emptyList(); } List<CardFullWithCounts> res = fetchCardFull(cards); Collections.sort(res, new Comparator<CardFullWithCounts>() { @Override public int compare(CardFullWithCounts o1, CardFullWithCounts o2) { return new CompareToBuilder().append(o1.getOrder(), o2.getOrder()).toComparison(); } }); // return res; } List<CardFull> findFullBy(Collection<Integer> ids) { return cardRepository.findFullBy(ids); } List<CardFullWithCounts> fetchCardFull(List<CardFull> cards) { List<Integer> ids = fetchIds(cards); Map<Integer, Map<String, CardDataCount>> counts = aggregateByCardId( cardDataRepository.findCountsByCardIds(ids)); Map<Integer, List<LabelAndValue>> labels = cardLabelRepository.findCardLabelValuesByCardIds(ids); List<CardFullWithCounts> res = new ArrayList<>(); for (CardFull card : cards) { res.add(new CardFullWithCounts(card, counts.get(card.getId()), labels.get(card.getId()))); } return res; } public List<CardFullWithCounts> fetchPaginatedByBoardIdAndLocation(int boardId, BoardColumnLocation location, int page) { List<Integer> ids = cardRepository.fetchPaginatedByBoardIdAndLocation(boardId, location, page); if (ids.isEmpty()) { return Collections.emptyList(); } Map<Integer, CardFull> cardFullById = aggregateCardFullByCardId(cardRepository.findAllByIds(ids)); Map<Integer, Map<String, CardDataCount>> counts = aggregateByCardId(cardDataRepository.findCountsByCardIds(ids)); Map<Integer, List<LabelAndValue>> labels = cardLabelRepository.findCardLabelValuesByCardIds(ids); List<CardFullWithCounts> res = new ArrayList<>(ids.size()); for (int id : ids) { CardFull card = cardFullById.get(id); res.add(new CardFullWithCounts(card, counts.get(card.getId()), labels.get(card.getId()))); } return res; } @Transactional(readOnly = false) public void moveCardsToColumn(List<Integer> cardIds, int previousColumnId, int columnId, int userId, EventType boardEventType, Date time) { List<Integer> updated = cardRepository.moveCardsToColumn(cardIds, previousColumnId, columnId, userId); eventRepository.insertCardEvents(updated, previousColumnId, columnId, userId, boardEventType, time, null); } @Transactional(readOnly = false) public Event updateCardName(int cardId, String name, User user, Date date) { Card card = cardRepository.updateCard(cardId, name); return eventRepository.insertCardEvent(cardId, card.getColumnId(), user.getId(), EventType.CARD_UPDATE, date, name); } @Transactional(readOnly = false) public Card cloneCard(int cardToCopyId, int columnId, User user) { Card cardToCopy = cardRepository.findBy(cardToCopyId); Card newCard = createCard(cardToCopy.getName(), columnId, new Date(), user); Map<CardLabel, List<CardLabelValue>> labels = cardLabelRepository.findCardLabelValuesByCardId(cardToCopyId); for (CardLabel label : labels.keySet()) { for (CardLabelValue labelValue : labels.get(label)) { cardLabelRepository.addLabelValueToCard(label, newCard.getId(), labelValue.getValue()); } } // Copy the description CardDataHistory desc = cardDataService.findLatestDescriptionByCardId(cardToCopyId); if (desc != null) { cardDataService.updateDescription(newCard.getId(), desc.getContent(), desc.getTime(), desc.getUserId()); } // Copy comments for (CardDataFull cData : cardDataService.findAllCommentsByCardId(cardToCopyId)) { if(cData.getEventType() == EventType.COMMENT_CREATE) { cardDataService.createComment(newCard.getId(), cData.getContent(), cData.getTime(), cData.getUserId()); } } // Copy action lists Map<Integer, Integer> actionListsNewIds = new HashMap<>(); for (CardData iData : cardDataService.findAllActionListsAndItemsByCardId(cardToCopyId)) { if (iData.getType().equals(CardType.ACTION_LIST)) { CardData actionList = cardDataService .createActionList(newCard.getId(), iData.getContent(), user.getId(), new Date()); actionListsNewIds.put(iData.getId(), actionList.getId()); } else { CardData actionItem = cardDataService.createActionItem(newCard.getId(), actionListsNewIds.get(iData.getReferenceId()), iData.getContent(), user.getId(), new Date()); if (iData.getType().equals(CardType.ACTION_CHECKED)) { cardDataService.toggleActionItem(actionItem.getId(), true, user.getId(), new Date()); } } } return newCard; } @Transactional(readOnly = false) public Card createCard(String name, int columnId, Date creationTime, User user) { Card card = cardRepository.createCard(name, columnId, user); eventRepository.insertCardEvent(card.getId(), columnId, user.getId(), EventType.CARD_CREATE, creationTime, card.getName()); return card; } @Transactional(readOnly = false) public Card createCardFromTop(String name, int columnId, Date creationTime, User user) { Card card = cardRepository.createCardFromTop(name, columnId, user); eventRepository.insertCardEvent(card.getId(), columnId, user.getId(), EventType.CARD_CREATE, creationTime, card.getName()); return card; } @Transactional(readOnly = false) public Event moveCardToColumn(int cardId, int previousColumnId, int columnId, int userId, Date date) { cardRepository.moveCardToColumn(cardId, previousColumnId, columnId); return eventRepository.insertCardEvent(cardId, previousColumnId, columnId, userId, EventType.CARD_MOVE, date, null); } @Transactional(readOnly = false) public Event moveCardToColumnAndReorder(int cardId, int prevColumnId, int newColumnId, List<Integer> newOrderForNewColumn, User user) { cardRepository.moveCardToColumnAndReorder(cardId, prevColumnId, newColumnId, newOrderForNewColumn); return eventRepository.insertCardEvent(cardId, prevColumnId, newColumnId, user.getId(), EventType.CARD_MOVE, new Date(), null); } private static Map<Integer, Map<String, CardDataCount>> aggregateByCardId(List<CardDataCount> counts) { Map<Integer, Map<String, CardDataCount>> r = new TreeMap<>(); for (CardDataCount c : counts) { if (!r.containsKey(c.getCardId())) { r.put(c.getCardId(), new TreeMap<String, CardDataCount>()); } r.get(c.getCardId()).put(c.getType(), c); } return r; } private static Map<Integer, CardFull> aggregateCardFullByCardId(List<CardFull> cards) { Map<Integer, CardFull> res = new HashMap<>(); for(CardFull card : cards) { res.put(card.getId(), card); } return res; } }