Skip to content

Commit f05f885

Browse files
JelteFpolobo
andcommitted
Introduce Drafts page and automatic CF creation
This introduce a new type of CommitFest a "Draft" CommitFest. This CommitFest is never "In Progress", but it can be open. It exists for a year. It opens when the final regular CommitFest of the year becomes "In Progress" and stays open for exactly a year. It never becomes "In Progress" itself. Adding a second type of CommitFest also needed a redesign of quite a few things, like the homepage. Also management of the CommitFests needed to be made a bit easier, so admins don't forget to close/create Draft CommitFests. So now, closing/opening/creating CommitFests is done automatically when the time is right for that. A help page is also introduced to explain the CommitFest app. The naming of CommitFests has been changed too. Since we now have a Draft CF every year that needs a name, it seemed reasonable to align the names of the other CFs with that too. So each PG release cycle now has 5 regular commitfests that are called: - PG18-1 - PG18-2 - PG18-3 - PG18-4 - PG18-Final And a single Draft CommitFest, called: - PG18-Draft Finally, it also adds a small initial API endpoint for the CFBot, to request the commitfests that need CI runs. Future PRs will extend this API surface to also include/allow requesting the actual patches that CI should run on. Co-Authored-By: David G. Johnston <[email protected]>
1 parent d17a0a8 commit f05f885

File tree

14 files changed

+781
-172
lines changed

14 files changed

+781
-172
lines changed

pgcommitfest/commitfest/apiv1.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from django.http import (
2+
HttpResponse,
3+
)
4+
5+
import json
6+
from datetime import date, datetime, timedelta, timezone
7+
8+
from .models import (
9+
CommitFest,
10+
)
11+
12+
13+
def datetime_serializer(obj):
14+
if isinstance(obj, date):
15+
return obj.isoformat()
16+
17+
if isinstance(obj, datetime):
18+
return obj.replace(tzinfo=timezone.utc).isoformat()
19+
20+
if hasattr(obj, "to_json"):
21+
return obj.to_json()
22+
23+
raise TypeError(f"Type {type(obj)} not serializable to JSON")
24+
25+
26+
def api_response(payload, status=200, content_type="application/json"):
27+
response = HttpResponse(
28+
json.dumps(payload, default=datetime_serializer), status=status
29+
)
30+
response["Content-Type"] = content_type
31+
response["Access-Control-Allow-Origin"] = "*"
32+
return response
33+
34+
35+
def commitfestst_that_need_ci(request):
36+
cfs = CommitFest.relevant_commitfests()
37+
38+
# We continue to run CI on the previous commitfest for a week after it ends
39+
# to give people some time to move patches over to the next one.
40+
if cfs["previous"].enddate <= datetime.now(timezone.utc).date() - timedelta(days=7):
41+
del cfs["previous"]
42+
43+
del cfs["next_open"]
44+
del cfs["final"]
45+
46+
return api_response({"commitfests": cfs})

pgcommitfest/commitfest/fixtures/auth_data.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,41 @@
8888
"groups": [],
8989
"user_permissions": []
9090
}
91+
},
92+
{
93+
"model": "auth.user",
94+
"pk": 6,
95+
"fields": {
96+
"password": "",
97+
"last_login": null,
98+
"is_superuser": false,
99+
"username": "prolific-author",
100+
"first_name": "Prolific",
101+
"last_name": "Author",
102+
"email": "",
103+
"is_staff": false,
104+
"is_active": true,
105+
"date_joined": "2025-01-01T00:00:00",
106+
"groups": [],
107+
"user_permissions": []
108+
}
109+
},
110+
{
111+
"model": "auth.user",
112+
"pk": 7,
113+
"fields": {
114+
"password": "",
115+
"last_login": null,
116+
"is_superuser": false,
117+
"username": "prolific-reviewer",
118+
"first_name": "Prolific",
119+
"last_name": "Reviewer",
120+
"email": "",
121+
"is_staff": false,
122+
"is_active": true,
123+
"date_joined": "2025-01-01T00:00:00",
124+
"groups": [],
125+
"user_permissions": []
126+
}
91127
}
92128
]

