package com.code44.finance.ui.reports.categories; import android.content.Context; import android.database.Cursor; import android.support.v4.util.Pair; import com.code44.finance.R; import com.code44.finance.common.model.TransactionState; import com.code44.finance.common.model.TransactionType; import com.code44.finance.data.model.Category; import com.code44.finance.data.model.Currency; import com.code44.finance.data.model.Tag; import com.code44.finance.data.model.Transaction; import com.code44.finance.graphs.pie.PieChartData; import com.code44.finance.graphs.pie.PieChartValue; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; public class CategoriesReportData { private final PieChartData pieChartData; private final List<CategoriesReportItem> categoriesReportItems; public CategoriesReportData(Context context, Cursor cursor, Currency mainCurrency, TransactionType transactionType) { final Map<Category, Long> categoryExpenses = new HashMap<>(); final Map<Category, Map<Tag, Long>> categoryTagExpenses = new HashMap<>(); if (cursor.moveToFirst()) { final Category noCategory = createNoCategory(context, transactionType); do { final Transaction transaction = Transaction.from(cursor); if (isTransactionValid(transaction, transactionType)) { final Long amount = getAmount(transaction, mainCurrency); final Category category = transaction.getCategory() == null ? noCategory : transaction.getCategory(); increaseCategoryExpenseAmount(categoryExpenses, category, amount); increaseCategoryTagsExpenses(categoryTagExpenses, transaction, category, amount); } } while (cursor.moveToNext()); } final List<Pair<Category, Long>> sortedExpenses = new ArrayList<>(); for (Category category : categoryExpenses.keySet()) { sortedExpenses.add(Pair.create(category, categoryExpenses.get(category))); } Collections.sort(sortedExpenses, new CategoryExpenseComparator()); categoriesReportItems = new ArrayList<>(); final PieChartData.Builder builder = PieChartData.builder(); for (Pair<Category, Long> category : sortedExpenses) { final Long amount = category.second; builder.addValues(new PieChartValue(amount, category.first.getColor())); final List<Pair<Tag, Long>> tags; final Map<Tag, Long> tagExpenses = categoryTagExpenses.get(category.first); if (tagExpenses != null) { tags = new ArrayList<>(); for (Tag tag : tagExpenses.keySet()) { tags.add(Pair.create(tag, tagExpenses.get(tag))); } Collections.sort(tags, new TagExpenseComparator()); } else { tags = Collections.emptyList(); } categoriesReportItems.add(new CategoriesReportItem(category.first, amount, tags)); } pieChartData = builder.build(); } public PieChartData getPieChartData() { return pieChartData; } public int size() { return categoriesReportItems.size(); } public CategoriesReportItem get(int position) { return categoriesReportItems.get(position); } private Category createNoCategory(Context context, TransactionType transactionType) { final Category noCategory = new Category(); noCategory.setId("0"); noCategory.setTitle(context.getString(R.string.no_category)); noCategory.setColor(context.getResources().getColor(transactionType == TransactionType.Expense ? R.color.text_negative : R.color.text_positive)); return noCategory; } private boolean isTransactionValid(Transaction transaction, TransactionType transactionType) { return transaction.includeInReports() && transaction.getTransactionType() == transactionType && transaction.getTransactionState() == TransactionState.Confirmed; } private Long getAmount(Transaction transaction, Currency mainCurrency) { final Currency currency = transaction.getTransactionType() == TransactionType.Expense ? transaction.getAccountFrom().getCurrency() : transaction.getAccountTo().getCurrency(); if (currency.getId().equals(mainCurrency.getId())) { return transaction.getAmount(); } else { return Math.round(transaction.getAmount() * currency.getExchangeRate()); } } private void increaseCategoryExpenseAmount(Map<Category, Long> categoryExpenses, Category category, Long amount) { Long totalExpenseForCategory = categoryExpenses.get(category); if (totalExpenseForCategory == null) { totalExpenseForCategory = amount; } else { totalExpenseForCategory += amount; } categoryExpenses.put(category, totalExpenseForCategory); } private void increaseCategoryTagsExpenses(Map<Category, Map<Tag, Long>> categoryTagExpenses, Transaction transaction, Category category, Long amount) { final List<Tag> tags = transaction.getTags(); for (Tag tag : tags) { Map<Tag, Long> tagExpense = categoryTagExpenses.get(category); Long tagExpenseAmount; if (tagExpense == null) { tagExpense = new HashMap<>(); tagExpenseAmount = amount; } else { tagExpenseAmount = tagExpense.get(tag); if (tagExpenseAmount == null) { tagExpenseAmount = 0L; } tagExpenseAmount += amount; } tagExpense.put(tag, tagExpenseAmount); categoryTagExpenses.put(category, tagExpense); } } public static class CategoriesReportItem { private final Category category; private final long amount; private final List<Pair<Tag, Long>> tags; public CategoriesReportItem(Category category, long amount, List<Pair<Tag, Long>> tags) { this.category = category; this.amount = amount; this.tags = tags; } public Category getCategory() { return category; } public long getAmount() { return amount; } public List<Pair<Tag, Long>> getTags() { return tags; } } }