Skip to content

Commit

Permalink
Display monthly statistics for the range of months where the project …
Browse files Browse the repository at this point in the history
…was active

Currently, we display a hard-coded "one year" range of monthly statistics
starting from today.  This generally is not the intended behaviour: for
instance, on an archived project, the bills might all be older than one
year, so the table only displays months without any operation.

Instead, display all months between the first and last bills.  There might
be empty months in the middle, but that's intended, because we want all
months to be consecutive.

If there are no bills, simply display an empty table.
  • Loading branch information
Baptiste Jonglez committed Dec 9, 2021
1 parent 915c832 commit 6c2cb73
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 4 deletions.
8 changes: 8 additions & 0 deletions ihatemoney/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
86 changes: 85 additions & 1 deletion ihatemoney/tests/budget_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"<thead>\s*<tr>\s*<th>Period</th>\s*<th>Spent</th>\s*</tr>\s*</thead>\s*<tbody>\s*</tbody>"
self.assertRegex(response.data.decode("utf-8"), regex)

# create bills
self.client.post(
"/raclette/add",
Expand Down Expand Up @@ -952,7 +957,7 @@ def test_statistics(self):
)

response = self.client.get("/raclette/statistics")
regex = r"<td class=\"d-md-none\">{}</td>\s+<td>{}</td>\s+<td>{}</td>"
regex = r"<td class=\"d-md-none\">{}</td>\s*<td>{}</td>\s*<td>{}</td>"
self.assertRegex(
response.data.decode("utf-8"),
regex.format("zorglub", r"\$20\.00", r"\$31\.67"),
Expand Down Expand Up @@ -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"<tbody>\s*<tr>\s*<td>August 2011</td>\s*<td>\$40.00</td>\s*</tr>\s*</tbody>"
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"<tr>\s*<td>{}</td>\s*<td>{}</td>\s*</tr>".format(month, amount)
for month, amount in months
]
regex = r"<tbody>\s*" + r"\s*".join(months_fmt) + r"\s*</tbody>"
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"<tr>\s*<td>{}</td>\s*<td>{}</td>\s*</tr>".format(month, amount)
for month, amount in months
]
regex = r"<tbody>\s*" + r"\s*".join(months_fmt) + r"\s*</tbody>"
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"<tr>\s*<td>{}</td>\s*<td>{}</td>\s*</tr>".format(month, amount)
for month, amount in months
]
regex = r"<tbody>\s*" + r"\s*".join(months_fmt) + r"\s*</tbody>"
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")
Expand Down
20 changes: 17 additions & 3 deletions ihatemoney/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -893,12 +894,25 @@ def strip_ip_addresses():
@main.route("/<project_id>/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():
oldest_date = g.project.get_oldest_bill().date
newest_date = g.project.get_newest_bill().date
# Infinite iterator towards the past
newest_month = datetime.date(
year=newest_date.year, month=newest_date.month, day=1
)
all_months = (newest_month - relativedelta(months=i) for i in itertools.count())
# Stop when reaching one month before the first date
months = itertools.takewhile(
lambda x: x > oldest_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",
)

Expand Down

0 comments on commit 6c2cb73

Please sign in to comment.