pgcommitfest/commitfest/fixtures/commitfest_data.json

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,40 +24,44 @@
2424
"model": "commitfest.commitfest",
2525
"pk": 1,
2626
"fields": {
27-
"name": "Sample Old Commitfest",
27+
"name": "PG18-3",
2828
"status": 4,
29-
"startdate": "2024-05-01",
30-
"enddate": "2024-05-31"
29+
"startdate": "2024-11-01",
30+
"enddate": "2024-11-30",
31+
"draft": false
3132
}
3233
},
3334
{
3435
"model": "commitfest.commitfest",
3536
"pk": 2,
3637
"fields": {
37-
"name": "Sample In Progress Commitfest",
38+
"name": "PG18-4",
3839
"status": 3,
3940
"startdate": "2025-01-01",
40-
"enddate": "2025-02-28"
41+
"enddate": "2025-01-31",
42+
"draft": false
4143
}
4244
},
4345
{
4446
"model": "commitfest.commitfest",
4547
"pk": 3,
4648
"fields": {
47-
"name": "Sample Open Commitfest",
49+
"name": "PG18-Final",
4850
"status": 2,
4951
"startdate": "2025-03-01",
50-
"enddate": "2025-03-31"
52+
"enddate": "2025-03-31",
53+
"draft": false
5154
}
5255
},
5356
{
5457
"model": "commitfest.commitfest",
5558
"pk": 4,
5659
"fields": {
57-
"name": "Sample Future Commitfest",
58-
"status": 1,
59-
"startdate": "2025-05-01",
60-
"enddate": "2025-05-31"
60+
"name": "PG18-Drafts",
61+
"status": 2,
62+
"startdate": "2024-03-01",
63+
"enddate": "2025-02-28",
64+
"draft": true
6165
}
6266
},
6367
{
@@ -237,6 +241,33 @@
237241
]
238242
}
239243
},
244+
{
245+
"model": "commitfest.patch",
246+
"pk": 8,
247+
"fields": {
248+
"name": "Test DGJ Multi-Author and Reviewer",
249+
"topic": 3,
250+
"wikilink": "",
251+
"gitlink": "",
252+
"targetversion": 1,
253+
"committer": 4,
254+
"created": "2025-02-01T00:00:00",
255+
"modified": "2025-02-01T00:00:00",
256+
"lastmail": "2025-02-01T00:00:00",
257+
"authors": [
258+
3,
259+
6
260+
],
261+
"reviewers": [
262+
1,
263+
7
264+
],
265+
"subscribers": [],
266+
"mailthread_set": [
267+
8
268+
]
269+
}
270+
},
240271
{
241272
"model": "commitfest.patchoncommitfest",
242273
"pk": 1,
@@ -325,6 +356,17 @@
325356
"status": 1
326357
}
327358
},
359+
{
360+
"model": "commitfest.patchoncommitfest",
361+
"pk": 9,
362+
"fields": {
363+
"patch": 8,
364+
"commitfest": 4,
365+
"enterdate": "2025-02-01T00:00:00",
366+
"leavedate": null,
367+
"status": 1
368+
}
369+
},
328370
{
329371
"model": "commitfest.patchhistory",
330372
"pk": 1,
@@ -632,6 +674,33 @@
632674
"latestmsgid": "example@message-31"
633675
}
634676
},
677+
{
678+
"model": "commitfest.mailthread",
679+
"pk": 8,
680+
"fields": {
681+
"messageid": "dgj-example@message-08",
682+
"subject": "Test DGJ Multi-Author and Reviewer",
683+
"firstmessage": "2025-02-01T00:00:00",
684+
"firstauthor": "[email protected]",
685+
"latestmessage": "2025-02-01T00:00:00",
686+
"latestauthor": "[email protected]",
687+
"latestsubject": "Test DGJ Multi-Author and Reviewer",
688+
"latestmsgid": "dgj-example@message-08"
689+
}
690+
},
691+
{
692+
"model": "commitfest.mailthreadattachment",
693+
"pk": 8,
694+
"fields": {
695+
"mailthread": 8,
696+
"messageid": "dgj-example@message-08",
697+
"attachmentid": 1,
698+
"filename": "v1-0001-content.patch",
699+
"date": "2025-02-01T00:00:00",
700+
"author": "[email protected]",
701+
"ispatch": true
702+
}
703+
},
635704
{
636705
"model": "commitfest.patchstatus",
637706
"pk": 1,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated by Django 4.2.19 on 2025-06-08 10:47
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("commitfest", "0010_add_failing_since_column"),
9+
]
10+
11+
operations = [
12+
migrations.AddField(
13+
model_name="commitfest",
14+
name="draft",
15+
field=models.BooleanField(default=False),
16+
),
17+
migrations.AlterField(
18+
model_name="commitfest",
19+
name="status",
20+
field=models.IntegerField(
21+
choices=[(2, "Open"), (3, "In Progress"), (4, "Closed")], default=2
22+
),
23+
),
24+
]
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from django.db import migrations
2+
3+
4+
class Migration(migrations.Migration):
5+
dependencies = [
6+
("commitfest", "0011_add_draft_remove_future"),
7+
]
8+
operations = [
9+
migrations.RunSQL(
10+
"""
11+
CREATE UNIQUE INDEX cf_enforce_maxoneopen_idx
12+
ON commitfest_commitfest (status, draft)
13+
WHERE status not in (1,4);
14+
""",
15+
reverse_sql="""
16+
DROP INDEX IF EXISTS cf_enforce_maxoneopen_idx;
17+
""",
18+
),
19+
migrations.RunSQL(
20+
"""
21+
CREATE UNIQUE INDEX poc_enforce_maxoneoutcome_idx
22+
ON commitfest_patchoncommitfest (patch_id)
23+
WHERE status not in (5);
24+
""",
25+
reverse_sql="""
26+
DROP INDEX IF EXISTS poc_enforce_maxoneoutcome_idx;
27+
""",
28+
),
29+
migrations.RunSQL(
30+
"""
31+
ALTER TABLE commitfest_patchoncommitfest
32+
ADD CONSTRAINT status_and_leavedate_correlation
33+
CHECK ((status IN (4,5,6,7,8)) = (leavedate IS NOT NULL));
34+
""",
35+
reverse_sql="""
36+
ALTER TABLE commitfest_patchoncommitfest
37+
DROP CONSTRAINT IF EXISTS status_and_leavedate_correlation;
38+
""",
39+
),
40+
migrations.RunSQL(
41+
"""
42+
COMMENT ON COLUMN commitfest_patchoncommitfest.leavedate IS
43+
$$A leave date is recorded in two situations, both of which
44+
means this particular patch-cf combination became inactive
45+
on the corresponding date. For status 5 the patch was moved
46+
to some other cf. For 4,6,7, and 8, this was the final cf.
47+
$$
48+
""",
49+
reverse_sql="""
50+
COMMENT ON COLUMN commitfest_patchoncommitfest.leavedate IS NULL;
51+
""",
52+
),
53+
migrations.RunSQL(
54+
"""
55+
COMMENT ON TABLE commitfest_patchoncommitfest IS
56+
$$This is a re-entrant table: patches may become associated
57+
with a given cf multiple times, resetting the entrydate and clearing
58+
the leavedate each time. Non-final statuses never have a leavedate
59+
while final statuses always do. The final status of 5 (moved) is
60+
special in that all but one of the rows a patch has in this table
61+
must have it as the status.
62+
$$
63+
""",
64+
reverse_sql="""
65+
COMMENT ON TABLE commitfest_patchoncommitfest IS NULL;
66+
""",
67+
),
68+
]

0 commit comments

Comments
 (0)