/**
* 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.common.Constants;
import io.lavagna.model.BoardColumn.BoardColumnLocation;
import io.lavagna.model.*;
import io.lavagna.model.CardLabel.LabelDomain;
import io.lavagna.model.CardLabel.LabelType;
import io.lavagna.model.CardLabelValue.LabelValue;
import io.lavagna.query.CardLabelQuery;
import io.lavagna.query.ListValueMetadataQuery;
import org.apache.commons.lang3.Validate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
@Repository
@Transactional(readOnly = true)
public class CardLabelRepository {
private final NamedParameterJdbcTemplate jdbc;
private final CardLabelQuery queries;
private final ListValueMetadataQuery listValuesMetadataQueries;
public CardLabelRepository(NamedParameterJdbcTemplate jdbc, CardLabelQuery queries,
ListValueMetadataQuery listValuesMetadataQueries) {
this.jdbc = jdbc;
this.queries = queries;
this.listValuesMetadataQueries = listValuesMetadataQueries;
}
@Transactional(readOnly = false)
public void addSystemLabels(int projectId) {
queries.addSystemLabels(projectId);
}
@Transactional(readOnly = false)
public CardLabel addLabel(int projectId, boolean unique, LabelType labelType, LabelDomain labelDomain, String name,
int color) {
final boolean reservedName = Constants.RESERVED_SYSTEM_LABELS_NAME.contains(name);
Validate.isTrue((labelDomain == LabelDomain.SYSTEM && reservedName)
|| (labelDomain == LabelDomain.USER && !reservedName), name + " is a reserved system label name");
queries.addLabel(projectId, unique, labelType.toString(), labelDomain.toString(), name, color);
return queries.findLastCreatedLabel();
}
@Transactional(readOnly = false)
public void removeLabel(int labelId) {
queries.removeLabelListValues(labelId);
queries.removeLabel(labelId);
}
public List<CardLabel> findLabelsByProject(int projectId) {
return queries.findLabelsByProject(projectId);
}
public CardLabel findLabelById(int labelId) {
return queries.findLabelById(labelId);
}
public CardLabel findLabelByName(int projectId, String labelName, LabelDomain labelDomain) {
return queries.findLabelByName(projectId, labelName, labelDomain.toString());
}
public List<CardLabel> findLabelsByName(int projectId, String labelName, LabelDomain labelDomain) {
return queries.findLabelsByName(projectId, labelName, labelDomain.toString());
}
public CardLabelValue findLabelValueById(int labelValueId) {
return queries.findLabelValueById(labelValueId);
}
public List<CardLabelValue> findLabelValueByLabelAndValue(int cardId, CardLabel cl, LabelValue lv) {
return queries.findLabelValueByLabelAndValue(cardId, cl.getId(), lv.getValueString(), lv.getValueTimestamp(),
lv.getValueInt(), lv.getValueCard(), lv.getValueUser(), lv.getValueList());
}
/**
* Return a map of label by {cardId => {CardLabel => [CardLabelValue]}}
*
* @return
*/
public Map<Integer, Map<CardLabel, List<CardLabelValue>>> findCardLabelValuesByBoardId(int boardId,
BoardColumnLocation location) {
Map<Integer, Map<CardLabel, List<CardLabelValue>>> res = new HashMap<>();
for (LabelAndValue lv : queries.findCardLabelValuesByBoardId(boardId, location.toString())) {
if (!res.containsKey(lv.getLabelValueCardId())) {
res.put(lv.getLabelValueCardId(), new HashMap<CardLabel, List<CardLabelValue>>());
}
CardLabel cl = lv.label();
if (!res.get(lv.getLabelValueCardId()).containsKey(cl)) {
res.get(lv.getLabelValueCardId()).put(cl, new ArrayList<CardLabelValue>());
}
res.get(lv.getLabelValueCardId()).get(cl).add(lv.labelValue());
}
return res;
}
public Map<Integer, List<LabelAndValue>> findCardLabelValuesByCardIds(List<Integer> ids) {
Map<Integer, List<LabelAndValue>> res = new HashMap<>();
for (LabelAndValue lv : queries.findCardLabelValuesByCardIds(ids)) {
if (!res.containsKey(lv.getLabelValueCardId())) {
res.put(lv.getLabelValueCardId(), new ArrayList<LabelAndValue>());
}
res.get(lv.getLabelValueCardId()).add(lv);
}
return res;
}
public Map<CardLabel, List<CardLabelValue>> findCardLabelValuesByCardId(int cardId) {
Map<CardLabel, List<CardLabelValue>> res = new HashMap<>();
for (LabelAndValue lv : queries.findCardLabelValuesByCardId(cardId)) {
CardLabel cl = lv.label();
if (!res.containsKey(cl)) {
res.put(cl, new ArrayList<CardLabelValue>());
}
res.get(cl).add(lv.labelValue());
}
return res;
}
@Transactional(readOnly = false)
public CardLabel updateLabel(int labelId, Label label) {
CardLabel cl = findLabelById(labelId);
Validate.isTrue(cl.getDomain() == LabelDomain.USER, "Cannot update values in SYSTEM label for label with id "
+ labelId);
return updateLabel(label, cl);
}
@Transactional(readOnly = false)
public CardLabel updateSystemLabel(int labelId, Label label) {
CardLabel cl = findLabelById(labelId);
Validate.isTrue(cl.getDomain() == LabelDomain.SYSTEM, "Cannot update values in USER label for label with id "
+ labelId);
return updateLabel(label, cl);
}
public List<CardLabel> findUserLabelNameBy(String term, Integer projectId, UserWithPermission userWithPermission) {
Set<Integer> projectIdFilter = userWithPermission.toProjectIdsFilter(projectId);
return projectIdFilter.isEmpty() ?
queries.findUserLabelNameBy(term) :
queries.findUserLabelNameBy(term, projectIdFilter);
}
public List<String> findListValuesBy(LabelDomain domain, String labelName, String term, Integer projectId,
UserWithPermission userWithPermission) {
Set<Integer> projectIdFilter = userWithPermission.toProjectIdsFilter(projectId);
return projectIdFilter.isEmpty() ?
queries.findListValuesBy(domain.toString(), labelName, term) :
queries.findListValuesBy(domain.toString(), labelName, term, projectIdFilter);
}
@Transactional(readOnly = false)
private CardLabel updateLabel(Label label, CardLabel cl) {
// type cannot be changed!
Validate.isTrue(cl.getType() == label.getType());
CardLabel toUpdate = cl.set(label.getName(), label.getType(), label.getColor());
queries.updateLabel(toUpdate.getName(), toUpdate.getColor(), toUpdate.getType().toString(), toUpdate.getId());
return toUpdate;
}
@Transactional(readOnly = false)
public CardLabelValue addLabelValueToCard(CardLabel label, int cardId, LabelValue val) {
queries.addLabelValueToCard(cardId, label.getUnique() ? true : null, label.getId(), label.getType().toString(),
val.getValueString(), val.getValueTimestamp(), val.getValueInt(), val.getValueCard(),
val.getValueUser(), val.getValueList());
return queries.findLastCreatedLabelValue();
}
@Transactional(readOnly = false)
public int removeLabelValue(CardLabelValue cardLabelValue) {
return queries.removeLabelValue(cardLabelValue.getCardLabelValueId());
}
// Label list values
@Transactional(readOnly = false)
public LabelListValue addLabelListValue(int labelId, String value) {
queries.addLabelListValue(labelId, value);
return queries.findLastCreatedLabelListValue();
}
@Transactional(readOnly = false)
public void removeLabelListValue(int labelListValueId) {
queries.removeLabelListValue(labelListValueId);
}
@Transactional(readOnly = false)
public void updateLabelListValue(LabelListValue llv) {
queries.updateLabelListValue(llv.getId(), llv.getValue());
}
public List<LabelListValueWithMetadata> findListValuesByLabelId(int labelId) {
List<LabelListValue> res = queries.findListValuesByLabelId(labelId);
return addMetadata(res);
}
public List<LabelListValueWithMetadata> findListValuesByLabelIdAndValue(int labelId, String value) {
List<LabelListValue> res = queries.findListValuesByLabelIdAndValue(labelId, value);
return addMetadata(res);
}
public SortedMap<Integer, LabelListValueWithMetadata> findLabeListValueAggregatedByCardLabelId(int projectId) {
SortedMap<Integer, LabelListValueWithMetadata> m = new TreeMap<>();
for (LabelListValueWithMetadata l : addMetadata(queries.findListValueByProjectId(projectId))) {
m.put(l.getId(), l);
}
return m;
}
public LabelListValueWithMetadata findListValueById(int labelListValueId) {
LabelListValue res = queries.findListValueById(labelListValueId);
return addMetadata(Collections.singletonList(res)).get(0);
}
public LabelListValue findSimpleListValueById(int labelListValueId) {
return queries.findListValueById(labelListValueId);
}
@Transactional(readOnly = false)
public void moveLabelListValueToOrder(int valueId, int order) {
LabelListValue value = findListValueById(valueId);
if (value.getOrder() == order) {
return;
}
List<LabelListValueWithMetadata> currentValues = findListValuesByLabelId(value.getCardLabelId());
LabelListValueWithMetadata rval = currentValues.remove(value.getOrder() - 1);
currentValues.add(order - 1, rval);
List<SqlParameterSource> vals = new ArrayList<>();
int currentOrder = 1;
for (LabelListValue llv : currentValues) {
if (llv.getOrder() != currentOrder) {
vals.add(new MapSqlParameterSource("id", llv.getId()).addValue("order", currentOrder));
}
currentOrder++;
}
jdbc.batchUpdate(queries.updateLabelListValueOrder(), vals.toArray(new SqlParameterSource[vals.size()]));
}
@Transactional(readOnly = false)
public void swapLabelListValues(int first, int second) {
LabelListValue firstValue = findListValueById(first);
LabelListValue secondValue = findListValueById(second);
SqlParameterSource p1 = new MapSqlParameterSource("id", firstValue.getId()).addValue("order",
secondValue.getOrder());
SqlParameterSource p2 = new MapSqlParameterSource("id", secondValue.getId()).addValue("order",
firstValue.getOrder());
jdbc.batchUpdate(queries.updateLabelListValueOrder(), new SqlParameterSource[] { p1, p2 });
}
/**
* Return a mapping
*
* {labelListValue : {labelId : labelListValueId}}
*
* @param labelListValues
* @return
*/
public Map<String, Map<Integer, Integer>> findLabelListValueMapping(List<String> labelListValues) {
if (labelListValues.isEmpty()) {
return Collections.emptyMap();
}
final Map<String, Map<Integer, Integer>> res = new HashMap<>();
jdbc.query(queries.findLabelListValueMapping(), new MapSqlParameterSource("values", labelListValues),
new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
String name = rs.getString("CARD_LABEL_LIST_VALUE");
if (!res.containsKey(name)) {
res.put(name, new HashMap<Integer, Integer>());
}
res.get(name).put(rs.getInt("CARD_LABEL_ID_FK"), rs.getInt("CARD_LABEL_LIST_VALUE_ID"));
}
});
return res;
}
public int labelUsedCount(int labelId) {
return queries.labelUsedCount(labelId);
}
// ---
private List<LabelListValueWithMetadata> addMetadata(List<LabelListValue> in) {
Set<Integer> ids = new HashSet<>(in.size());
for (LabelListValue llv : in) {
ids.add(llv.getId());
}
Map<Integer, Map<String, String>> grouped = new HashMap<>();
for (ListValueMetadata lvm : ids.isEmpty() ?
Collections.<ListValueMetadata>emptyList() :
listValuesMetadataQueries.findByLabelListValueIds(ids)) {
if (!grouped.containsKey(lvm.getLabelListValueId())) {
grouped.put(lvm.getLabelListValueId(), new HashMap<String, String>());
}
grouped.get(lvm.getLabelListValueId()).put(lvm.getKey(), lvm.getValue());
}
List<LabelListValueWithMetadata> out = new ArrayList<>(in.size());
for (LabelListValue llv : in) {
out.add(new LabelListValueWithMetadata(llv, grouped.get(llv.getId())));
}
return out;
}
public List<ListValueMetadata> findListValueMetadataByLabelListValueId(int labelListValueId) {
return listValuesMetadataQueries.findByLabelListValueId(labelListValueId);
}
@Transactional(readOnly = false)
public void updateLabelListMetadata(ListValueMetadata metadata) {
listValuesMetadataQueries.update(metadata.getLabelListValueId(), metadata.getKey(), metadata.getValue());
}
@Transactional(readOnly = false)
public void createLabelListMetadata(int labelListValueId, String key, String value) {
listValuesMetadataQueries.insert(labelListValueId, key, value);
}
@Transactional(readOnly = false)
public void removeLabelListMetadata(int labelListValueId, String key) {
listValuesMetadataQueries.delete(labelListValueId, key);
}
public int countLabeListValueUse(int labelListValueId) {
return listValuesMetadataQueries.countUse(labelListValueId);
}
}