Update graphics
This commit is contained in:
parent
d9abdf798f
commit
73ce1a298e
4 changed files with 110 additions and 63 deletions
40
nummi/main/static/main/css/chart.css
Normal file
40
nummi/main/static/main/css/chart.css
Normal 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)}
|
|
@ -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>
|
||||||
|
|
|
@ -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"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue