Skip to content

Commit 07ac406

Browse files
committed
More changes
1 parent 6cfd8ee commit 07ac406

File tree

5 files changed

+320
-9
lines changed

5 files changed

+320
-9
lines changed

pgcommitfest/commitfest/templates/help.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ <h2>Commitfest</h2>
1717

1818
<h3>Commitfest closure</h3>
1919
<p>
20-
When a Commitfest closes, patches that have been active recently are automatically moved to the next Commitfest. A patch is considered "active" if it has had email activity in the past 30 days and has not been failing CI for more than 21 days. Patches that are not automatically moved will stay in the closed Commitfest, where they will no longer be picked up by CI. Authors of such patches that have enabled "Notify on all where author" in their profile settings will receive an email notification asking them to either move the patch to the next Commitfest or close it with an appropriate status.
20+
When a Commitfest closes, patches that have been active recently are automatically moved to the next Commitfest. A patch is considered "active" if it has had email activity in the past {{auto_move_email_activity_days}} days and has not been failing CI for more than {{auto_move_max_failing_days}} days. Patches that are not automatically moved will stay in the closed Commitfest, where they will no longer be picked up by CI. Authors of such patches that have enabled "Notify on all where author" in their profile settings will receive an email notification asking them to either move the patch to the next Commitfest or close it with an appropriate status.
2121
</p>
2222

2323
<h2>Patches</h2>

pgcommitfest/commitfest/templates/mail/commitfest_closure.txt

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,8 @@ You have {{patches|length}} open patch{{patches|length|pluralize:"es"}} that nee
99
https://commitfest.postgresql.org/patch/{{poc.patch.id}}/
1010
{% endfor %}
1111

12-
Please take action on {{patches|length|pluralize:"these patches,this patch"}}:
12+
Please take action on {{patches|length|pluralize:"these patches,this patch"}} by doing either of the following:
1313

1414
1. If you want to continue working on {{patches|length|pluralize:"them,it"}}, move {{patches|length|pluralize:"them,it"}} to the next commitfest{% if next_cf %}: {{next_cf_url}}{% endif %}
1515

