Analytics & Dashboard Logic
Relevant source files
The following files were used as context for generating this wiki page:
- app/src/main/java/io/github/mojri/hesabyar/domain/usecase/GetAnalyticsUseCase.kt
- app/src/main/java/io/github/mojri/hesabyar/domain/usecase/GetDashboardDataUseCase.kt
- rust/hesabyar-core/src/analytics.rs
- rust/hesabyar-core/src/dashboard.rs
- app/src/main/java/io/github/mojri/hesabyar/rust/RustBridge.kt
- rust/hesabyar-core/src/ffi/mod.rs
The Analytics and Dashboard system in Hesabyar is responsible for aggregating financial data, calculating key performance indicators (KPIs), and generating insights based on transactions, loans, and installments. The logic is primarily implemented in a high-performance Rust Core (hesabyar-core), which is accessed by the Android Kotlin layer via a UniFFI bridge.
This system ensures that complex financial calculations—such as Jalali month boundary detection, debt-to-income ratios, and category spending breakdowns—are performed consistently and efficiently across different platforms.
Sources: README.md, rust/hesabyar-core/README.md, app/src/main/java/io/github/mojri/hesabyar/rust/RustBridge.kt:180-189
High-Level Architecture
The architecture follows a bridge pattern where the Android UI requests data through Use Cases, which in turn call the Rust native library. If the Rust library is unavailable or encounters an error, a Kotlin-based fallback logic is executed to ensure the UI remains functional.
The diagram shows the request flow from the Android UI to the Rust Core, including the fallback mechanism.
Sources: app/src/main/java/io/github/mojri/hesabyar/domain/usecase/GetDashboardDataUseCase.kt:33-45, app/src/main/java/io/github/mojri/hesabyar/rust/RustBridge.kt:28-40
Dashboard Logic
The dashboard provides a snapshot of the user's current financial health. It aggregates data specifically for the current Jalali month, rather than a rolling window, to align with typical monthly budgeting cycles.
Key Metrics Calculated
| Metric | Description | Source |
|---|---|---|
| Current Balance | Lifetime total income minus lifetime total expenses. | rust/hesabyar-core/src/dashboard.rs:56-68 |
| Monthly Income | Total income transactions within the current Jalali month. | rust/hesabyar-core/src/dashboard.rs:58-61 |
| Monthly Expenses | Total expense transactions within the current Jalali month. | rust/hesabyar-core/src/dashboard.rs:64-67 |
| Savings Rate | Calculated as (Income - Expenses) / Income, clamped between 0.0 and 1.0. | rust/hesabyar-core/src/dashboard.rs:77-81 |
| Debt to Income | Ratio of monthly debt obligations (installments + prorated loans) to income. | rust/hesabyar-core/src/dashboard.rs:93-97 |
Fallback Mechanism
When the Rust FFI is unavailable, the GetDashboardDataUseCase utilizes computeFallbackDashboardData. This Kotlin implementation replicates the Rust logic, including calculating Jalali month boundaries in UTC to ensure data consistency.
Sources: app/src/main/java/io/github/mojri/hesabyar/domain/usecase/GetDashboardDataUseCase.kt:65-120
Analytics Engine
The Analytics engine processes historical data to identify spending patterns, category distributions, and loan progress.
Processing Pipeline
- Jalali Aggregation: Transactions are grouped by Jalali year and month. Labels include the number of days in the specific month (e.g., "1403/1 (31 days)").
- Category Breakdown: Expenses are grouped by
category_id. The engine calculates the total for each and its percentage relative to total monthly spending. - Debt Progress: For each unsettled loan, progress is calculated as
(Original - Remaining) / Original.
This flowchart illustrates the internal transformation of raw data into structured analytics within the Rust core.
Sources: rust/hesabyar-core/src/analytics.rs:13-110
Analytics Data Structure
The compute_analytics function returns a comprehensive AnalyticsData struct:
| Field | Type | Description |
|---|---|---|
monthly_spending | Vec<MonthlyData> | History of expenses by Jalali month. |
category_breakdown | Vec<CategoryBreakdown> | Expenses by category, sorted descending by total. |
debtors / creditors | Vec<DebtSummary> | List of unsettled loans with calculated progress. |
total_installments | i32 | Count of all installments. |
paid_installments | i32 | Count of installments marked as paid. |
Sources: rust/hesabyar-core/src/analytics.rs:112-123, rust/uniffi/hesabyar_core/uniffi/hesabyar_core/hesabyar_core.kt:1010-1022
Data Flow and Threading
The Kotlin layer manages the threading and lifecycle of these requests. The RustBridge object dispatches asynchronous calls to Dispatchers.Default to prevent blocking the UI thread during heavy computation.
The sequence diagram shows the synchronous FFI call wrapped in the Use Case logic.
Sources: app/src/main/java/io/github/mojri/hesabyar/rust/RustBridge.kt:33-40, app/src/main/java/io/github/mojri/hesabyar/domain/usecase/GetAnalyticsUseCase.kt:47-60
Error Handling and Safety
The Rust Core employs a "panic-safe" boundary. Using catch_unwind_safe, the FFI layer intercepts Rust panics and converts them into HesabyarError or returns None/default values. This prevents native crashes from terminating the Android process.
Sources: rust/hesabyar-core/src/ffi/mod.rs:37-56, rust/hesabyar-core/src/ffi/mod.rs:125-134
Implementation Example: Analytics FFI Wrapper
#[uniffi::export]
pub fn compute_analytics(
transactions: Vec<Transaction>,
loans: Vec<Loan>,
installments: Vec<Installment>,
categories: Vec<Category>,
) -> Option<AnalyticsData> {
catch_unwind_safe(|| {
crate::analytics::compute_analytics(&transactions, &loans, &installments, &categories)
})
.ok()
}
Sources: rust/hesabyar-core/src/ffi/mod.rs:125-134
Summary
The Analytics & Dashboard Logic serves as the financial intelligence center of Hesabyar. By offloading heavy aggregation and Jalali calendar logic to a shared Rust core, the project achieves high performance and mathematical consistency. The system is resilient, featuring a robust Kotlin fallback mechanism and panic-safe FFI boundaries to ensure a stable user experience.