Update graphics

This commit is contained in:
Edgar P. Burkhart 2022-05-24 17:09:16 +02:00
parent d9abdf798f
commit 73ce1a298e
Signed by: edpibu
GPG key ID: 9833D3C5A25BD227
4 changed files with 110 additions and 63 deletions

View file

@ -0,0 +1,40 @@
.chart {
display: grid;
grid-template-columns: auto auto 1fr 1fr auto;
grid-gap: var(--gap) 0;
}
.chart > div {
position: relative;
height: 2rem;
line-height: 2rem;
}
.chart .left {
text-align: right;
}
.chart .bar,
.chart .value {
display: inline-block;
height: 2rem;
line-height: 2rem;
}
.chart .value {
padding: 0 var(--gap);
}
.chart .bar {
width: 0;
box-sizing: border-box;
z-index: 1;
}
.chart .bar.tot {
position: absolute;
z-index: 10;
border-top: .5rem solid black;
}
.chart .left .bar.tot {right: 0}
.chart .right .bar.tot {left: 0}
.chart .bar_m {background: var(--red)}
.chart .bar_p {background: var(--green)}

View file

@ -6,6 +6,7 @@
{{ block.super }} {{ block.super }}
<link rel="stylesheet" href="{% static 'main/css/form.css' %}" type="text/css" /> <link rel="stylesheet" href="{% static 'main/css/form.css' %}" type="text/css" />
<link rel="stylesheet" href="{% static 'main/css/table.css' %}" type="text/css" /> <link rel="stylesheet" href="{% static 'main/css/table.css' %}" type="text/css" />
<link rel="stylesheet" href="{% static 'main/css/chart.css' %}" type="text/css" />
{% endblock %} {% endblock %}
{% block body %} {% block body %}
@ -32,7 +33,54 @@
</form> </form>
<h2>Plot</h2> <h2>Plot</h2>
<img src="{% url 'snapshot_graph' snapshot.id %}" /> <div class="chart">
{% for cat in categories %}
<div class="name">
{% if cat.category %}
<i class="fa fa-{{ cat.category__icon }}"></i>
<a href="{% url 'category' cat.category %}">
{{ cat.category__name }}
</a>
{% else %}
<i class="fa fa-wallet"></i></i>
{% endif %}
</div>
<div class="value left">
{% pmvalue cat.sum_m %}
</div>
<div class="left">
<div
class="bar bar_m"
style="width:{% widthratio cat.sum_m cat_lim_m 100 %}%"
title="{{ cat.sum_m }}">
</div>
{% if cat.sum < 0 %}
<div
class="bar tot"
style="width:{% widthratio cat.sum cat_lim_m 100 %}%"
title="{{ cat.sum }}">
</div>
{% endif %}
</div>
<div class="right">
<div
class="bar bar_p"
style="width:{% widthratio cat.sum_p cat_lim 100 %}%"
title="{{ cat.sum_p }}">
</div>
{% if cat.sum >= 0 %}
<div
class="bar tot"
style="width:{% widthratio cat.sum cat_lim 100 %}%"
title="{{ cat.sum }}">
</div>
{% endif %}
</div>
<div class="value right">
{% pmvalue cat.sum_p %}
</div>
{% endfor %}
</div>
{% if snapshot.transactions %} {% if snapshot.transactions %}
<h2>Transactions ({% pmvalue sum %} / {% pmvalue snapshot.diff %})</h2> <h2>Transactions ({% pmvalue sum %} / {% pmvalue snapshot.diff %})</h2>

View file

@ -17,6 +17,5 @@ urlpatterns = [
path("category/<uuid>/del", views.del_category, name="del_category"), path("category/<uuid>/del", views.del_category, name="del_category"),
path("snapshot", views.snapshot, name="snapshot"), path("snapshot", views.snapshot, name="snapshot"),
path("snapshot/<uuid>", views.snapshot, name="snapshot"), path("snapshot/<uuid>", views.snapshot, name="snapshot"),
path("snapshot/<uuid>/graph.svg", views.snapshot_graph, name="snapshot_graph"),
path("snapshot/<uuid>/del", views.del_snapshot, name="del_snapshot"), path("snapshot/<uuid>/del", views.del_snapshot, name="del_snapshot"),
] ]

View file

@ -6,8 +6,6 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView from django.views.generic import ListView
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.db import models from django.db import models
import matplotlib.pyplot as plt
import tempfile
from .models import ( from .models import (
Transaction, Transaction,
@ -165,6 +163,27 @@ def snapshot(request, uuid=None):
"form": _form, "form": _form,
} }
context["categories"] = (
_snapshot.transactions.values("category", "category__name", "category__icon")
.annotate(
sum=models.Sum("value"),
sum_m=models.Sum("value", filter=models.Q(value__lt=0)),
sum_p=models.Sum("value", filter=models.Q(value__gt=0)),
)
.order_by("-sum")
)
context["cat_lim"] = max(
map(
abs,
context["categories"]
.aggregate(
max=models.Max("sum_p"),
min=models.Min("sum_m"),
)
.values(),
)
)
context["cat_lim_m"] = -context["cat_lim"]
return render( return render(
request, request,
"main/snapshot.html", "main/snapshot.html",
@ -172,65 +191,6 @@ def snapshot(request, uuid=None):
) )
@login_required
def snapshot_graph(request, uuid):
_snapshot = get_object_or_404(Snapshot, id=uuid)
_categories_p = (
_snapshot.transactions.filter(value__gt=0)
.values("category")
.annotate(sum=models.Sum("value"))
)
_categories_m = (
_snapshot.transactions.filter(value__lt=0)
.values("category")
.annotate(sum=models.Sum("value"))
)
_categories = (
_snapshot.transactions.values("category")
.annotate(sum=models.Sum("value"))
.order_by("-sum")
)
print(_categories_p)
print(_categories_m)
fig, ax = plt.subplots(constrained_layout=True)
ax.barh(
[
"*" if (c := _cat["category"]) is None else Category.objects.get(id=c).name
for _cat in _categories
],
[_cat["sum"] for _cat in _categories],
hatch="/",
fill=False,
zorder=10,
)
ax.barh(
[
"*" if (c := _cat["category"]) is None else Category.objects.get(id=c).name
for _cat in _categories_p
],
[_cat["sum"] for _cat in _categories_p],
color="#007339",
zorder=9,
)
ax.barh(
[
"*" if (c := _cat["category"]) is None else Category.objects.get(id=c).name
for _cat in _categories_m
],
[_cat["sum"] for _cat in _categories_m],
color="#bf1500",
zorder=9,
)
ax.grid(color="k", alpha=0.2)
ax.set(xlabel="Value (€)")
# fig.tight_layout()
with tempfile.NamedTemporaryFile(suffix=".svg") as f:
fig.savefig(f.name)
f.seek(0)
return HttpResponse(f.read(), content_type="image/svg+xml")
@login_required @login_required
def del_snapshot(request, uuid): def del_snapshot(request, uuid):
_snapshot = get_object_or_404(Snapshot, id=uuid) _snapshot = get_object_or_404(Snapshot, id=uuid)