diff --git a/ihatemoney/models.py b/ihatemoney/models.py index 473e7c0b0..475b3cfa7 100644 --- a/ihatemoney/models.py +++ b/ihatemoney/models.py @@ -262,6 +262,14 @@ def get_member_bills(self, member_id): .order_by(Bill.id.desc()) ) + def get_newest_bill(self): + """Returns the most recent bill (according to bill date) or None if there are no bills""" + return self.get_bills_unordered().order_by(Bill.date.desc()).first() + + def get_oldest_bill(self): + """Returns the least recent bill (according to bill date) or None if there are no bills""" + return self.get_bills_unordered().order_by(Bill.date.asc()).first() + 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/tests/budget_test.py b/ihatemoney/tests/budget_test.py index 92618594e..6b2a8f27f 100644 --- a/ihatemoney/tests/budget_test.py +++ b/ihatemoney/tests/budget_test.py @@ -917,6 +917,11 @@ def test_statistics(self): # Add a participant with a balance at 0 : self.client.post("/raclette/members/add", data={"name": "pépé"}) + # Check that the "monthly expenses" table is empty + response = self.client.get("/raclette/statistics") + regex = r"\s*\s*Period\s*Spent\s*\s*\s*\s*" + self.assertRegex(response.data.decode("utf-8"), regex) + # create bills self.client.post( "/raclette/add", @@ -952,7 +957,7 @@ def test_statistics(self): ) response = self.client.get("/raclette/statistics") - regex = r"{}\s+{}\s+{}" + regex = r"{}\s*{}\s*{}" self.assertRegex( response.data.decode("utf-8"), regex.format("zorglub", r"\$20\.00", r"\$31\.67"), @@ -982,6 +987,85 @@ def test_statistics(self): self.assertRegex(response.data.decode("utf-8"), re.compile(regex1, re.DOTALL)) self.assertRegex(response.data.decode("utf-8"), re.compile(regex2, re.DOTALL)) + # Check that the "monthly expenses" table has a single month and the correct amount + regex = r"\s*\s*August 2011\s*\$40.00\s*\s*" + self.assertRegex(response.data.decode("utf-8"), regex) + + # Add bills for other months and check monthly expenses again + self.client.post( + "/raclette/add", + data={ + "date": "2011-12-31", + "what": "champomy", + "payer": 1, + "payed_for": [1, 2], + "amount": "10", + }, + ) + self.client.post( + "/raclette/add", + data={ + "date": "2011-08-01", + "what": "ice cream", + "payer": 2, + "payed_for": [1, 2], + "amount": "10", + }, + ) + months = [ + ("December 2011", r"\$10.00"), + ("November 2011", r"\$0.00"), + ("October 2011", r"\$0.00"), + ("September 2011", r"\$0.00"), + ("August 2011", r"\$50.00"), + ] + months_fmt = [ + r"\s*{}\s*{}\s*".format(month, amount) + for month, amount in months + ] + regex = r"\s*" + r"\s*".join(months_fmt) + r"\s*" + response = self.client.get("/raclette/statistics") + self.assertRegex(response.data.decode("utf-8"), regex) + + # Test more corner cases + self.client.post( + "/raclette/add", + data={ + "date": "2011-07-31", + "what": "more ice cream", + "payer": 1, + "payed_for": [1, 2], + "amount": "20", + }, + ) + months.append(("July 2011", r"\$20.00")) + months_fmt = [ + r"\s*{}\s*{}\s*".format(month, amount) + for month, amount in months + ] + regex = r"\s*" + r"\s*".join(months_fmt) + r"\s*" + response = self.client.get("/raclette/statistics") + self.assertRegex(response.data.decode("utf-8"), regex) + + self.client.post( + "/raclette/add", + data={ + "date": "2012-01-01", + "what": "more champomy", + "payer": 2, + "payed_for": [1, 2], + "amount": "30", + }, + ) + months.insert(0, ("January 2012", r"\$30.00")) + months_fmt = [ + r"\s*{}\s*{}\s*".format(month, amount) + for month, amount in months + ] + regex = r"\s*" + r"\s*".join(months_fmt) + r"\s*" + response = self.client.get("/raclette/statistics") + self.assertRegex(response.data.decode("utf-8"), regex) + def test_settle_page(self): self.post_project("raclette") response = self.client.get("/raclette/settle_bills") diff --git a/ihatemoney/web.py b/ihatemoney/web.py index d9e7ec087..c338aefe8 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -8,8 +8,9 @@ some shortcuts to make your life better when coding (see `pull_project` and `add_project_id` for a quick overview) """ -from datetime import datetime from functools import wraps +import datetime +import itertools import json import os @@ -893,12 +894,23 @@ def strip_ip_addresses(): @main.route("//statistics") def statistics(): """Compute what each participant has paid and spent and display it""" - today = datetime.now() + # Determine range of months between which there are bills + months = [] + if g.project.has_bills(): + first_date = g.project.get_oldest_bill().date + last_date = g.project.get_newest_bill().date + # Infinite iterator towards the past + last_month = datetime.date(year=last_date.year, month=last_date.month, day=1) + all_months = (last_month - relativedelta(months=i) for i in itertools.count()) + # Stop when reaching one month before the first date + months = itertools.takewhile( + lambda x: x > first_date - relativedelta(months=1), all_months + ) return render_template( "statistics.html", members_stats=g.project.members_stats, monthly_stats=g.project.monthly_stats, - months=[today - relativedelta(months=i) for i in range(12)], + months=months, current_view="statistics", )