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",
)