Compare commits
No commits in common. "fdc9214b10cdf61361cf257499e1142dcbd6e56e" and "ee7e6e60a788ab37fbad3b085da7d87c854c5d78" have entirely different histories.
fdc9214b10
...
ee7e6e60a7
22 changed files with 301 additions and 300 deletions
Binary file not shown.
|
@ -7,7 +7,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: 0.0.1\n"
|
"Project-Id-Version: 0.0.1\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-04-17 18:15+0200\n"
|
"POT-Creation-Date: 2023-01-01 10:02+0100\n"
|
||||||
"PO-Revision-Date: 2022-12-21 17:30+0100\n"
|
"PO-Revision-Date: 2022-12-21 17:30+0100\n"
|
||||||
"Last-Translator: edpibu <git@edgarpierre.fr>\n"
|
"Last-Translator: edpibu <git@edgarpierre.fr>\n"
|
||||||
"Language-Team: edpibu <git@edgarpierre.fr>\n"
|
"Language-Team: edpibu <git@edgarpierre.fr>\n"
|
||||||
|
@ -21,28 +21,20 @@ msgstr ""
|
||||||
msgid "Add transactions"
|
msgid "Add transactions"
|
||||||
msgstr "Ajouter des transactions"
|
msgstr "Ajouter des transactions"
|
||||||
|
|
||||||
#: .\main\forms.py:97 .\main\templates\main\base.html:59
|
|
||||||
#: .\main\templates\main\form\search.html:5
|
|
||||||
#: .\main\templates\main\list\transaction.html:9
|
|
||||||
#: .\main\templates\main\list\transaction.html:26
|
|
||||||
#: .\main\templates\main\search.html:6 .\main\templates\main\search.html:15
|
|
||||||
msgid "Search"
|
|
||||||
msgstr "Rechercher"
|
|
||||||
|
|
||||||
#: .\main\models.py:17
|
#: .\main\models.py:17
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr "Utilisateur"
|
msgstr "Utilisateur"
|
||||||
|
|
||||||
#: .\main\models.py:45 .\main\models.py:79 .\main\models.py:87
|
#: .\main\models.py:45 .\main\models.py:79 .\main\models.py:87
|
||||||
#: .\main\models.py:237 .\main\templates\main\base.html:39
|
#: .\main\models.py:237 .\main\templates\main\base.html:39
|
||||||
#: .\main\templates\main\table\snapshot.html:10
|
#: .\main\templates\main\table\snapshot.html:7
|
||||||
#: .\main\templates\main\table\transaction.html:15
|
#: .\main\templates\main\table\transaction.html:11
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "Compte"
|
msgstr "Compte"
|
||||||
|
|
||||||
#: .\main\models.py:45 .\main\models.py:97 .\main\models.py:208
|
#: .\main\models.py:45 .\main\models.py:97 .\main\models.py:208
|
||||||
#: .\main\models.py:278 .\main\templates\main\table\transaction.html:8
|
#: .\main\models.py:278 .\main\templates\main\table\transaction.html:7
|
||||||
#: .\main\templates\main\transaction_form.html:28
|
#: .\main\templates\main\transaction_form.html:27
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nom"
|
msgstr "Nom"
|
||||||
|
|
||||||
|
@ -54,13 +46,13 @@ msgstr "Icône"
|
||||||
msgid "Default"
|
msgid "Default"
|
||||||
msgstr "Défaut"
|
msgstr "Défaut"
|
||||||
|
|
||||||
#: .\main\models.py:80 .\main\templates\main\index.html:19
|
#: .\main\models.py:80 .\main\templates\main\index.html:16
|
||||||
msgid "Accounts"
|
msgid "Accounts"
|
||||||
msgstr "Comptes"
|
msgstr "Comptes"
|
||||||
|
|
||||||
#: .\main\models.py:97 .\main\models.py:121 .\main\models.py:227
|
#: .\main\models.py:97 .\main\models.py:121 .\main\models.py:227
|
||||||
#: .\main\templates\main\base.html:49
|
#: .\main\templates\main\base.html:49
|
||||||
#: .\main\templates\main\table\transaction.html:12
|
#: .\main\templates\main\table\transaction.html:10
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Catégorie"
|
msgstr "Catégorie"
|
||||||
|
|
||||||
|
@ -68,8 +60,8 @@ msgstr "Catégorie"
|
||||||
msgid "Budget"
|
msgid "Budget"
|
||||||
msgstr "Budget"
|
msgstr "Budget"
|
||||||
|
|
||||||
#: .\main\models.py:122 .\main\templates\main\index.html:35
|
#: .\main\models.py:122 .\main\templates\main\index.html:30
|
||||||
#: .\main\templates\main\snapshot_form.html:35
|
#: .\main\templates\main\snapshot_form.html:34
|
||||||
msgid "Categories"
|
msgid "Categories"
|
||||||
msgstr "Catégories"
|
msgstr "Catégories"
|
||||||
|
|
||||||
|
@ -89,7 +81,7 @@ msgstr "Valeur de fin"
|
||||||
msgid "Start value"
|
msgid "Start value"
|
||||||
msgstr "Valeur de début"
|
msgstr "Valeur de début"
|
||||||
|
|
||||||
#: .\main\models.py:141 .\main\templates\main\table\snapshot.html:13
|
#: .\main\models.py:141 .\main\templates\main\table\snapshot.html:9
|
||||||
msgid "Difference"
|
msgid "Difference"
|
||||||
msgstr "Différence"
|
msgstr "Différence"
|
||||||
|
|
||||||
|
@ -110,27 +102,26 @@ msgstr "Relevé du %(date)s"
|
||||||
msgid "Statement"
|
msgid "Statement"
|
||||||
msgstr "Relevé"
|
msgstr "Relevé"
|
||||||
|
|
||||||
#: .\main\models.py:202 .\main\templates\main\account_form.html:24
|
#: .\main\models.py:202 .\main\templates\main\account_form.html:23
|
||||||
#: .\main\templates\main\list\snapshot.html:6
|
#: .\main\templates\main\list\snapshot.html:15
|
||||||
#: .\main\templates\main\list\snapshot.html:20
|
|
||||||
msgid "Statements"
|
msgid "Statements"
|
||||||
msgstr "Relevés"
|
msgstr "Relevés"
|
||||||
|
|
||||||
#: .\main\models.py:208 .\main\models.py:271 .\main\templates\main\base.html:54
|
#: .\main\models.py:208 .\main\models.py:271 .\main\templates\main\base.html:44
|
||||||
msgid "Transaction"
|
msgid "Transaction"
|
||||||
msgstr "Transaction"
|
msgstr "Transaction"
|
||||||
|
|
||||||
#: .\main\models.py:210 .\main\templates\main\table\transaction.html:17
|
#: .\main\models.py:210 .\main\templates\main\table\transaction.html:12
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Description"
|
msgstr "Description"
|
||||||
|
|
||||||
#: .\main\models.py:212 .\main\templates\main\table\snapshot.html:12
|
#: .\main\models.py:212 .\main\templates\main\table\snapshot.html:8
|
||||||
#: .\main\templates\main\table\transaction.html:9
|
#: .\main\templates\main\table\transaction.html:8
|
||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Valeur"
|
msgstr "Valeur"
|
||||||
|
|
||||||
#: .\main\models.py:214 .\main\templates\main\table\snapshot.html:8
|
#: .\main\models.py:214 .\main\templates\main\table\snapshot.html:6
|
||||||
#: .\main\templates\main\table\transaction.html:7
|
#: .\main\templates\main\table\transaction.html:6
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Date"
|
msgstr "Date"
|
||||||
|
|
||||||
|
@ -138,7 +129,7 @@ msgstr "Date"
|
||||||
msgid "Real date"
|
msgid "Real date"
|
||||||
msgstr "Date réelle"
|
msgstr "Date réelle"
|
||||||
|
|
||||||
#: .\main\models.py:217 .\main\templates\main\table\transaction.html:10
|
#: .\main\models.py:217 .\main\templates\main\table\transaction.html:9
|
||||||
msgid "Trader"
|
msgid "Trader"
|
||||||
msgstr "Commerçant"
|
msgstr "Commerçant"
|
||||||
|
|
||||||
|
@ -146,13 +137,12 @@ msgstr "Commerçant"
|
||||||
msgid "Payment"
|
msgid "Payment"
|
||||||
msgstr "Paiement"
|
msgstr "Paiement"
|
||||||
|
|
||||||
#: .\main\models.py:272 .\main\templates\main\account_form.html:28
|
#: .\main\models.py:272 .\main\templates\main\account_form.html:27
|
||||||
#: .\main\templates\main\category_form.html:24
|
#: .\main\templates\main\category_form.html:25
|
||||||
#: .\main\templates\main\index.html:29
|
#: .\main\templates\main\index.html:26
|
||||||
#: .\main\templates\main\list\transaction.html:6
|
#: .\main\templates\main\list\transaction.html:15
|
||||||
#: .\main\templates\main\list\transaction.html:23
|
#: .\main\templates\main\snapshot_form.html:75
|
||||||
#: .\main\templates\main\snapshot_form.html:76
|
#: .\main\templates\main\table\snapshot.html:10
|
||||||
#: .\main\templates\main\table\snapshot.html:14
|
|
||||||
msgid "Transactions"
|
msgid "Transactions"
|
||||||
msgstr "Transactions"
|
msgstr "Transactions"
|
||||||
|
|
||||||
|
@ -160,15 +150,19 @@ msgstr "Transactions"
|
||||||
msgid "Invoice"
|
msgid "Invoice"
|
||||||
msgstr "Facture"
|
msgstr "Facture"
|
||||||
|
|
||||||
#: .\main\models.py:318 .\main\templates\main\transaction_form.html:24
|
#: .\main\models.py:318 .\main\templates\main\transaction_form.html:23
|
||||||
msgid "Invoices"
|
msgid "Invoices"
|
||||||
msgstr "Factures"
|
msgstr "Factures"
|
||||||
|
|
||||||
#: .\main\templates\main\base.html:44
|
#: .\main\templates\main\base.html:54
|
||||||
msgid "Snapshot"
|
msgid "Snapshot"
|
||||||
msgstr "Relevé"
|
msgstr "Relevé"
|
||||||
|
|
||||||
#: .\main\templates\main\base.html:61
|
#: .\main\templates\main\base.html:60 .\main\templates\main\base.html:62
|
||||||
|
msgid "Search"
|
||||||
|
msgstr "Rechercher"
|
||||||
|
|
||||||
|
#: .\main\templates\main\base.html:64
|
||||||
msgid "Log out"
|
msgid "Log out"
|
||||||
msgstr "Se déconnecter"
|
msgstr "Se déconnecter"
|
||||||
|
|
||||||
|
@ -187,7 +181,7 @@ msgstr "Confirmer"
|
||||||
|
|
||||||
#: .\main\templates\main\form\base.html:17
|
#: .\main\templates\main\form\base.html:17
|
||||||
#: .\main\templates\main\tag\form_buttons.html:4
|
#: .\main\templates\main\tag\form_buttons.html:4
|
||||||
#: .\main\templates\main\transaction_form.html:35
|
#: .\main\templates\main\transaction_form.html:34
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr "Supprimer"
|
msgstr "Supprimer"
|
||||||
|
|
||||||
|
@ -196,19 +190,15 @@ msgstr "Supprimer"
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Enregistrer"
|
msgstr "Enregistrer"
|
||||||
|
|
||||||
#: .\main\templates\main\index.html:31
|
#: .\main\templates\main\index.html:40
|
||||||
msgid "History"
|
|
||||||
msgstr "Historique"
|
|
||||||
|
|
||||||
#: .\main\templates\main\index.html:45
|
|
||||||
msgid "Snapshots"
|
msgid "Snapshots"
|
||||||
msgstr "Relevés"
|
msgstr "Relevés"
|
||||||
|
|
||||||
#: .\main\templates\main\list\snapshot.html:40
|
#: .\main\templates\main\list\snapshot.html:35
|
||||||
msgid "No snapshots to show"
|
msgid "No snapshots to show"
|
||||||
msgstr "Aucun relevé à afficher"
|
msgstr "Aucun relevé à afficher"
|
||||||
|
|
||||||
#: .\main\templates\main\list\transaction.html:46
|
#: .\main\templates\main\list\transaction.html:35
|
||||||
msgid "No transactions to show"
|
msgid "No transactions to show"
|
||||||
msgstr "Aucune transaction à afficher"
|
msgstr "Aucune transaction à afficher"
|
||||||
|
|
||||||
|
@ -216,37 +206,26 @@ msgstr "Aucune transaction à afficher"
|
||||||
msgid "Log In"
|
msgid "Log In"
|
||||||
msgstr "Se connecter"
|
msgstr "Se connecter"
|
||||||
|
|
||||||
#: .\main\templates\main\plot\history.html:7
|
#: .\main\templates\main\table\snapshot.html:11
|
||||||
msgid "Month"
|
msgid "Valid"
|
||||||
msgstr "Mois"
|
msgstr "Valide"
|
||||||
|
|
||||||
#: .\main\templates\main\plot\history.html:8
|
#: .\main\templates\main\table\snapshot.html:15
|
||||||
msgid "Expenses"
|
|
||||||
msgstr "Dépenses"
|
|
||||||
|
|
||||||
#: .\main\templates\main\plot\history.html:9
|
|
||||||
msgid "Income"
|
|
||||||
msgstr "Revenus"
|
|
||||||
|
|
||||||
#: .\main\templates\main\table\snapshot.html:18
|
|
||||||
msgid "New statement"
|
msgid "New statement"
|
||||||
msgstr "Nouveau relevé"
|
msgstr "Nouveau relevé"
|
||||||
|
|
||||||
#: .\main\templates\main\table\snapshot.html:53
|
#: .\main\templates\main\table\snapshot.html:50
|
||||||
msgid "View all statements"
|
msgid "View all statements"
|
||||||
msgstr "Voir tous les relevés"
|
msgstr "Voir tous les relevés"
|
||||||
|
|
||||||
#: .\main\templates\main\table\transaction.html:21
|
#: .\main\templates\main\table\transaction.html:16
|
||||||
msgid "New transaction"
|
msgid "New transaction"
|
||||||
msgstr "Ajouter une transaction"
|
msgstr "Ajouter une transaction"
|
||||||
|
|
||||||
#: .\main\templates\main\table\transaction.html:64
|
#: .\main\templates\main\table\transaction.html:55
|
||||||
msgid "View all transactions"
|
msgid "View all transactions"
|
||||||
msgstr "Voir toutes les transactions"
|
msgstr "Voir toutes les transactions"
|
||||||
|
|
||||||
#: .\main\templates\main\transaction_form.html:40
|
#: .\main\templates\main\transaction_form.html:39
|
||||||
msgid "New invoice"
|
msgid "New invoice"
|
||||||
msgstr "Nouvelle facture"
|
msgstr "Nouvelle facture"
|
||||||
|
|
||||||
#~ msgid "Valid"
|
|
||||||
#~ msgstr "Valide"
|
|
||||||
|
|
59
nummi/main/static/main/css/chart.css
Normal file
59
nummi/main/static/main/css/chart.css
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
.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);
|
||||||
|
font-feature-settings: var(--num);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.chart .bar {
|
||||||
|
width: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.chart .bar.tot {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
height: .5rem;
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .left .bar.tot {right: 0}
|
||||||
|
.chart .right .bar.tot {left: 0}
|
||||||
|
.chart .left .bar {border-radius: var(--radius) 0 0 var(--radius)}
|
||||||
|
.chart .right .bar {border-radius: 0 var(--radius) var(--radius) 0}
|
||||||
|
|
||||||
|
.chart .bar_m {background: var(--red-1)}
|
||||||
|
.chart .bar_p {background: var(--green-1)}
|
||||||
|
|
||||||
|
.chart .bar span {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: 0 var(--gap);
|
||||||
|
font-weight: 650;
|
||||||
|
top: .5rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
font-feature-settings: var(--num);
|
||||||
|
}
|
||||||
|
.chart .right .bar span {left: 0}
|
||||||
|
.chart .left .bar span {right: 0}
|
|
@ -11,7 +11,6 @@
|
||||||
--theme-2: var(--theme);
|
--theme-2: var(--theme);
|
||||||
--theme-3: #802653;
|
--theme-3: #802653;
|
||||||
--theme-4: #cc6699;
|
--theme-4: #cc6699;
|
||||||
--gray: #dedede;
|
|
||||||
|
|
||||||
--text-theme: var(--text);
|
--text-theme: var(--text);
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@
|
||||||
--border: .5em;
|
--border: .5em;
|
||||||
--radius: .25em;
|
--radius: .25em;
|
||||||
|
|
||||||
--num: "tnum", "ss01", "ss02", "case";
|
--num: "tnum", "ss01", "case";
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
.plot table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
table-layout: auto;
|
|
||||||
}
|
|
||||||
.plot thead {
|
|
||||||
background: var(--bg-01);
|
|
||||||
}
|
|
||||||
.plot col.desc, .plot col.value {width: 8rem}
|
|
||||||
.plot col.icon {width: 1ch}
|
|
||||||
.plot tr {
|
|
||||||
padding-bottom: .5rem;
|
|
||||||
border: .1rem solid var(--gray);
|
|
||||||
}
|
|
||||||
.plot th {text-align: left}
|
|
||||||
.plot th.r {text-align: right}
|
|
||||||
.plot th.l {text-align: left}
|
|
||||||
.plot td.c {text-align: center}
|
|
||||||
|
|
||||||
.plot td, .plot th, .plot td.bar div {
|
|
||||||
position: relative;
|
|
||||||
height: 2rem;
|
|
||||||
line-height: 2rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.plot td, .plot th {
|
|
||||||
padding: .5rem var(--gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
.plot td.bar {
|
|
||||||
position: relative;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.plot td.bar div {
|
|
||||||
position: absolute;
|
|
||||||
top: .5rem;
|
|
||||||
}
|
|
||||||
.plot td.m {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.plot tbody th {
|
|
||||||
font-feature-settings: var(--num);
|
|
||||||
}
|
|
||||||
.plot td.value {
|
|
||||||
font-feature-settings: var(--num);
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plot td.bar div:not(.tot) {
|
|
||||||
width: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
z-index: 1;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.plot td.bar.p div {
|
|
||||||
left: 0;
|
|
||||||
border-radius: 0 var(--radius) var(--radius) 0;
|
|
||||||
}
|
|
||||||
.plot td.bar.m div {
|
|
||||||
right: 0;
|
|
||||||
border-radius: var(--radius) 0 0 var(--radius);
|
|
||||||
}
|
|
||||||
.plot td.bar.m div:not(.tot) {
|
|
||||||
background: var(--red-1);
|
|
||||||
}
|
|
||||||
.plot td.bar.p div:not(.tot) {
|
|
||||||
background: var(--green-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.plot td.bar div.tot {
|
|
||||||
z-index: 10;
|
|
||||||
height: .5rem;
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
.plot td.bar div.tot span {
|
|
||||||
position: absolute;
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0 var(--gap);
|
|
||||||
font-weight: 650;
|
|
||||||
top: .5rem;
|
|
||||||
line-height: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
font-feature-settings: var(--num);
|
|
||||||
}
|
|
||||||
.plot td.bar.p div.tot span {left: 0}
|
|
||||||
.plot td.bar.m div.tot span {right: 0}
|
|
|
@ -21,6 +21,8 @@
|
||||||
{{ form }}
|
{{ form }}
|
||||||
</form>
|
</form>
|
||||||
{% if form.instance.transactions %}
|
{% if form.instance.transactions %}
|
||||||
|
<img src="{% url "plot-category" form.instance.id %}"
|
||||||
|
alt="Graph representing value over time"/>
|
||||||
<h2>{% translate "Transactions" %}</h2>
|
<h2>{% translate "Transactions" %}</h2>
|
||||||
{% include "main/table/transaction.html" %}
|
{% include "main/table/transaction.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -10,9 +10,6 @@
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="{% static 'main/css/table.css' %}"
|
href="{% static 'main/css/table.css' %}"
|
||||||
type="text/css"/>
|
type="text/css"/>
|
||||||
<link rel="stylesheet"
|
|
||||||
href="{% static 'main/css/plot.css' %}"
|
|
||||||
type="text/css"/>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% if accounts %}
|
{% if accounts %}
|
||||||
|
@ -28,8 +25,6 @@
|
||||||
{% if transactions %}
|
{% if transactions %}
|
||||||
<h2>{% translate "Transactions" %}</h2>
|
<h2>{% translate "Transactions" %}</h2>
|
||||||
{% include "main/table/transaction.html" %}
|
{% include "main/table/transaction.html" %}
|
||||||
<h2>{% translate "History" %}</h2>
|
|
||||||
{% include "main/plot/history.html" %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if categories %}
|
{% if categories %}
|
||||||
<h2>{% translate "Categories" %}</h2>
|
<h2>{% translate "Categories" %}</h2>
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
{% load main_extras %}
|
|
||||||
{% load i18n %}
|
|
||||||
<div class="plot">
|
|
||||||
<table>
|
|
||||||
<colgroup>
|
|
||||||
<col class="desc">
|
|
||||||
<col class="icon">
|
|
||||||
<col class="value">
|
|
||||||
<col span="2" class="bar">
|
|
||||||
<col class="value">
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col" colspan="2">{% translate "Category" %}</th>
|
|
||||||
<th class="l" scope="col" colspan="2">{% translate "Expenses" %}</th>
|
|
||||||
<th class="r" scope="col" colspan="2">{% translate "Income" %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% spaceless %}
|
|
||||||
{% for cat in categories.data %}
|
|
||||||
<tr>
|
|
||||||
<th scope="row">
|
|
||||||
{% if cat.category %}{{ cat.category__name }}{% endif %}
|
|
||||||
</th>
|
|
||||||
<td class="c">
|
|
||||||
{% if cat.category %}
|
|
||||||
<i class="fa fa-{{ cat.category__icon }}"></i>
|
|
||||||
{% else %}
|
|
||||||
<i class="fa fa-wallet"></i>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="value">{{ cat.sum_m|pmrvalue }}</td>
|
|
||||||
<td class="bar m">
|
|
||||||
<div style="width: {% widthratio cat.sum_m categories.max -100 %}%"></div>
|
|
||||||
{% if cat.sum < 0 %}
|
|
||||||
<div class="tot"
|
|
||||||
style="width:{% widthratio cat.sum categories.max -100 %}%">
|
|
||||||
<span>{{ cat.sum|pmrvalue }}</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="bar p">
|
|
||||||
<div style="width: {% widthratio cat.sum_p categories.max 100 %}%"></div>
|
|
||||||
{% if cat.sum > 0 %}
|
|
||||||
<div class="tot"
|
|
||||||
style="width:{% widthratio cat.sum categories.max 100 %}%">
|
|
||||||
<span>{{ cat.sum|pmrvalue }}</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="value">{{ cat.sum_p|pmrvalue }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% endspaceless %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
|
@ -1,46 +0,0 @@
|
||||||
{% load main_extras %}
|
|
||||||
{% load i18n %}
|
|
||||||
<div class="plot">
|
|
||||||
<table>
|
|
||||||
<colgroup>
|
|
||||||
<col class="desc">
|
|
||||||
<col class="value">
|
|
||||||
<col span="2" class="bar">
|
|
||||||
<col class="value">
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">{% translate "Month" %}</th>
|
|
||||||
<th class="l" scope="col" colspan="2">{% translate "Expenses" %}</th>
|
|
||||||
<th class="r" scope="col" colspan="2">{% translate "Income" %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% spaceless %}
|
|
||||||
{% for date in history.data %}
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{ date.month|date:"Y-m" }}</th>
|
|
||||||
<td class="value">{{ date.sum_m|pmrvalue }}</td>
|
|
||||||
<td class="bar m">
|
|
||||||
<div style="width: {% widthratio date.sum_m history.max -100 %}%"></div>
|
|
||||||
{% 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">
|
|
||||||
<div style="width: {% widthratio date.sum_p history.max 100 %}%"></div>
|
|
||||||
{% 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>
|
|
||||||
{% endfor %}
|
|
||||||
{% endspaceless %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
|
@ -12,7 +12,7 @@
|
||||||
href="{% static 'main/css/table.css' %}"
|
href="{% static 'main/css/table.css' %}"
|
||||||
type="text/css"/>
|
type="text/css"/>
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="{% static 'main/css/plot.css' %}"
|
href="{% static 'main/css/chart.css' %}"
|
||||||
type="text/css"/>
|
type="text/css"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -33,7 +33,44 @@
|
||||||
</form>
|
</form>
|
||||||
{% if categories %}
|
{% if categories %}
|
||||||
<h2>{% translate "Categories" %}</h2>
|
<h2>{% translate "Categories" %}</h2>
|
||||||
{% include "main/plot/category.html" %}
|
<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">{{ cat.sum_m|pmvalue }}</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 }}">
|
||||||
|
<span>{{ cat.sum|pmvalue }}</span>
|
||||||
|
</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 }}">
|
||||||
|
<span>{{ cat.sum|pmvalue }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="value right">{{ cat.sum_p|pmvalue }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not snapshot.adding %}
|
{% if not snapshot.adding %}
|
||||||
<h2>{% translate "Transactions" %} ({{ snapshot.sum|pmvalue }} / {{ snapshot.diff|pmvalue }})</h2>
|
<h2>{% translate "Transactions" %} ({{ snapshot.sum|pmvalue }} / {{ snapshot.diff|pmvalue }})</h2>
|
||||||
|
|
|
@ -6,12 +6,12 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def value(val, pm=False, r=2):
|
def value(val, pm=False):
|
||||||
if not val:
|
if not val:
|
||||||
return mark_safe("–")
|
return mark_safe("–")
|
||||||
_prefix = ""
|
_prefix = ""
|
||||||
_suffix = " €"
|
_suffix = " €"
|
||||||
_val = formats.number_format(val, r, use_l10n=True, force_grouping=True)
|
_val = formats.number_format(val, 2, use_l10n=True, force_grouping=True)
|
||||||
|
|
||||||
if val > 0:
|
if val > 0:
|
||||||
if pm:
|
if pm:
|
||||||
|
@ -28,11 +28,6 @@ def pmvalue(val):
|
||||||
return value(val, True)
|
return value(val, True)
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
|
||||||
def pmrvalue(val):
|
|
||||||
return value(val, True, r=0)
|
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag("main/tag/form_buttons.html")
|
@register.inclusion_tag("main/tag/form_buttons.html")
|
||||||
def form_buttons(instance):
|
def form_buttons(instance):
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -41,31 +41,12 @@ class IndexView(LoginRequiredMixin, TemplateView):
|
||||||
_max = 8
|
_max = 8
|
||||||
_transactions = Transaction.objects.filter(user=self.request.user)
|
_transactions = Transaction.objects.filter(user=self.request.user)
|
||||||
_snapshots = Snapshot.objects.filter(user=self.request.user)
|
_snapshots = Snapshot.objects.filter(user=self.request.user)
|
||||||
_history = (
|
|
||||||
_transactions.filter(category__budget=True)
|
|
||||||
.values(month=models.functions.TruncMonth("date"))
|
|
||||||
.annotate(
|
|
||||||
sum_p=models.Sum("value", filter=models.Q(value__gt=0)),
|
|
||||||
sum_m=models.Sum("value", filter=models.Q(value__lt=0)),
|
|
||||||
sum=models.Sum("value"),
|
|
||||||
)
|
|
||||||
.order_by("-month")
|
|
||||||
)
|
|
||||||
|
|
||||||
res = {
|
res = {
|
||||||
"accounts": Account.objects.filter(user=self.request.user),
|
"accounts": Account.objects.filter(user=self.request.user),
|
||||||
"transactions": _transactions[:_max],
|
"transactions": _transactions[:_max],
|
||||||
"categories": Category.objects.filter(user=self.request.user),
|
"categories": Category.objects.filter(user=self.request.user),
|
||||||
"snapshots": _snapshots[:_max],
|
"snapshots": _snapshots[:_max],
|
||||||
"history": {
|
|
||||||
"data": _history,
|
|
||||||
"max": max(
|
|
||||||
_history.aggregate(
|
|
||||||
max=models.Max("sum_p"),
|
|
||||||
min=-models.Min("sum_m"),
|
|
||||||
).values(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
if _transactions.count() > _max:
|
if _transactions.count() > _max:
|
||||||
res["transactions_url"] = reverse_lazy("transactions")
|
res["transactions_url"] = reverse_lazy("transactions")
|
||||||
|
@ -251,8 +232,10 @@ class SnapshotUpdateView(NummiUpdateView):
|
||||||
"snapshot_transactions", args=(snapshot.pk,)
|
"snapshot_transactions", args=(snapshot.pk,)
|
||||||
)
|
)
|
||||||
if _transactions:
|
if _transactions:
|
||||||
_categories = (
|
data["categories"] = (
|
||||||
_transactions.values("category", "category__name", "category__icon")
|
_transactions.values(
|
||||||
|
"category", "category__name", "category__icon"
|
||||||
|
)
|
||||||
.annotate(
|
.annotate(
|
||||||
sum=models.Sum("value"),
|
sum=models.Sum("value"),
|
||||||
sum_m=models.Sum("value", filter=models.Q(value__lt=0)),
|
sum_m=models.Sum("value", filter=models.Q(value__lt=0)),
|
||||||
|
@ -260,15 +243,18 @@ class SnapshotUpdateView(NummiUpdateView):
|
||||||
)
|
)
|
||||||
.order_by("-sum")
|
.order_by("-sum")
|
||||||
)
|
)
|
||||||
data["categories"] = {
|
data["cat_lim"] = max(
|
||||||
"data": _categories,
|
map(
|
||||||
"max": max(
|
lambda x: abs(x) if x else 0,
|
||||||
_categories.aggregate(
|
data["categories"]
|
||||||
|
.aggregate(
|
||||||
max=models.Max("sum_p"),
|
max=models.Max("sum_p"),
|
||||||
min=models.Min("sum_m"),
|
min=models.Min("sum_m"),
|
||||||
).values(),
|
)
|
||||||
),
|
.values(),
|
||||||
}
|
)
|
||||||
|
)
|
||||||
|
data["cat_lim_m"] = -data["cat_lim"]
|
||||||
|
|
||||||
return data | {
|
return data | {
|
||||||
"new_transaction_url": reverse_lazy(
|
"new_transaction_url": reverse_lazy(
|
||||||
|
|
|
@ -19,6 +19,7 @@ from django.urls import include, path
|
||||||
|
|
||||||
urlpatterns = i18n_patterns(
|
urlpatterns = i18n_patterns(
|
||||||
path("", include("main.urls")),
|
path("", include("main.urls")),
|
||||||
|
path("plot/", include("plot.urls")),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
prefix_default_language=False,
|
prefix_default_language=False,
|
||||||
)
|
)
|
||||||
|
|
0
nummi/plot/__init__.py
Normal file
0
nummi/plot/__init__.py
Normal file
1
nummi/plot/admin.py
Normal file
1
nummi/plot/admin.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Register your models here.
|
6
nummi/plot/apps.py
Normal file
6
nummi/plot/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PlotConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "plot"
|
0
nummi/plot/migrations/__init__.py
Normal file
0
nummi/plot/migrations/__init__.py
Normal file
1
nummi/plot/models.py
Normal file
1
nummi/plot/models.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Create your models here.
|
13
nummi/plot/nummi.mplstyle
Normal file
13
nummi/plot/nummi.mplstyle
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
font.family: Inter
|
||||||
|
|
||||||
|
lines.linewidth: 2
|
||||||
|
|
||||||
|
figure.autolayout: True
|
||||||
|
figure.figsize: 8, 4
|
||||||
|
figure.dpi: 300
|
||||||
|
|
||||||
|
axes.prop_cycle: cycler('color', ["66cc66", "338033", "99ff99", "802653", "cc6699"])
|
||||||
|
axes.axisbelow: True
|
||||||
|
axes.grid: True
|
||||||
|
|
||||||
|
svg.fonttype: none
|
1
nummi/plot/tests.py
Normal file
1
nummi/plot/tests.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Create your tests here.
|
9
nummi/plot/urls.py
Normal file
9
nummi/plot/urls.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("timeline", views.timeline, name="plot-timeline"),
|
||||||
|
path("categories", views.categories, name="plot-categories"),
|
||||||
|
path("category/<uuid>", views.category, name="plot-category"),
|
||||||
|
]
|
108
nummi/plot/views.py
Normal file
108
nummi/plot/views.py
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import io
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.db import models
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
from matplotlib import dates as mdates
|
||||||
|
|
||||||
|
from main.models import Category, Snapshot, Transaction
|
||||||
|
|
||||||
|
matplotlib.use("Agg")
|
||||||
|
plt.style.use("./plot/nummi.mplstyle")
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def timeline(request):
|
||||||
|
_snapshots = Snapshot.objects.all()
|
||||||
|
|
||||||
|
fig, ax = plt.subplots()
|
||||||
|
ax.step(
|
||||||
|
[s.date for s in _snapshots],
|
||||||
|
[s.value for s in _snapshots],
|
||||||
|
where="post",
|
||||||
|
)
|
||||||
|
ax.set(ylabel=_("Snapshots"), ylim=0)
|
||||||
|
ax.xaxis.set_major_formatter(
|
||||||
|
mdates.ConciseDateFormatter(ax.xaxis.get_major_locator())
|
||||||
|
)
|
||||||
|
ax.autoscale(True, "x", True)
|
||||||
|
|
||||||
|
_io = io.StringIO()
|
||||||
|
|
||||||
|
fig.savefig(_io, format="svg")
|
||||||
|
|
||||||
|
return HttpResponse(_io.getvalue(), headers={"Content-Type": "image/svg+xml"})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def categories(request):
|
||||||
|
_categories = Category.objects.filter(budget=True)
|
||||||
|
|
||||||
|
fig, ax = plt.subplots(figsize=(8, _categories.count() / 4))
|
||||||
|
ax.barh(
|
||||||
|
[str(c) for c in _categories][::-1],
|
||||||
|
[
|
||||||
|
Transaction.objects.filter(category=c).aggregate(sum=models.Sum("value"))[
|
||||||
|
"sum"
|
||||||
|
]
|
||||||
|
for c in _categories
|
||||||
|
][::-1],
|
||||||
|
)
|
||||||
|
|
||||||
|
_io = io.StringIO()
|
||||||
|
|
||||||
|
fig.savefig(_io, format="svg")
|
||||||
|
|
||||||
|
return HttpResponse(_io.getvalue(), headers={"Content-Type": "image/svg+xml"})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def category(request, uuid):
|
||||||
|
_category = get_object_or_404(Category, id=uuid)
|
||||||
|
_values_p = (
|
||||||
|
Transaction.objects.filter(category=_category)
|
||||||
|
.filter(value__gt=0)
|
||||||
|
.annotate(m=models.functions.TruncMonth("date"))
|
||||||
|
.values("m")
|
||||||
|
.annotate(sum=models.Sum("value"))
|
||||||
|
.order_by("m")
|
||||||
|
)
|
||||||
|
_values_m = (
|
||||||
|
Transaction.objects.filter(category=_category)
|
||||||
|
.filter(value__lt=0)
|
||||||
|
.annotate(m=models.functions.TruncMonth("date"))
|
||||||
|
.values("m")
|
||||||
|
.annotate(sum=models.Sum("value"))
|
||||||
|
.order_by("m")
|
||||||
|
)
|
||||||
|
|
||||||
|
fig, ax = plt.subplots()
|
||||||
|
ax.bar(
|
||||||
|
[v["m"] for v in _values_p],
|
||||||
|
[v["sum"] for v in _values_p],
|
||||||
|
width=12,
|
||||||
|
color="#66cc66",
|
||||||
|
)
|
||||||
|
ax.bar(
|
||||||
|
[v["m"] for v in _values_m],
|
||||||
|
[v["sum"] for v in _values_m],
|
||||||
|
width=12,
|
||||||
|
color="#cc6699",
|
||||||
|
)
|
||||||
|
ax.xaxis.set_major_formatter(
|
||||||
|
mdates.ConciseDateFormatter(ax.xaxis.get_major_locator())
|
||||||
|
)
|
||||||
|
ax.autoscale(True, "x", True)
|
||||||
|
_ym, _yp = ax.get_ylim()
|
||||||
|
ax.set(ylim=(min(_ym, 0), max(_yp, 0)))
|
||||||
|
ax.set(ylabel=f"{_category.name} (€)")
|
||||||
|
|
||||||
|
_io = io.StringIO()
|
||||||
|
|
||||||
|
fig.savefig(_io, format="svg")
|
||||||
|
|
||||||
|
return HttpResponse(_io.getvalue(), headers={"Content-Type": "image/svg+xml"})
|
Loading…
Reference in a new issue