From 1b2a77aaf3b0da4f5615877c189015a86ba1d8d3 Mon Sep 17 00:00:00 2001 From: Herculino Trotta Date: Tue, 24 Dec 2024 02:36:26 -0300 Subject: [PATCH] feat(monthly-view): add percentage bar --- app/apps/monthly_overview/views.py | 7 +- app/apps/transactions/utils/calculations.py | 88 +++++++++++++++++++ .../fragments/monthly_summary.html | 33 +++++++ 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/app/apps/monthly_overview/views.py b/app/apps/monthly_overview/views.py index 1ab8846..0289a4d 100644 --- a/app/apps/monthly_overview/views.py +++ b/app/apps/monthly_overview/views.py @@ -13,7 +13,10 @@ from apps.monthly_overview.utils.daily_spending_allowance import ( ) from apps.transactions.filters import TransactionsFilter from apps.transactions.models import Transaction -from apps.transactions.utils.calculations import calculate_currency_totals +from apps.transactions.utils.calculations import ( + calculate_currency_totals, + calculate_percentage_distribution, +) from apps.transactions.utils.default_ordering import default_order @@ -98,6 +101,7 @@ def monthly_summary(request, month: int, year: int): ).exclude(Q(category__mute=True) & ~Q(category=None)) data = calculate_currency_totals(base_queryset, ignore_empty=True) + percentages = calculate_percentage_distribution(data) context = { "income_current": remove_falsey_entries(data, "income_current"), @@ -110,6 +114,7 @@ def monthly_summary(request, month: int, year: int): "daily_spending_allowance": calculate_daily_allowance_currency( currency_totals=data, month=month, year=year ), + "percentages": percentages, } return render( diff --git a/app/apps/transactions/utils/calculations.py b/app/apps/transactions/utils/calculations.py index 5241388..4f9da15 100644 --- a/app/apps/transactions/utils/calculations.py +++ b/app/apps/transactions/utils/calculations.py @@ -157,6 +157,94 @@ def calculate_currency_totals(transactions_queryset, ignore_empty=False): return result +def calculate_percentage_distribution(currency_totals): + """ + Calculate percentage distribution of financial metrics for each currency. + Returns a new dictionary with currency IDs as keys and percentage distributions. + """ + percentages = {} + + for currency_id, data in currency_totals.items(): + # Calculate total volume of transactions + total_volume = sum( + [ + abs(data["income_current"]), + abs(data["income_projected"]), + abs(data["expense_current"]), + abs(data["expense_projected"]), + ] + ) + + # Initialize percentages for this currency + percentages[currency_id] = { + "currency": data["currency"], # Keep currency info for reference + "percentages": {}, + } + + # Calculate percentages if total_volume is not zero + if total_volume > 0: + percentages[currency_id]["percentages"] = { + "income_current": (abs(data["income_current"]) / total_volume) * 100, + "income_projected": (abs(data["income_projected"]) / total_volume) + * 100, + "expense_current": (abs(data["expense_current"]) / total_volume) * 100, + "expense_projected": (abs(data["expense_projected"]) / total_volume) + * 100, + } + else: + percentages[currency_id]["percentages"] = { + "income_current": 0, + "income_projected": 0, + "expense_current": 0, + "expense_projected": 0, + } + + # If there's exchanged data, calculate percentages for that too + if "exchanged" in data: + exchanged_total = sum( + [ + abs(data["exchanged"]["income_current"]), + abs(data["exchanged"]["income_projected"]), + abs(data["exchanged"]["expense_current"]), + abs(data["exchanged"]["expense_projected"]), + ] + ) + + percentages[currency_id]["exchanged"] = { + "currency": data["exchanged"]["currency"], + "percentages": {}, + } + + if exchanged_total > 0: + percentages[currency_id]["exchanged"]["percentages"] = { + "income_current": ( + abs(data["exchanged"]["income_current"]) / exchanged_total + ) + * 100, + "income_projected": ( + abs(data["exchanged"]["income_projected"]) / exchanged_total + ) + * 100, + "expense_current": ( + abs(data["exchanged"]["expense_current"]) / exchanged_total + ) + * 100, + "expense_projected": ( + abs(data["exchanged"]["expense_projected"]) / exchanged_total + ) + * 100, + } + else: + percentages[currency_id]["exchanged"]["percentages"] = { + "income_current": 0, + "income_projected": 0, + "expense_current": 0, + "expense_projected": 0, + } + + return percentages + + def calculate_account_totals(transactions_queryset, ignore_empty=False): # Prepare the aggregation expressions account_totals = transactions_queryset.values( diff --git a/app/templates/monthly_overview/fragments/monthly_summary.html b/app/templates/monthly_overview/fragments/monthly_summary.html index 72df630..ff75e81 100644 --- a/app/templates/monthly_overview/fragments/monthly_summary.html +++ b/app/templates/monthly_overview/fragments/monthly_summary.html @@ -1,5 +1,6 @@ {% load i18n %} {% load currency_display %} +
{# Daily Spending#}
@@ -281,3 +282,35 @@
+{% for p in percentages.values %} +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endfor %}