aboutsummaryrefslogtreecommitdiff
path: root/ihatemoney
diff options
context:
space:
mode:
Diffstat (limited to 'ihatemoney')
-rw-r--r--ihatemoney/api.py8
-rw-r--r--ihatemoney/models.py20
-rw-r--r--ihatemoney/templates/statistics.html21
-rw-r--r--ihatemoney/tests/tests.py58
-rw-r--r--ihatemoney/web.py14
5 files changed, 85 insertions, 36 deletions
diff --git a/ihatemoney/api.py b/ihatemoney/api.py
index 31ed06c..6068cf7 100644
--- a/ihatemoney/api.py
+++ b/ihatemoney/api.py
@@ -65,6 +65,13 @@ class ProjectHandler(Resource):
return form.errors, 400
+class ProjectStatsHandler(Resource):
+ method_decorators = [need_auth]
+
+ def get(self, project):
+ return project.members_stats
+
+
class APIMemberForm(MemberForm):
""" Member is not disablable via a Form.
@@ -163,6 +170,7 @@ class BillHandler(Resource):
restful_api.add_resource(ProjectsHandler, '/projects')
restful_api.add_resource(ProjectHandler, '/projects/<string:project_id>')
restful_api.add_resource(MembersHandler, "/projects/<string:project_id>/members")
+restful_api.add_resource(ProjectStatsHandler, "/projects/<string:project_id>/statistics")
restful_api.add_resource(MemberHandler, "/projects/<string:project_id>/members/<int:member_id>")
restful_api.add_resource(BillsHandler, "/projects/<string:project_id>/bills")
restful_api.add_resource(BillHandler, "/projects/<string:project_id>/bills/<int:bill_id>")
diff --git a/ihatemoney/models.py b/ihatemoney/models.py
index aa3083d..c6ce23f 100644
--- a/ihatemoney/models.py
+++ b/ihatemoney/models.py
@@ -53,6 +53,26 @@ class Project(db.Model):
return balances
@property
+ def members_stats(self):
+ """Compute what each member has paid
+
+ :return: one stat dict per member
+ :rtype list:
+ """
+ return [{
+ 'member': member,
+ 'paid': sum([
+ bill.amount
+ for bill in self.get_member_bills(member.id).all()
+ ]),
+ 'spent': sum([
+ bill.pay_each() * member.weight
+ for bill in self.get_bills().all() if member in bill.owers
+ ]),
+ 'balance': self.balance[member.id]
+ } for member in self.active_members]
+
+ @property
def uses_weights(self):
return len([i for i in self.members if i.weight != 1]) > 0
diff --git a/ihatemoney/templates/statistics.html b/ihatemoney/templates/statistics.html
index ae1c80e..1b07a33 100644
--- a/ihatemoney/templates/statistics.html
+++ b/ihatemoney/templates/statistics.html
@@ -3,12 +3,11 @@
{% 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]) }}
+ {% for stat in members_stats| sort(attribute='member.name') %}
+ <tr>
+ <td class="balance-name">{{ stat.member.name }}</td>
+ <td class="balance-value {% if stat.balance|round(2) > 0 %}positive{% elif stat.balance|round(2) < 0 %}negative{% endif %}">
+ {% if stat.balance|round(2) > 0 %}+{% endif %}{{ "%.2f" | format(stat.balance) }}
</td>
</tr>
{% endfor %}
@@ -21,12 +20,12 @@
<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 %}
+ {% for stat in members_stats %}
<tr>
- <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>
+ <td>{{ stat.member.name }}</td>
+ <td>{{ "%0.2f"|format(stat.paid) }}</td>
+ <td>{{ "%0.2f"|format(stat.spent) }}</td>
+ <td>{{ "%0.2f"|format(stat.balance) }}</td>
</tr>
{% endfor %}
</tbody>
diff --git a/ihatemoney/tests/tests.py b/ihatemoney/tests/tests.py
index c13131c..3797f09 100644
--- a/ihatemoney/tests/tests.py
+++ b/ihatemoney/tests/tests.py
@@ -750,24 +750,24 @@ class BudgetTestCase(IhatemoneyTestCase):
})
response = self.client.get("/raclette/statistics")
- self.assertIn("<td>alexis</td>\n "
- + "<td>20.00</td>\n "
- + "<td>31.67</td>\n "
+ 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 "
+ 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 "
+ 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 "
+ 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'))
@@ -1325,6 +1325,40 @@ class APITestCase(IhatemoneyTestCase):
headers=self.get_auth("raclette"))
self.assertStatus(404, req)
+ def test_statistics(self):
+ # create a project
+ self.api_create("raclette")
+
+ # add members
+ self.api_add_member("raclette", "alexis")
+ self.api_add_member("raclette", "fred")
+
+ # add a bill
+ req = self.client.post("/api/projects/raclette/bills", data={
+ 'date': '2011-08-10',
+ 'what': 'fromage',
+ 'payer': "1",
+ 'payed_for': ["1", "2"],
+ 'amount': '25',
+ }, headers=self.get_auth("raclette"))
+
+ # get the list of bills (should be empty)
+ req = self.client.get("/api/projects/raclette/statistics",
+ headers=self.get_auth("raclette"))
+ self.assertStatus(200, req)
+ self.assertEqual([
+ {'balance': 12.5,
+ 'member': {'activated': True, 'id': 1,
+ 'name': 'alexis', 'weight': 1.0},
+ 'paid': 25.0,
+ 'spent': 12.5},
+ {'balance': -12.5,
+ 'member': {'activated': True, 'id': 2,
+ 'name': 'fred', 'weight': 1.0},
+ 'paid': 0,
+ 'spent': 12.5}],
+ json.loads(req.data.decode('utf-8')))
+
def test_username_xss(self):
# create a project
# self.api_create("raclette")
diff --git a/ihatemoney/web.py b/ihatemoney/web.py
index 6b1b358..1e16202 100644
--- a/ihatemoney/web.py
+++ b/ihatemoney/web.py
@@ -566,21 +566,9 @@ 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,
+ members_stats=g.project.members_stats,
current_view='statistics',
)