1616
2. If you no longer wish to pursue {{patches|length|pluralize:"these patches,this patch"}}, please close {{patches|length|pluralize:"them,it"}} with an appropriate status (Withdrawn, Returned with feedback, etc.)
17-
18-
{% if next_cf %}The next commitfest is {{next_cf.name}}, which runs from {{next_cf.startdate}} to {{next_cf.enddate}}.{% else %}Please check https://commitfest.postgresql.org/ for upcoming commitfests.{% endif %}
19-
20-
Thank you for your contributions to PostgreSQL!
21-
22-
--
23-
This is an automated message from the PostgreSQL Commitfest application.
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
from datetime import date, datetime
2+
3+
import pytest
4+
from freezegun import freeze_time
5+
6+
from pgcommitfest.commitfest.models import (
7+
CommitFest,
8+
Patch,
9+
PatchOnCommitFest,
10+
Topic,
11+
)
12+
from pgcommitfest.userprofile.models import UserProfile
13+
14+
15+
@pytest.fixture
16+
def topic(db):
17+
return Topic.objects.create(topic="General")
18+
19+
20+
@pytest.fixture
21+
def alice(db, django_user_model):
22+
user = django_user_model.objects.create_user(
23+
username="alice",
24+
email="alice@example.com",
25+
first_name="Alice",
26+
last_name="Smith",
27+
)
28+
UserProfile.objects.create(user=user, notify_all_author=True)
29+
return user
30+
31+
32+
def create_closed_cf(name, startdate, enddate):
33+
"""Helper to create a closed CF for padding."""
34+
return CommitFest.objects.create(
35+
name=name,
36+
status=CommitFest.STATUS_CLOSED,
37+
startdate=startdate,
38+
enddate=enddate,
39+
)
40+
41+
42+
@pytest.mark.django_db
43+
@freeze_time("2024-12-05")
44+
def test_inprogress_cf_closes_when_enddate_passed(topic, alice):
45+
"""When an in_progress CF's enddate has passed, it should be closed."""
46+
# Create some closed CFs for padding (relevant_commitfests needs history)
47+
create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31))
48+
create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30))
49+
50+
# Create an in_progress CF that ended
51+
in_progress_cf = CommitFest.objects.create(
52+
name="2024-11",
53+
status=CommitFest.STATUS_INPROGRESS,
54+
startdate=date(2024, 11, 1),
55+
enddate=date(2024, 11, 30),
56+
)
57+
# Create the next open CF (required for auto_move)
58+
open_cf = CommitFest.objects.create(
59+
name="2025-01",
60+
status=CommitFest.STATUS_OPEN,
61+
startdate=date(2025, 1, 1),
62+
enddate=date(2025, 1, 31),
63+
)
64+
# Create draft CF
65+
CommitFest.objects.create(
66+
name="2025-draft",
67+
status=CommitFest.STATUS_OPEN,
68+
startdate=date(2024, 7, 1),
69+
enddate=date(2025, 3, 31),
70+
draft=True,
71+
)
72+
73+
# Create a patch with recent activity that should be auto-moved
74+
patch = Patch.objects.create(
75+
name="Test Patch",
76+
topic=topic,
77+
lastmail=datetime(2024, 11, 25),
78+
)
79+
patch.authors.add(alice)
80+
PatchOnCommitFest.objects.create(
81+
patch=patch,
82+
commitfest=in_progress_cf,
83+
enterdate=datetime.now(),
84+
status=PatchOnCommitFest.STATUS_REVIEW,
85+
)
86+
87+
CommitFest._refresh_relevant_commitfests(for_update=False)
88+
89+
in_progress_cf.refresh_from_db()
90+
assert in_progress_cf.status == CommitFest.STATUS_CLOSED
91+
92+
# Patch should have been moved to the open CF
93+
patch.refresh_from_db()
94+
assert patch.current_commitfest().id == open_cf.id
95+
96+
97+
@pytest.mark.django_db
98+
@freeze_time("2025-01-15")
99+
def test_open_cf_becomes_inprogress_when_startdate_reached():
100+
"""When an open CF's startdate is reached, it becomes in_progress."""
101+
# Create some closed CFs for padding
102+
create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31))
103+
create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30))
104+
create_closed_cf("2024-11", date(2024, 11, 1), date(2024, 11, 30))
105+
106+
open_cf = CommitFest.objects.create(
107+
name="2025-01",
108+
status=CommitFest.STATUS_OPEN,
109+
startdate=date(2025, 1, 1),
110+
enddate=date(2025, 1, 31),
111+
)
112+
# Create draft CF
113+
CommitFest.objects.create(
114+
name="2025-draft",
115+
status=CommitFest.STATUS_OPEN,
116+
startdate=date(2024, 7, 1),
117+
enddate=date(2025, 3, 31),
118+
draft=True,
119+
)
120+
121+
CommitFest._refresh_relevant_commitfests(for_update=False)
122+
123+
open_cf.refresh_from_db()
124+
assert open_cf.status == CommitFest.STATUS_INPROGRESS
125+
126+
# A new open CF should have been created
127+
new_open = CommitFest.objects.filter(
128+
status=CommitFest.STATUS_OPEN, draft=False
129+
).first()
130+
assert new_open is not None
131+
assert new_open.startdate > open_cf.enddate
132+
133+
134+
@pytest.mark.django_db
135+
@freeze_time("2025-02-05")
136+
def test_open_cf_closes_when_enddate_passed(topic, alice):
137+
"""When an open CF's enddate has passed (skipping in_progress), it closes."""
138+
# Create some closed CFs for padding
139+
create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31))
140+
create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30))
141+
create_closed_cf("2024-11", date(2024, 11, 1), date(2024, 11, 30))
142+
143+
open_cf = CommitFest.objects.create(
144+
name="2025-01",
145+
status=CommitFest.STATUS_OPEN,
146+
startdate=date(2025, 1, 1),
147+
enddate=date(2025, 1, 31),
148+
)
149+
# Create draft CF
150+
CommitFest.objects.create(
151+
name="2025-draft",
152+
status=CommitFest.STATUS_OPEN,
153+
startdate=date(2024, 7, 1),
154+
enddate=date(2025, 3, 31),
155+
draft=True,
156+
)
157+
158+
# Create a patch with recent activity
159+
patch = Patch.objects.create(
160+
name="Test Patch",
161+
topic=topic,
162+
lastmail=datetime(2025, 1, 25),
163+
)
164+
patch.authors.add(alice)
165+
PatchOnCommitFest.objects.create(
166+
patch=patch,
167+
commitfest=open_cf,
168+
enterdate=datetime.now(),
169+
status=PatchOnCommitFest.STATUS_REVIEW,
170+
)
171+
172+
CommitFest._refresh_relevant_commitfests(for_update=False)
173+
174+
open_cf.refresh_from_db()
175+
assert open_cf.status == CommitFest.STATUS_CLOSED
176+
177+
# A new open CF should have been created
178+
new_open = CommitFest.objects.filter(
179+
status=CommitFest.STATUS_OPEN, draft=False
180+
).first()
181+
assert new_open is not None
182+
183+
# Patch should have been moved to the new open CF
184+
patch.refresh_from_db()
185+
assert patch.current_commitfest().id == new_open.id
186+
187+
188+
@pytest.mark.django_db
189+
@freeze_time("2025-01-15")
190+
def test_draft_cf_created_when_missing():
191+
"""When no draft CF exists, one should be created."""
192+
# Create closed CFs for padding
193+
create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31))
194+
create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30))
195+
create_closed_cf("2024-11", date(2024, 11, 1), date(2024, 11, 30))
196+
197+
# Create only regular CFs
198+
CommitFest.objects.create(
199+
name="2025-01",
200+
status=CommitFest.STATUS_OPEN,
201+
startdate=date(2025, 3, 1),
202+
enddate=date(2025, 3, 31),
203+
)
204+
205+
assert not CommitFest.objects.filter(draft=True).exists()
206+
207+
CommitFest._refresh_relevant_commitfests(for_update=False)
208+
209+
# A draft CF should have been created
210+
draft_cf = CommitFest.objects.filter(draft=True).first()
211+
assert draft_cf is not None
212+
assert draft_cf.status == CommitFest.STATUS_OPEN
213+
214+
215+
@pytest.mark.django_db
216+
@freeze_time("2025-04-05")
217+
def test_draft_cf_closes_when_enddate_passed(topic, alice):
218+
"""When a draft CF's enddate has passed, it should be closed."""
219+
# Create closed CFs for padding
220+
create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31))
221+
create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30))
222+
create_closed_cf("2024-11", date(2024, 11, 1), date(2024, 11, 30))
223+
224+
# Create an open regular CF (required)
225+
CommitFest.objects.create(
226+
name="2025-03",
227+
status=CommitFest.STATUS_OPEN,
228+
startdate=date(2025, 5, 1),
229+
enddate=date(2025, 5, 31),
230+
)
231+
232+
# Create a draft CF that ended
233+
draft_cf = CommitFest.objects.create(
234+
name="2025-draft",
235+
status=CommitFest.STATUS_OPEN,
236+
startdate=date(2025, 1, 1),
237+
enddate=date(2025, 3, 31),
238+
draft=True,
239+
)
240+
241+
# Create a patch with recent activity
242+
patch = Patch.objects.create(
243+
name="Draft Patch",
244+
topic=topic,
245+
lastmail=datetime(2025, 3, 25),
246+
)
247+
patch.authors.add(alice)
248+
PatchOnCommitFest.objects.create(
249+
patch=patch,
250+
commitfest=draft_cf,
251+
enterdate=datetime.now(),
252+
status=PatchOnCommitFest.STATUS_REVIEW,
253+
)
254+
255+
CommitFest._refresh_relevant_commitfests(for_update=False)
256+
257+
draft_cf.refresh_from_db()
258+
assert draft_cf.status == CommitFest.STATUS_CLOSED
259+
260+
# A new draft CF should have been created
261+
new_draft = CommitFest.objects.filter(
262+
draft=True, status=CommitFest.STATUS_OPEN
263+
).first()
264+
assert new_draft is not None
265+
assert new_draft.startdate > draft_cf.enddate
266+
267+
# Patch should have been moved to the new draft CF
268+
patch.refresh_from_db()
269+
assert patch.current_commitfest().id == new_draft.id
270+
271+
272+
@pytest.mark.django_db
273+
@freeze_time("2025-01-15")
274+
def test_no_changes_when_up_to_date():
275+
"""When commitfests are up to date, no changes should be made."""
276+
# Create closed CFs for padding
277+
create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31))
278+
create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30))
279+
280+
# Create CFs that are all up to date
281+
in_progress_cf = CommitFest.objects.create(
282+
name="2025-01",
283+
status=CommitFest.STATUS_INPROGRESS,
284+
startdate=date(2025, 1, 1),
285+
enddate=date(2025, 1, 31),
286+
)
287+
open_cf = CommitFest.objects.create(
288+
name="2025-03",
289+
status=CommitFest.STATUS_OPEN,
290+
startdate=date(2025, 3, 1),
291+
enddate=date(2025, 3, 31),
292+
)
293+
draft_cf = CommitFest.objects.create(
294+
name="2025-draft",
295+
status=CommitFest.STATUS_OPEN,
296+
startdate=date(2025, 1, 1),
297+
enddate=date(2025, 3, 31),
298+
draft=True,
299+
)
300+
301+
initial_cf_count = CommitFest.objects.count()
302+
303+
CommitFest._refresh_relevant_commitfests(for_update=False)
304+
305+
# All statuses should remain unchanged
306+
in_progress_cf.refresh_from_db()
307+
open_cf.refresh_from_db()
308+
draft_cf.refresh_from_db()
309+
310+
assert in_progress_cf.status == CommitFest.STATUS_INPROGRESS
311+
assert open_cf.status == CommitFest.STATUS_OPEN
312+
assert draft_cf.status == CommitFest.STATUS_OPEN
313+
314+
# No new CFs should have been created
315+
assert CommitFest.objects.count() == initial_cf_count

pgcommitfest/commitfest/views.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ def help(request):
174174
"help.html",
175175
{
176176
"title": "What is the Commitfest app?",
177+
"auto_move_email_activity_days": settings.AUTO_MOVE_EMAIL_ACTIVITY_DAYS,
178+
"auto_move_max_failing_days": settings.AUTO_MOVE_MAX_FAILING_DAYS,
177179
},
178180
)
179181

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dev = [
2020
"djhtml",
2121
"pytest",
2222
"pytest-django",
23+
"freezegun",
2324
]
2425

2526
[tool.setuptools.packages.find]

0 commit comments

Comments
 (0)