package io.budgetapp.dao;
import io.budgetapp.model.Budget;
import io.budgetapp.util.Util;
import io.budgetapp.application.AccessDeniedException;
import io.budgetapp.application.NotFoundException;
import io.budgetapp.configuration.AppConfiguration;
import io.budgetapp.model.User;
import io.dropwizard.hibernate.AbstractDAO;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
*
*/
public class BudgetDAO extends AbstractDAO<Budget> {
private static final Logger LOGGER = LoggerFactory.getLogger(BudgetDAO.class);
private final AppConfiguration configuration;
public BudgetDAO(SessionFactory sessionFactory, AppConfiguration configuration) {
super(sessionFactory);
this.configuration = configuration;
}
/**
* add budget for a given user
* @param user owner
* @param budget new budget
* @return new budget
*/
public Budget addBudget(User user, Budget budget) {
LOGGER.debug("User {} add budget {}", user, budget);
if(budget.getPeriod() == null) {
budget.setPeriod(Util.currentYearMonth());
}
budget.setUser(user);
return persist(budget);
}
/**
* find budgets for a given user for current month-year
* @param user
* @return
*/
public List<Budget> findBudgets(User user) {
LocalDate now = LocalDate.now();
return findBudgets(user, now.getMonthValue(), now.getYear(), false);
}
/**
* find budgets for a given user for given month-year
* @param user
* @param month
* @param year
* @param lazy
* @return
*/
public List<Budget> findBudgets(User user, int month, int year, boolean lazy) {
LOGGER.debug("Find budgets by user {} by date {}-{}", user, month, year);
Date yearMonth = Util.yearMonthDate(month, year);
Criteria criteria = currentSession().createCriteria(Budget.class);
if(!lazy) {
criteria.setFetchMode("category", FetchMode.JOIN);
}
criteria.add(Restrictions.eq("user", user));
criteria.add(Restrictions.eq("period", yearMonth));
criteria.addOrder(Order.asc("id"));
return list(criteria);
}
public Date findLatestBudget(User user) {
LOGGER.debug("Find latest budget by user {}", user);
Criteria criteria = currentSession().createCriteria(Budget.class);
criteria.add(Restrictions.eq("user", user));
criteria.setProjection(Projections.max("createdAt"));
criteria.setMaxResults(1);
return (Date)criteria.uniqueResult();
}
/**
* find budget by given id
* @param budgetId
* @return
*/
public Budget findById(long budgetId) {
Budget budget = get(budgetId);
if(budget == null) {
throw new NotFoundException();
}
return budget;
}
/**
* find budget for given user and id
* @param user
* @param budgetId
* @return
*/
public Budget findById(User user, Long budgetId) {
Budget budget = findById(budgetId);
if(!Objects.equals(user.getId(), budget.getUser().getId())) {
throw new AccessDeniedException();
}
return budget;
}
public void update(Budget budget) {
persist(budget);
}
public void delete(Budget budget) {
currentSession().delete(budget);
}
public Map<String, List<Budget>> findDefaultBudgets() {
return configuration.getBudgets();
}
public List<Budget> findByRange(User user, int startMonth, int startYear, int endMonth, int endYear) {
Date start = Util.yearMonthDate(startMonth, startYear);
Date end = Util.yearMonthDate(endMonth, endYear);
Query query = currentSession().createQuery("FROM Budget b WHERE b.user = :user AND b.period BETWEEN :start AND :end");
query
.setParameter("user", user)
.setParameter("start", start)
.setParameter("end", end);
return list(query);
}
public Budget findByBudgetType(Long budgetTypeId) {
Date now = Util.currentYearMonth();
Query query = currentSession().createQuery("FROM Budget b WHERE b.budgetType.id = :budgetTypeId AND b.period = :period");
query
.setParameter("budgetTypeId", budgetTypeId)
.setParameter("period", now);
return uniqueResult(query);
}
public List<Budget> findByUserAndCategory(User user, long categoryId) {
Criteria criteria = userCriteria(user);
criteria.add(Restrictions.eq("category.id", categoryId));
criteria.add(Restrictions.eq("period", Util.currentYearMonth()));
return list(criteria);
}
public List<String> findSuggestions(User user, String q) {
q = q == null? "": q.toLowerCase();
Query query = currentSession().createQuery("SELECT b.name FROM Budget l WHERE b.user != :user AND LOWER(b.name) LIKE :q");
query
.setParameter("user", user)
.setParameter("q", "%" + q + "%");
return (List<String>)query.list();
}
private Criteria defaultCriteria() {
return currentSession().createCriteria(Budget.class);
}
private Criteria userCriteria(User user) {
Criteria criteria = defaultCriteria();
criteria.add(Restrictions.eq("user", user));
return criteria;
}
}