Skip to content

Commit 8672f1c

Browse files
flodolomathjazz
andauthored
Include last day of month in monthly locale stats (mozilla#3993)
Co-authored-by: Matjaž Horvat <matjaz.horvat@gmail.com>
1 parent 1991216 commit 8672f1c

2 files changed

Lines changed: 61 additions & 7 deletions

File tree

pontoon/messaging/emails.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,26 @@ def _get_monthly_locale_actions(months_ago):
7272

7373
def _get_monthly_locale_stats(months_ago):
7474
month_date = timezone.now() - relativedelta(months=months_ago)
75-
last_day = calendar.monthrange(month_date.year, month_date.month)[1]
7675

77-
snapshots = LocaleInsightsSnapshot.objects.filter(
78-
created_at__day=last_day,
79-
created_at__month=month_date.month,
80-
created_at__year=month_date.year,
76+
# collect_insights() takes data snapshots at the beginning of the day,
77+
# every day. To represent the state of the past month, we should
78+
# use the snapshot from the first day of the current month.
79+
next_month = month_date + relativedelta(months=1)
80+
81+
snapshots = list(
82+
LocaleInsightsSnapshot.objects.filter(
83+
created_at__day=1,
84+
created_at__month=next_month.month,
85+
created_at__year=next_month.year,
86+
)
8187
)
8288

89+
if not snapshots:
90+
raise LookupError(
91+
f"No LocaleInsightsSnapshot found for "
92+
f"{next_month.year}-{next_month.month:02d}-01."
93+
)
94+
8395
return {snapshot.locale_id: snapshot for snapshot in snapshots}
8496

8597

@@ -108,7 +120,7 @@ def _get_monthly_locale_contributors(locales, months_ago):
108120
}
109121

110122
# Get all contributors in the given month,
111-
# grouped by locale and orderd by contribution count
123+
# grouped by locale and ordered by contribution count
112124
monthly_contributors = (
113125
actions.filter(
114126
created_at__month=month_date.month,

pontoon/messaging/tests/test_emails.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1+
from datetime import date
12
from unittest.mock import patch
23

34
import pytest
45

56
from django.test.client import RequestFactory
7+
from django.utils import timezone
68

7-
from pontoon.messaging.emails import send_verification_email
9+
from pontoon.insights.models import LocaleInsightsSnapshot
10+
from pontoon.messaging.emails import (
11+
_get_monthly_locale_stats,
12+
send_verification_email,
13+
)
14+
from pontoon.test.factories import LocaleFactory
815

916

1017
@pytest.mark.django_db
@@ -21,3 +28,38 @@ def test_send_verification_email(member):
2128
kwargs = mock_email_message.call_args.kwargs
2229
assert link in kwargs["body"]
2330
assert kwargs["to"] == [request.user.email]
31+
32+
33+
@pytest.mark.django_db
34+
def test_get_monthly_locale_stats_uses_end_of_month_snapshot():
35+
locale = LocaleFactory(code="x-test", name="Test Language")
36+
37+
# Simulate 6 strings added on October 30.
38+
LocaleInsightsSnapshot.objects.create(
39+
locale=locale,
40+
created_at=date(2025, 10, 31),
41+
total_strings=100,
42+
approved_strings=94,
43+
completion=94.0,
44+
)
45+
46+
# Nov 1 snapshot is taken at midnight, capturing end of Oct 31.
47+
# The 6 strings were translated.
48+
snapshot_nov_1 = LocaleInsightsSnapshot.objects.create(
49+
locale=locale,
50+
created_at=date(2025, 11, 1),
51+
total_strings=100,
52+
approved_strings=100,
53+
completion=100.0,
54+
)
55+
56+
with patch("pontoon.messaging.emails.timezone") as mock_tz:
57+
mock_tz.now.return_value = timezone.datetime(
58+
2025, 11, 1, 6, 30, 0, tzinfo=timezone.utc
59+
)
60+
result = _get_monthly_locale_stats(months_ago=1)
61+
62+
assert locale.pk in result
63+
assert result[locale.pk].pk == snapshot_nov_1.pk
64+
assert result[locale.pk].approved_strings == 100
65+
assert result[locale.pk].completion == 100.0

0 commit comments

Comments
 (0)