Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions cli/src/main/scala/co/uproot/abandon/web/WebAPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ object WebAPI {

def getDetailsByMonth(accName: AccountName): Array[Map[String, Any]] = {
val name = accName.fullPathStr
val gPosts = currPosts.filter(_.name == name)
val gPosts = groupedPosts.getOrElse(name, Nil)
val openingBalance = openingBalances.getOrElse(name, Zero)
val closingBalance = openingBalance + sumDeltas(gPosts)
val months = gPosts.map(p => Date(p.date.year, p.date.month, 1)).toSet.toArray
Expand Down Expand Up @@ -187,41 +187,45 @@ object WebAPI {
updateAccTxnReport(accTree)

def makeMonthlyRegister(): Array[Map[String, Any]] = {
val months = currPosts.map(p => Date(p.date.year, p.date.month, 1)).toSet.toArray
val postsByMonth = currPosts.groupBy(p => Date(p.date.year, p.date.month, 1))
val months = postsByMonth.keys.toArray.sortBy(_.toInt)

months.sortBy(_.toInt).map(mDate =>
val currMonthPosts = currPosts.filter(p => p.date.month == mDate.month && p.date.year == mDate.year)
val runningBalances = scala.collection.mutable.Map[String, BigDecimal]()
openingBalances.foreach { case (k, v) => runningBalances(k) = v }

months.map(mDate => {
val currMonthPosts = postsByMonth(mDate)
val closingBalance = sumDeltas(currMonthPosts)
assert(closingBalance == Zero)

val (debitPosts, creditPosts) = currMonthPosts.partition(_.delta < Zero)
val debitSubTotal = sumDeltas(debitPosts)
val creditSubTotal = sumDeltas(creditPosts)

val accountSummaries = currMonthPosts.groupBy(_.name).toArray.sortBy(_._1).map((name, posts) =>
val priorPosts = currPosts.filter(p => p.date.toIntYYYYMM < mDate.toIntYYYYMM && p.name == name)
val accOpeningBalance = openingBalances.getOrElse(name, Zero) + sumDeltas(priorPosts)
val accountSummaries = currMonthPosts.groupBy(_.name).toArray.sortBy(_._1).map((name, posts) => {
val accOpeningBalance = runningBalances.getOrElse(name, Zero)
val (accDebitPosts, accCreditPosts) = posts.partition(_.delta < Zero)
val accDebitSubTotal = sumDeltas(accDebitPosts)
val accCreditSubTotal = sumDeltas(accCreditPosts)
val accClosingBalance = accOpeningBalance + accDebitSubTotal + accCreditSubTotal
runningBalances(name) = accClosingBalance
Map(
"name" -> name,
"opening" -> accOpeningBalance,
"debit" -> accDebitSubTotal,
"credit" -> accCreditSubTotal,
"closing" -> accClosingBalance,
)
)
})
Map(
"month" -> s"${Helper.getShortMonth(mDate.month)} ${mDate.year}",
"opening" -> Zero,
"closing" -> closingBalance,
"debit" -> debitSubTotal,
"credit" -> creditSubTotal,
"accountSummaries" -> accountSummaries
)
"month" -> s"${Helper.getShortMonth(mDate.month)} ${mDate.year}",
"opening" -> Zero,
"closing" -> closingBalance,
"debit" -> debitSubTotal,
"credit" -> creditSubTotal,
"accountSummaries" -> accountSummaries
)
})
}

val fullReport = Map(
Expand Down
57 changes: 57 additions & 0 deletions cli/src/test/scala/co/uproot/abandon/web/WebAPIBenchmarkTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package co.uproot.abandon.web

import co.uproot.abandon._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class WebAPIBenchmarkTest extends AnyFlatSpec with Matchers {

def buildBenchmarkAppState(numMonths: Int, numAccounts: Int, postsPerMonth: Int): AppState = {
val accState = new AccountState()
val appState = AppState(accState)

for {
m <- 1 to numMonths
year = 2020 + (m - 1) / 12
month = ((m - 1) % 12) + 1
p <- 1 to postsPerMonth
} {
val day = (p % 28) + 1
val date = Date(year, month, day)
val accIdx = p % numAccounts
val acc1 = AccountName(Seq("Expenses", s"Category$accIdx"))
val acc2 = AccountName(Seq("Assets", "Bank"))

val amount = BigDecimal(p * 10)
val dp1 = DetailedPost(acc1, amount, Some(s"Expense $p"))
val dp2 = DetailedPost(acc2, -amount, Some(s"Payment $p"))

val txn = Transaction(null, date, Nil, None, Some("Payee"), Nil)
val pg = new PostGroup(Seq(dp1, dp2), txn, date, None, Some("Payee"), Nil)
accState.updateAmounts(pg)
}

appState
}

"WebAPI.makeReport" should "execute efficiently on standard datasets" in {
val startDate = Date(2020, 1, 1)
val appState = buildBenchmarkAppState(numMonths = 24, numAccounts = 50, postsPerMonth = 200)

// Warmup
for (_ <- 1 to 5) {
WebAPI.makeReport(startDate, appState, None, showInactiveAccounts = true)
}

// Benchmark run
val runs = 10
val start = System.currentTimeMillis()
for (_ <- 1 to runs) {
val report = WebAPI.makeReport(startDate, appState, None, showInactiveAccounts = true)
report.length should be > 0
}
val elapsed = System.currentTimeMillis() - start
val avgMs = elapsed.toDouble / runs
println(s"[BENCHMARK] Average WebAPI.makeReport execution time: ${avgMs} ms over $runs runs")
}
}
Loading