aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author0livd <github@destras.fr>2017-08-21 23:51:32 +0200
committerAlexis Metaireau <alexis@notmyidea.org>2017-08-21 23:51:32 +0200
commit2ec4fb589fdc0fa14297263fd9357c796689dee9 (patch)
tree33357717c20a5781cbff4b783dc3db1517dda533
parentec4a099f182629d86a7421af7d4899a655be684e (diff)
downloadihatemoney-mirror-2ec4fb589fdc0fa14297263fd9357c796689dee9.zip
ihatemoney-mirror-2ec4fb589fdc0fa14297263fd9357c796689dee9.tar.gz
ihatemoney-mirror-2ec4fb589fdc0fa14297263fd9357c796689dee9.tar.bz2
Add a statistics tab (#257)
-rw-r--r--CHANGELOG.rst4
-rw-r--r--ihatemoney/models.py10
-rw-r--r--ihatemoney/templates/layout.html1
-rw-r--r--ihatemoney/templates/statistics.html35
-rw-r--r--ihatemoney/tests/tests.py62
-rw-r--r--ihatemoney/translations/fr/LC_MESSAGES/messages.mobin8629 -> 8789 bytes
-rw-r--r--ihatemoney/translations/fr/LC_MESSAGES/messages.po20
-rw-r--r--ihatemoney/web.py22
8 files changed, 154 insertions, 0 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 33e1de5..94b802c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -12,6 +12,10 @@ This document describes changes between each past release.
- **BREAKING CHANGE** Turn the WSGI file into a python module, renamed from budget/ihatemoney.wsgi to budget/wsgi.py. Please update your Apache configuration!
- Changed the recommended gunicorn configuration to use the wsgi module as an entrypoint
+### Added
+
+- Add a statistics tab (#257)
+- Add python3.6 support (#259)
### Removed
diff --git a/ihatemoney/models.py b/ihatemoney/models.py
index 6c71a57..cd896f3 100644
--- a/ihatemoney/models.py
+++ b/ihatemoney/models.py
@@ -144,6 +144,16 @@ class Project(db.Model):
.order_by(Bill.date.desc())\
.order_by(Bill.id.desc())
+ def get_member_bills(self, member_id):
+ """Return the list of bills related to a specific member"""
+ return Bill.query.join(Person, Project)\
+ .filter(Bill.payer_id == Person.id)\
+ .filter(Person.project_id == Project.id)\
+ .filter(Person.id == member_id)\
+ .filter(Project.id == self.id)\
+ .order_by(Bill.date.desc())\
+ .order_by(Bill.id.desc())
+
def get_pretty_bills(self, export_format="json"):
"""Return a list of project's bills with pretty formatting"""
bills = self.get_bills()
diff --git a/ihatemoney/templates/layout.html b/ihatemoney/templates/layout.html
index 76ae890..36f01f8 100644
--- a/ihatemoney/templates/layout.html
+++ b/ihatemoney/templates/layout.html
@@ -40,6 +40,7 @@
{% block navbar %}
<li class="nav-item{% if current_view == 'list_bills' %} active{% endif %}"><a class="nav-link" href="{{ url_for(".list_bills") }}">{{ _("Bills") }}</a></li>
<li class="nav-item{% if current_view == 'settle_bill' %} active{% endif %}"><a class="nav-link" href="{{ url_for(".settle_bill") }}">{{ _("Settle") }}</a></li>
+ <li class="nav-item{% if current_view == 'statistics' %} active{% endif %}"><a class="nav-link" href="{{ url_for(".statistics") }}">{{ _("Statistics") }}</a></li>
{% endblock %}
{% endif %}
</ul>
diff --git a/ihatemoney/templates/statistics.html b/ihatemoney/templates/statistics.html
new file mode 100644
index 0000000..061c629
--- /dev/null
+++ b/ihatemoney/templates/statistics.html
@@ -0,0 +1,35 @@
+{% extends "sidebar_table_layout.html" %}
+
+{% block sidebar %}
+ <div id="table_overflow">
+ <table class="balance table">
+ {% set balance = g.project.balance %}
+ {% for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2) != 0 %}
+ <tr id="bal-member-{{ member.id }}" action={% if member.activated %}delete{% else %}reactivate{% endif %}>
+ <td class="balance-name">{{ member.name }}</td>
+ <td class="balance-value {% if balance[member.id]|round(2) > 0 %}positive{% elif balance[member.id]|round(2) < 0 %}negative{% endif %}">
+ {% if balance[member.id]|round(2) > 0 %}+{% endif %}{{ "%.2f" | format(balance[member.id]) }}
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+{% endblock %}
+
+
+{% block content %}
+ <table id="bill_table" class="split_bills table table-striped">
+ <thead><tr><th>{{ _("Who?") }}</th><th>{{ _("Paid") }}</th><th>{{ _("Spent") }}</th><th>{{ _("Balance") }}</th></tr></thead>
+ <tbody>
+ {% for member in members %}
+ <tr class="{{ loop.cycle("odd", "even") }}">
+ <td>{{ member.name }}</td>
+ <td>{{ "%0.2f"|format(paid[member.id]) }}</td>
+ <td>{{ "%0.2f"|format(spent[member.id]) }}</td>
+ <td>{{ "%0.2f"|format(balance[member.id]) }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+
+{% endblock %}
diff --git a/ihatemoney/tests/tests.py b/ihatemoney/tests/tests.py
index 86f11f3..ac3551c 100644
--- a/ihatemoney/tests/tests.py
+++ b/ihatemoney/tests/tests.py
@@ -627,6 +627,68 @@ class BudgetTestCase(IhatemoneyTestCase):
response = self.client.get("/dashboard")
self.assertEqual(response.status_code, 200)
+ def test_statistics_page(self):
+ self.post_project("raclette")
+ response = self.client.get("/raclette/statistics")
+ self.assertEqual(response.status_code, 200)
+
+ def test_statistics(self):
+ self.post_project("raclette")
+
+ # add members
+ self.client.post("/raclette/members/add", data={'name': 'alexis', 'weight': 2})
+ self.client.post("/raclette/members/add", data={'name': 'fred'})
+ self.client.post("/raclette/members/add", data={'name': 'tata'})
+ # Add a member with a balance=0 :
+ self.client.post("/raclette/members/add", data={'name': 'toto'})
+
+ # create bills
+ self.client.post("/raclette/add", data={
+ 'date': '2011-08-10',
+ 'what': 'fromage à raclette',
+ 'payer': 1,
+ 'payed_for': [1, 2, 3],
+ 'amount': '10.0',
+ })
+
+ self.client.post("/raclette/add", data={
+ 'date': '2011-08-10',
+ 'what': 'red wine',
+ 'payer': 2,
+ 'payed_for': [1],
+ 'amount': '20',
+ })
+
+ self.client.post("/raclette/add", data={
+ 'date': '2011-08-10',
+ 'what': 'delicatessen',
+ 'payer': 1,
+ 'payed_for': [1, 2],
+ 'amount': '10',
+ })
+
+ response = self.client.get("/raclette/statistics")
+ self.assertIn("<td>alexis</td>\n "
+ + "<td>20.00</td>\n "
+ + "<td>31.67</td>\n "
+ + "<td>-11.67</td>\n",
+ response.data.decode('utf-8'))
+ self.assertIn("<td>fred</td>\n "
+ + "<td>20.00</td>\n "
+ + "<td>5.83</td>\n "
+ + "<td>14.17</td>\n",
+ response.data.decode('utf-8'))
+ self.assertIn("<td>tata</td>\n "
+ + "<td>0.00</td>\n "
+ + "<td>2.50</td>\n "
+ + "<td>-2.50</td>\n",
+ response.data.decode('utf-8'))
+ self.assertIn("<td>toto</td>\n "
+ + "<td>0.00</td>\n "
+ + "<td>0.00</td>\n "
+ + "<td>0.00</td>\n",
+ response.data.decode('utf-8'))
+
def test_settle_page(self):
self.post_project("raclette")
response = self.client.get("/raclette/settle_bills")
diff --git a/ihatemoney/translations/fr/LC_MESSAGES/messages.mo b/ihatemoney/translations/fr/LC_MESSAGES/messages.mo
index 2f46b71..d6011d5 100644
--- a/ihatemoney/translations/fr/LC_MESSAGES/messages.mo
+++ b/ihatemoney/translations/fr/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/ihatemoney/translations/fr/LC_MESSAGES/messages.po b/ihatemoney/translations/fr/LC_MESSAGES/messages.po
index 65c295d..e8b9793 100644
--- a/ihatemoney/translations/fr/LC_MESSAGES/messages.po
+++ b/ihatemoney/translations/fr/LC_MESSAGES/messages.po
@@ -400,6 +400,10 @@ msgstr "Factures"
msgid "Settle"
msgstr "Remboursements"
+#: templates/layout.html:50
+msgid "Statistics"
+msgstr "Statistiques"
+
#: templates/layout.html:53
msgid "options"
msgstr "options"
@@ -529,3 +533,19 @@ msgstr "Qui doit payer ?"
#: templates/settle_bills.html:31
msgid "To whom?"
msgstr "Pour qui ?"
+
+#: templates/statistics.html:22
+msgid "Who?"
+msgstr "Qui ?"
+
+#: templates/statistics.html:22
+msgid "Paid"
+msgstr "A payé"
+
+#: templates/statistics.html:22
+msgid "Spent"
+msgstr "A dépensé"
+
+#: templates/statistics.html:22
+msgid "Balance"
+msgstr "Solde"
diff --git a/ihatemoney/web.py b/ihatemoney/web.py
index cc2eeac..82e1591 100644
--- a/ihatemoney/web.py
+++ b/ihatemoney/web.py
@@ -507,6 +507,28 @@ def settle_bill():
)
+@main.route("/<project_id>/statistics")
+def statistics():
+ """Compute what each member has paid and spent and display it"""
+ members = g.project.active_members
+ balance = g.project.balance
+ paid = {}
+ spent = {}
+ for member in members:
+ paid[member.id] = sum([bill.amount
+ for bill in g.project.get_member_bills(member.id).all()])
+ spent[member.id] = sum([bill.pay_each() * member.weight
+ for bill in g.project.get_bills().all() if member in bill.owers])
+ return render_template(
+ "statistics.html",
+ members=members,
+ balance=balance,
+ paid=paid,
+ spent=spent,
+ current_view='statistics',
+ )
+
+
@main.route("/dashboard")
def dashboard():
return render_template("dashboard.html", projects=Project.query.all())