Refactor History plot
This commit is contained in:
parent
6b50de5e35
commit
0940904cd8
4 changed files with 130 additions and 105 deletions
|
@ -18,57 +18,23 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for y in history.years reversed %}
|
||||
{% for date in y.d reversed %}
|
||||
{% ifchanged %}
|
||||
{% if date %}
|
||||
<tr {% if not date.month.month|divisibleby:"2" %}class="even"{% endif %}>
|
||||
<td class="icon">
|
||||
<span class="ri-{% if date.sum > 0 %}arrow-up-s-line green{% elif date.sum < 0 %}arrow-down-s-line red{% endif %}"></span>
|
||||
</td>
|
||||
<th class="date" scope="row">
|
||||
{% if date.has_transactions %}
|
||||
{% if account %}
|
||||
<a href="{% url "account_transaction_month" account=account.pk year=date.month.year month=date.month.month %}">{{ date.month|date:"Y-m" }}</a>
|
||||
{% elif category %}
|
||||
<a href="{% url "category_transaction_month" category=category.pk year=date.month.year month=date.month.month %}">{{ date.month|date:"Y-m" }}</a>
|
||||
{% else %}
|
||||
<a href="{% url "transaction_month" year=date.month.year month=date.month.month %}">{{ date.month|date:"Y-m" }}</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ date.month|date:"Y-m" }}
|
||||
{% endif %}
|
||||
</th>
|
||||
<td class="value">{{ date.sum_m|pmrvalue }}</td>
|
||||
<td class="bar m">
|
||||
{% if date.sum_m %}
|
||||
<div style="width: {% widthratio date.sum_m history.max -100 %}%"></div>
|
||||
{% endif %}
|
||||
{% if date.sum < 0 %}
|
||||
<div class="tot" style="width:{% widthratio date.sum history.max -100 %}%">
|
||||
<span>{{ date.sum|pmrvalue }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="bar p">
|
||||
{% if date.sum_p %}
|
||||
<div style="width: {% widthratio date.sum_p history.max 100 %}%"></div>
|
||||
{% endif %}
|
||||
{% if date.sum > 0 %}
|
||||
<div class="tot" style="width:{% widthratio date.sum history.max 100 %}%">
|
||||
<span>{{ date.sum|pmrvalue }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="value">{{ date.sum_p|pmrvalue }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="empty">
|
||||
<td colspan="6" class="empty"></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endifchanged %}
|
||||
{% endfor %}
|
||||
{% for date in history.data reversed %}
|
||||
{% ifchanged %}
|
||||
{% if date.sum_m or date.sum_p %}
|
||||
<tr {% if not date.month.month|divisibleby:"2" %}class="even"{% endif %}>
|
||||
<td class="icon">{% up_down_icon date.sum %}</td>
|
||||
<th class="date" scope="row">{% month_url date.month account=account category=category %}</th>
|
||||
<td class="value">{{ date.sum_m|pmrvalue }}</td>
|
||||
<td class="bar m">{% plot_bar date.sum date.sum_m history.max.pm %}</td>
|
||||
<td class="bar p">{% plot_bar date.sum date.sum_p history.max.pm %}</td>
|
||||
<td class="value">{{ date.sum_p|pmrvalue }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="empty">
|
||||
<td colspan="6" class="empty"></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endifchanged %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -78,36 +44,26 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% translate "Year" %}</th>
|
||||
<th scope="col">01</th>
|
||||
<th scope="col">02</th>
|
||||
<th scope="col">03</th>
|
||||
<th scope="col">04</th>
|
||||
<th scope="col">05</th>
|
||||
<th scope="col">06</th>
|
||||
<th scope="col">07</th>
|
||||
<th scope="col">08</th>
|
||||
<th scope="col">09</th>
|
||||
<th scope="col">10</th>
|
||||
<th scope="col">11</th>
|
||||
<th scope="col">12</th>
|
||||
{% calendar_head %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for year in history.years reversed %}
|
||||
{% regroup history.data by month.year as years_list %}
|
||||
{% for y, year in years_list reversed %}
|
||||
<tr>
|
||||
<th>{{ year.y }}</th>
|
||||
{% for m in year.d %}
|
||||
<th>{{ y }}</th>
|
||||
{% for m in year %}
|
||||
{% if forloop.parentloop.last and forloop.first %}
|
||||
{% for _ in history.offset.0 %}<td></td>{% endfor %}
|
||||
{% empty_calendar_cells_start m.month.month %}
|
||||
{% endif %}
|
||||
{% if m %}
|
||||
<td class="{% if m.sum > 0 %}p{% else %}m{% endif %}"
|
||||
style="opacity: {% opacity m.sum history.years_max %}"></td>
|
||||
style="opacity: {% opacity m.sum history.max.sum %}"></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% if forloop.parentloop.first and forloop.last %}
|
||||
{% for _ in history.offset.1 %}<td></td>{% endfor %}
|
||||
{% empty_calendar_cells_end m.month.month %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
|
|
@ -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"],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 * "<td></td>")
|
||||
|
||||
|
||||
@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("""<span class="ri-arrow-up-s-line green"></span>""")
|
||||
elif val < 0:
|
||||
return mark_safe("""<span class="ri-arrow-down-s-line red"></span>""")
|
||||
|
||||
|
||||
@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"""<a href="{url}">{ date(month, "Y-m") }</a>""")
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def plot_bar(s, sum_pm, s_max):
|
||||
_res = ""
|
||||
|
||||
if sum_pm:
|
||||
_w = abs(sum_pm / s_max)
|
||||
_res += f"""<div style="width: {_w:.1%}"></div>"""
|
||||
if sum_pm is not None and s * sum_pm > 0:
|
||||
_w = abs(s / s_max)
|
||||
_res += (
|
||||
f"""<div class="tot" style="width: {_w:.1%}">"""
|
||||
f"""<span>{pmrvalue(s)}</span></div>"""
|
||||
)
|
||||
|
||||
return mark_safe(_res)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def calendar_head():
|
||||
months = (datetime.date(1, m + 1, 1) for m in range(12))
|
||||
th = (f"""<th>{date(month, "m")}</th>""" for month in months)
|
||||
|
||||
return mark_safe("".join(th))
|
||||
|
|
Loading…
Reference in a new issue