package com.piggymetrics.statistics.service;
import com.google.common.collect.ImmutableMap;
import com.piggymetrics.statistics.domain.*;
import com.piggymetrics.statistics.domain.timeseries.DataPoint;
import com.piggymetrics.statistics.domain.timeseries.DataPointId;
import com.piggymetrics.statistics.domain.timeseries.ItemMetric;
import com.piggymetrics.statistics.domain.timeseries.StatisticMetric;
import com.piggymetrics.statistics.repository.DataPointRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class StatisticsServiceImpl implements StatisticsService {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
private DataPointRepository repository;
@Autowired
private ExchangeRatesService ratesService;
/**
* {@inheritDoc}
*/
@Override
public List<DataPoint> findByAccountName(String accountName) {
Assert.hasLength(accountName);
return repository.findByIdAccount(accountName);
}
/**
* {@inheritDoc}
*/
@Override
public DataPoint save(String accountName, Account account) {
Instant instant = LocalDate.now().atStartOfDay()
.atZone(ZoneId.systemDefault()).toInstant();
DataPointId pointId = new DataPointId(accountName, Date.from(instant));
Set<ItemMetric> incomes = account.getIncomes().stream()
.map(this::createItemMetric)
.collect(Collectors.toSet());
Set<ItemMetric> expenses = account.getExpenses().stream()
.map(this::createItemMetric)
.collect(Collectors.toSet());
Map<StatisticMetric, BigDecimal> statistics = createStatisticMetrics(incomes, expenses, account.getSaving());
DataPoint dataPoint = new DataPoint();
dataPoint.setId(pointId);
dataPoint.setIncomes(incomes);
dataPoint.setExpenses(expenses);
dataPoint.setStatistics(statistics);
dataPoint.setRates(ratesService.getCurrentRates());
log.debug("new datapoint has been created: {}", pointId);
return repository.save(dataPoint);
}
private Map<StatisticMetric, BigDecimal> createStatisticMetrics(Set<ItemMetric> incomes, Set<ItemMetric> expenses, Saving saving) {
BigDecimal savingAmount = ratesService.convert(saving.getCurrency(), Currency.getBase(), saving.getAmount());
BigDecimal expensesAmount = expenses.stream()
.map(ItemMetric::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal incomesAmount = incomes.stream()
.map(ItemMetric::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return ImmutableMap.of(
StatisticMetric.EXPENSES_AMOUNT, expensesAmount,
StatisticMetric.INCOMES_AMOUNT, incomesAmount,
StatisticMetric.SAVING_AMOUNT, savingAmount
);
}
/**
* Normalizes given item amount to {@link Currency#getBase()} currency with
* {@link TimePeriod#getBase()} time period
*/
private ItemMetric createItemMetric(Item item) {
BigDecimal amount = ratesService
.convert(item.getCurrency(), Currency.getBase(), item.getAmount())
.divide(item.getPeriod().getBaseRatio(), 4, RoundingMode.HALF_UP);
return new ItemMetric(item.getTitle(), amount);
}
}