Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #794 from Vishwa-Raghavendra/PieChartScreen
Browse files Browse the repository at this point in the history
PieChartScreen Fix
  • Loading branch information
ILIYANGERMANOV authored Apr 26, 2022
2 parents acc13e5 + 7d49381 commit c622eab
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.ivy.wallet.domain.action.category

import com.ivy.fp.action.FPAction
import com.ivy.fp.action.then
import com.ivy.wallet.domain.action.transaction.CalcTrnsIncomeExpenseAct
import com.ivy.wallet.domain.data.core.Account
import com.ivy.wallet.domain.data.core.Category
import com.ivy.wallet.domain.data.core.Transaction
import com.ivy.wallet.domain.pure.data.IncomeExpensePair
import javax.inject.Inject

class CategoryIncomeWithAccountFiltersAct @Inject constructor(
val calcTrnsIncomeExpenseAct: CalcTrnsIncomeExpenseAct
) : FPAction<CategoryIncomeWithAccountFiltersAct.Input, IncomeExpensePair>() {

override suspend fun Input.compose(): suspend () -> IncomeExpensePair = {
val accountFilterSet = accountFilterList.map { it.id }.toHashSet()
transactions.filter {
it.categoryId == category?.id
}.filter {
if (accountFilterSet.isEmpty())
true
else
accountFilterSet.contains(it.accountId)
}
} then {
CalcTrnsIncomeExpenseAct.Input(
transactions = it,
baseCurrency = baseCurrency,
accounts = accountFilterList
)
} then calcTrnsIncomeExpenseAct

data class Input(
val transactions: List<Transaction>,
val accountFilterList: List<Account>,
val category: Category?,
val baseCurrency: String
)
}
108 changes: 108 additions & 0 deletions app/src/main/java/com/ivy/wallet/domain/action/charts/PieChartAct.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.ivy.wallet.domain.action.charts

import com.ivy.fp.action.FPAction
import com.ivy.fp.action.then
import com.ivy.wallet.domain.action.account.AccountsAct
import com.ivy.wallet.domain.action.category.CategoriesAct
import com.ivy.wallet.domain.action.category.CategoryIncomeWithAccountFiltersAct
import com.ivy.wallet.domain.action.transaction.CalcTrnsIncomeExpenseAct
import com.ivy.wallet.domain.action.transaction.TrnsWithRangeAndAccFiltersAct
import com.ivy.wallet.domain.data.TransactionType
import com.ivy.wallet.domain.pure.account.filterExcluded
import com.ivy.wallet.ui.onboarding.model.FromToTimeRange
import com.ivy.wallet.ui.statistic.level1.CategoryAmount
import java.util.*
import javax.inject.Inject

