diff --git a/nummi/history/templates/history/plot.html b/nummi/history/templates/history/plot.html index 399bc4e..8d9bad4 100644 --- a/nummi/history/templates/history/plot.html +++ b/nummi/history/templates/history/plot.html @@ -18,57 +18,23 @@ - {% for y in history.years reversed %} - {% for date in y.d reversed %} - {% ifchanged %} - {% if date %} - - - - - - {% if date.has_transactions %} - {% if account %} - {{ date.month|date:"Y-m" }} - {% elif category %} - {{ date.month|date:"Y-m" }} - {% else %} - {{ date.month|date:"Y-m" }} - {% endif %} - {% else %} - {{ date.month|date:"Y-m" }} - {% endif %} - - {{ date.sum_m|pmrvalue }} - - {% if date.sum_m %} -
- {% endif %} - {% if date.sum < 0 %} -
- {{ date.sum|pmrvalue }} -
- {% endif %} - - - {% if date.sum_p %} -
- {% endif %} - {% if date.sum > 0 %} -
- {{ date.sum|pmrvalue }} -
- {% endif %} - - {{ date.sum_p|pmrvalue }} - - {% else %} - - - - {% endif %} - {% endifchanged %} - {% endfor %} + {% for date in history.data reversed %} + {% ifchanged %} + {% if date.sum_m or date.sum_p %} + + {% up_down_icon date.sum %} + {% month_url date.month account=account category=category %} + {{ date.sum_m|pmrvalue }} + {% plot_bar date.sum date.sum_m history.max.pm %} + {% plot_bar date.sum date.sum_p history.max.pm %} + {{ date.sum_p|pmrvalue }} + + {% else %} + + + + {% endif %} + {% endifchanged %} {% endfor %} @@ -78,36 +44,26 @@ {% translate "Year" %} - 01 - 02 - 03 - 04 - 05 - 06 - 07 - 08 - 09 - 10 - 11 - 12 + {% calendar_head %} - {% for year in history.years reversed %} + {% regroup history.data by month.year as years_list %} + {% for y, year in years_list reversed %} - {{ year.y }} - {% for m in year.d %} + {{ y }} + {% for m in year %} {% if forloop.parentloop.last and forloop.first %} - {% for _ in history.offset.0 %}{% endfor %} + {% empty_calendar_cells_start m.month.month %} {% endif %} {% if m %} + style="opacity: {% opacity m.sum history.max.sum %}"> {% else %} {% endif %} {% if forloop.parentloop.first and forloop.last %} - {% for _ in history.offset.1 %}{% endfor %} + {% empty_calendar_cells_end m.month.month %} {% endif %} {% endfor %} diff --git a/nummi/history/utils.py b/nummi/history/utils.py index b15e792..423d34e 100644 --- a/nummi/history/utils.py +++ b/nummi/history/utils.py @@ -1,6 +1,6 @@ import datetime -from django.db.models import Func, Max, Min, Q, Sum, Value +from django.db.models import Func, Max, Min, Q, Sum from django.db.models.functions import Abs, TruncMonth @@ -23,34 +23,30 @@ def history(transaction_set): sum_p=Sum("value", filter=Q(value__gt=0)), sum_m=Sum("value", filter=Q(value__lt=0)), sum=Sum("value"), - has_transactions=Value(1), ).order_by("-month") _data = [ - { - "y": y, - "d": [ - _history.filter(month=datetime.date(y, m + 1, 1)).first() - for m in range( - _first_month.month - 1 if _first_month.year == y else 0, - _last_month.month if _last_month.year == y else 12, - ) - ], - } + _history.filter(month=datetime.date(y, m + 1, 1)).first() + or {"month": datetime.date(y, m + 1, 1), "sum": 0} for y in range( _first_month.year, _last_month.year + 1, ) + for m in range( + _first_month.month - 1 if _first_month.year == y else 0, + _last_month.month if _last_month.year == y else 12, + ) ] return { - "years": _data, - "offset": [range(_first_month.month - 1), range(12 - _last_month.month)], - "max": max( - _history.aggregate( - max=Max("sum_p", default=0), - min=-Min("sum_m", default=0), - ).values(), - ), - "years_max": _history.aggregate(max=Max(Abs("sum")))["max"], + "data": _data, + "max": { + "pm": max( + _history.aggregate( + max=Max("sum_p", default=0), + min=-Min("sum_m", default=0), + ).values(), + ), + "sum": _history.aggregate(max=Max(Abs("sum")))["max"], + }, } diff --git a/nummi/main/static/main/css/plot.css b/nummi/main/static/main/css/plot.css index 631d3c4..0ec7189 100644 --- a/nummi/main/static/main/css/plot.css +++ b/nummi/main/static/main/css/plot.css @@ -65,12 +65,17 @@ table.full-width col.bar { } } } - tr.empty { - height: 0.5rem; - } - tr.even { - background: #eeeeff; + + tbody tr { + background: initial; + &.empty { + height: 0.5rem; + } + &.even { + background: #eeeeff; + } } + tfoot { background: var(--bg-01); } @@ -78,22 +83,23 @@ table.full-width col.bar { .calendar { margin-top: var(--gap); + font-feature-settings: var(--num); - tbody tr { - background: initial; - } .p { background: var(--green); } .m { background: var(--red); } - tbody tr { - &:not(:last-child) { - border-bottom: none; - } - &:not(:first-child) { - border-top: none; + table { + tbody tr { + background: initial; + &:not(:last-child) { + border-bottom: none; + } + &:not(:first-child) { + border-top: none; + } } } } diff --git a/nummi/main/templatetags/main_extras.py b/nummi/main/templatetags/main_extras.py index 670bda9..8fca38e 100644 --- a/nummi/main/templatetags/main_extras.py +++ b/nummi/main/templatetags/main_extras.py @@ -1,7 +1,10 @@ +import datetime import math from django import template +from django.template.defaultfilters import date from django.templatetags.static import static +from django.urls import reverse from django.utils import formats from django.utils.safestring import mark_safe @@ -73,4 +76,68 @@ def css(href): @register.simple_tag def opacity(v, vmax): - return f"{math.sin(math.fabs(v/vmax)*math.pi/2):.2f}" + return f"{math.sin(math.fabs(v/vmax)*math.pi/2):.3f}" + + +@register.simple_tag +def empty_calendar_cells(n): + return mark_safe(n * "") + + +@register.simple_tag +def empty_calendar_cells_start(n): + return empty_calendar_cells(n - 1) + + +@register.simple_tag +def empty_calendar_cells_end(n): + return empty_calendar_cells(12 - n) + + +@register.simple_tag +def up_down_icon(val): + if val > 0: + return mark_safe("""""") + elif val < 0: + return mark_safe("""""") + + +@register.simple_tag +def month_url(month, account=None, category=None): + url_name = "transaction_month" + url_params = {"year": month.year, "month": month.month} + + if account: + url_name = "account_" + url_name + url_params |= {"account": account.pk} + elif category: + url_name = "category_" + url_name + url_params |= {"category": category.pk} + + url = reverse(url_name, kwargs=url_params) + return mark_safe(f"""{ date(month, "Y-m") }""") + + +@register.simple_tag +def plot_bar(s, sum_pm, s_max): + _res = "" + + if sum_pm: + _w = abs(sum_pm / s_max) + _res += f"""
""" + if sum_pm is not None and s * sum_pm > 0: + _w = abs(s / s_max) + _res += ( + f"""
""" + f"""{pmrvalue(s)}
""" + ) + + return mark_safe(_res) + + +@register.simple_tag +def calendar_head(): + months = (datetime.date(1, m + 1, 1) for m in range(12)) + th = (f"""{date(month, "m")}""" for month in months) + + return mark_safe("".join(th))