class PieChartAct @Inject constructor(
private val accountsAct: AccountsAct,
private val trnsWithRangeAndAccFiltersAct: TrnsWithRangeAndAccFiltersAct,
private val calcTrnsIncomeExpenseAct: CalcTrnsIncomeExpenseAct,
private val categoriesAct: CategoriesAct,
private val categoryIncomeWithAccountFiltersAct: CategoryIncomeWithAccountFiltersAct
) : FPAction<PieChartAct.Input, PieChartAct.Output>() {
override suspend fun Input.compose(): suspend () -> Output = suspend {
val allAccounts = accountsAct(Unit)
val accountsUsed = if (accountIdFilterList.isEmpty())
allAccounts.let(::filterExcluded)
else
accountIdFilterList.mapNotNull { accID ->
allAccounts.find { it.id == accID }
}
val accountIdFilterSet = accountsUsed.map { it.id }.toHashSet()

Pair(accountsUsed, accountIdFilterSet)
} then {
val accountsUsed = it.first
val accountIdFilterSet = it.second

val transactions = trnsWithRangeAndAccFiltersAct(
TrnsWithRangeAndAccFiltersAct.Input(
range = range,
accountIdFilterSet = accountIdFilterSet
)
)

Pair(accountsUsed, transactions)
} then {
val accountsUsed = it.first
val transactions = it.second

val totalAmount = asyncIo {
val incomeExpensePair = calcTrnsIncomeExpenseAct(
CalcTrnsIncomeExpenseAct.Input(
transactions = transactions,
accounts = accountsUsed,
baseCurrency = baseCurrency
)
)

when (type) {
TransactionType.INCOME -> incomeExpensePair.income.toDouble()
TransactionType.EXPENSE -> incomeExpensePair.expense.toDouble()
else -> error("not supported transactionType - $type")
}
}

val categoryAmounts = asyncIo {
val categories = categoriesAct(Unit)
categories
.plus(null) //for unspecified
.map { category ->

val catIncomeExpense = categoryIncomeWithAccountFiltersAct(
CategoryIncomeWithAccountFiltersAct.Input(
transactions = transactions,
accountFilterList = accountsUsed,
category = category,
baseCurrency = baseCurrency
)
)

CategoryAmount(
category = category,
amount = when (type) {
TransactionType.INCOME -> catIncomeExpense.income.toDouble()
TransactionType.EXPENSE -> catIncomeExpense.expense.toDouble()
else -> error("not supported transactionType - $type")
}
)
}
.filter { catAmt ->
catAmt.amount != 0.0
}
.sortedByDescending { it.amount }
}

Output(totalAmount = totalAmount.await(), categoryAmounts = categoryAmounts.await())
}

data class Input(
val baseCurrency: String,
val range: FromToTimeRange,
val type: TransactionType,
val accountIdFilterList: List<UUID>
)

data class Output(val totalAmount: Double, val categoryAmounts: List<CategoryAmount>)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.ivy.wallet.domain.action.transaction

import com.ivy.fp.action.FPAction
import com.ivy.fp.action.thenFilter
import com.ivy.wallet.domain.data.core.Transaction
import com.ivy.wallet.io.persistence.dao.TransactionDao
import com.ivy.wallet.ui.onboarding.model.FromToTimeRange
import java.util.*
import javax.inject.Inject

class TrnsWithRangeAndAccFiltersAct @Inject constructor(
private val transactionDao: TransactionDao
) : FPAction<TrnsWithRangeAndAccFiltersAct.Input, List<Transaction>>() {

override suspend fun Input.compose(): suspend () -> List<Transaction> = suspend {
transactionDao.findAllBetween(range.from(), range.to())
.map { it.toDomain() }
} thenFilter {
accountIdFilterSet.contains(it.accountId)
}

data class Input(
val range: FromToTimeRange,
val accountIdFilterSet: Set<UUID>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ suspend fun exchangeInCurrency(
return exchange(
ExchangeData(
baseCurrency = baseCurrency,
fromCurrency = trnCurrency(transaction, accounts),
fromCurrency = trnCurrency(transaction, accounts, baseCurrency),
toCurrency = toCurrency
),
transaction.amount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ fun isOverdue(transaction: Transaction, timeNowUTC: LocalDateTime): Boolean =
fun trnCurrency(
transaction: Transaction,
accounts: List<Account>
): Option<String> = accounts.find { it.id == transaction.accountId }?.currency.toOption()
): Option<String> = accounts.find { it.id == transaction.accountId }?.currency.toOption()

@Pure
fun trnCurrency(
transaction: Transaction,
accounts: List<Account>,
baseCurrency: String
): Option<String> =
((accounts.find { it.id == transaction.accountId }?.currency) ?: baseCurrency).toOption()
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.ui.graphics.toArgb
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ivy.wallet.R
import com.ivy.wallet.domain.action.charts.PieChartAct
import com.ivy.wallet.domain.data.TransactionType
import com.ivy.wallet.domain.data.core.Category
import com.ivy.wallet.domain.data.core.Transaction
Expand Down Expand Up @@ -31,7 +32,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import java.util.*
import javax.inject.Inject
import kotlin.math.absoluteValue

@HiltViewModel
class PieChartStatisticViewModel @Inject constructor(
Expand All @@ -40,7 +40,8 @@ class PieChartStatisticViewModel @Inject constructor(
private val settingsDao: SettingsDao,
private val transactionDao: TransactionDao,
private val exchangeRatesLogic: ExchangeRatesLogic,
private val ivyContext: IvyWalletCtx
private val ivyContext: IvyWalletCtx,
private val pieChartAct: PieChartAct
) : ViewModel() {
private val _period = MutableStateFlow(ivyContext.selectedPeriod)
val period = _period.readOnly()
Expand Down Expand Up @@ -112,7 +113,7 @@ class PieChartStatisticViewModel @Inject constructor(
load(
period = period,
type = type,
accountFilterList = accountList
accountIdFilterList = accountList
)
}

Expand Down Expand Up @@ -193,7 +194,7 @@ class PieChartStatisticViewModel @Inject constructor(
private fun load(
period: TimePeriod,
type: TransactionType,
accountFilterList: List<UUID>
accountIdFilterList: List<UUID>
) {

_period.value = period
Expand All @@ -202,81 +203,21 @@ class PieChartStatisticViewModel @Inject constructor(

_selectedCategory.value = null

viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
val settings = ioThread { settingsDao.findFirst() }

_baseCurrencyCode.value = settings.currency

_totalAmount.value = ioThread {
when (type) {
TransactionType.INCOME -> {
//TODO: Fix that
// calculateWalletIncomeWithAccountFilters(
// walletDAOs = walletDAOs,
// baseCurrencyCode = baseCurrencyCode.value,
// range = range.toCloseTimeRange(),
// accountIdFilterList = accountFilterList,
// filterExcluded = filterExcluded
// ).value.toDouble()
0.0
}
TransactionType.EXPENSE -> {
//TODO: Fix that
// calculateWalletExpenseWithAccountFilters(
// walletDAOs = walletDAOs,
// baseCurrencyCode = baseCurrencyCode.value,
// range = range.toCloseTimeRange(),
// accountIdFilterList = accountFilterList,
// filterExcluded = filterExcluded
// ).value.toDouble()
0.0
}
else -> error("not supported transactionType - $type")
}
}.absoluteValue

_categoryAmounts.value = scopedIOThread { scope ->

val categories =
getCategories(
fetchCategoriesFromTransactions = accountFilterList.isNotEmpty(),
timeRange = range
)

categories
.plus(null) //for unspecified
.map { category ->
CategoryAmount(
category = category,
amount = when (type) {
TransactionType.INCOME -> {
//TODO: Fix that
// calculateCategoryIncomeWithAccountFilters(
// walletDAOs = walletDAOs,
// baseCurrencyCode = baseCurrencyCode.value,
// categoryId = category?.id,
// accountIdFilterList = accountFilterList,
// range = range.toCloseTimeRange()
// ).toDouble()
0.0
}
TransactionType.EXPENSE -> {
//TODO: Fix that
// calculateCategoryExpenseWithAccountFilters(
// walletDAOs = walletDAOs,
// baseCurrencyCode = baseCurrencyCode.value,
// categoryId = category?.id,
// accountIdList = accountFilterList,
// range = range.toCloseTimeRange()
// ).toDouble()
0.0
}
else -> error("not supported transactionType - $type")
}
)
}
.sortedByDescending { it.amount }
}
val pieChartActOutput = pieChartAct(
PieChartAct.Input(
baseCurrency = _baseCurrencyCode.value,
range = range,
type = _type.value,
accountIdFilterList = accountIdFilterList
)
)

_totalAmount.value = pieChartActOutput.totalAmount
_categoryAmounts.value = pieChartActOutput.categoryAmounts
}
}

Expand All @@ -302,7 +243,7 @@ class PieChartStatisticViewModel @Inject constructor(
load(
period = period,
type = type.value,
accountFilterList = accountIdFilterList.value
accountIdFilterList = accountIdFilterList.value
)
}

Expand All @@ -313,7 +254,7 @@ class PieChartStatisticViewModel @Inject constructor(
load(
period = month.incrementMonthPeriod(ivyContext, 1L, year),
type = type.value,
accountFilterList = accountIdFilterList.value
accountIdFilterList = accountIdFilterList.value
)
}
}
Expand All @@ -325,7 +266,7 @@ class PieChartStatisticViewModel @Inject constructor(
load(
period = month.incrementMonthPeriod(ivyContext, -1L, year),
type = type.value,
accountFilterList = accountIdFilterList.value
accountIdFilterList = accountIdFilterList.value
)
}
}
Expand Down
7 changes: 7 additions & 0 deletions ivy-fp/src/main/java/com/ivy/fp/action/Action.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.ivy.fp.action

import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext

abstract class Action<in I, out O> {
Expand All @@ -14,6 +16,11 @@ abstract class Action<in I, out O> {
return@withContext action()
}

protected suspend fun <T> asyncIo(action: suspend () -> T): Deferred<T> =
withContext(Dispatchers.IO) {
return@withContext this.async { action() }
}

protected suspend fun <T> computation(action: suspend () -> T): T =
withContext(Dispatchers.Default) {
return@withContext action()
Expand Down

0 comments on commit c622eab

Please sign in to comment.