Skip to content

Commit 33d014a

Browse files
committed
Migrate topics to tags
1 parent 19ee7d3 commit 33d014a

File tree

10 files changed

+125
-36
lines changed

10 files changed

+125
-36
lines changed

pgcommitfest/commitfest/forms.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class Meta:
9595
"modified",
9696
"lastmail",
9797
"subscribers",
98+
# TODO: Remove once we fully get rid of topics
99+
"topic",
98100
)
99101

100102
def __init__(self, *args, **kwargs):
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Generated by Django 4.2.19 on 2025-08-07 17:25
2+
3+
from django.db import migrations
4+
5+
6+
def create_missing_tags_and_map_topics(apps, schema_editor):
7+
"""
8+
Create missing tags and map existing topics to appropriate tags
9+
"""
10+
Tag = apps.get_model("commitfest", "Tag")
11+
Patch = apps.get_model("commitfest", "Patch")
12+
Topic = apps.get_model("commitfest", "Topic")
13+
14+
# Create missing tags
15+
Tag.objects.get_or_create(
16+
name="SQL Commands",
17+
defaults={
18+
"color": "#198754",
19+
"description": "SQL command implementation and enhancement",
20+
},
21+
)
22+
23+
Tag.objects.get_or_create(
24+
name="System Administration",
25+
defaults={
26+
"color": "#495057",
27+
"description": "System administration and configuration",
28+
},
29+
)
30+
31+
# Topic to Tag mapping
32+
topic_tag_mapping = {
33+
"Testing": "Testing",
34+
"Refactoring": "Refactoring Only",
35+
"Documentation": "Docs Only",
36+
"Code Comments": "Comments Only",
37+
"Bug Fixes": "Bugfix",
38+
"Performance": "Performance",
39+
"Security": "Security",
40+
"Monitoring & Control": "Monitoring",
41+
"Procedural Languages": "PL/pgSQL",
42+
"Replication & Recovery": "Physical Replication",
43+
"Clients": "libpq",
44+
"SQL Commands": "SQL Commands",
45+
"System Administration": "System Administration",
46+
# 'Miscellaneous' and 'Server Features' are left untagged
47+
}
48+
49+
# Apply tags to existing patches based on their topics
50+
for topic_name, tag_name in topic_tag_mapping.items():
51+
try:
52+
topic = Topic.objects.get(topic=topic_name)
53+
tag = Tag.objects.get(name=tag_name)
54+
55+
# Get all patches with this topic
56+
patches_with_topic = Patch.objects.filter(topic=topic)
57+
58+
# Add the corresponding tag to each patch
59+
for patch in patches_with_topic:
60+
patch.tags.add(tag)
61+
62+
print(
63+
f"Mapped {patches_with_topic.count()} patches from topic '{topic_name}' to tag '{tag_name}'"
64+
)
65+
66+
except Topic.DoesNotExist:
67+
print(f"Topic '{topic_name}' not found, skipping...")
68+
except Tag.DoesNotExist:
69+
print(f"Tag '{tag_name}' not found, skipping...")
70+
71+
72+
class Migration(migrations.Migration):
73+
dependencies = [
74+
("commitfest", "0015_cfbot_duplicate_task_fix"),
75+
]
76+
77+
operations = [
78+
migrations.RunPython(
79+
create_missing_tags_and_map_topics,
80+
migrations.RunPython.noop,
81+
),
82+
]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 4.2.19 on 2026-01-04 16:09
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("commitfest", "0016_migrate_topics_to_tags"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="patch",
15+
name="topic",
16+
field=models.ForeignKey(
17+
blank=True,
18+
null=True,
19+
on_delete=django.db.models.deletion.CASCADE,
20+
to="commitfest.topic",
21+
),
22+
),
23+
]

pgcommitfest/commitfest/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,9 @@ class Patch(models.Model, DiffableModel):
347347
name = models.CharField(
348348
max_length=500, blank=False, null=False, verbose_name="Description"
349349
)
350-
topic = models.ForeignKey(Topic, blank=False, null=False, on_delete=models.CASCADE)
350+
# Topic is deprecated, tags are used instead. For now this field is kept
351+
# for debugging purposes in case of problems with the migration.
352+
topic = models.ForeignKey(Topic, blank=True, null=True, on_delete=models.CASCADE)
351353
tags = models.ManyToManyField(Tag, related_name="patches", blank=True)
352354

353355
# One patch can be in multiple commitfests, if it has history

pgcommitfest/commitfest/templates/commitfest.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ <h3>{{p.is_open|yesno:"Active patches,Closed patches"}}</h3>
4646
<tbody>
4747
{%endifchanged%}
4848

49-
{%if grouping%}
50-
{%ifchanged p.topic%}
51-
<tr><th colspan="{%if user.is_staff%}13{%else%}12{%endif%}">{{p.topic}}</th></tr>
52-
{%endifchanged%}
53-
{%endif%}
5449
<tr>
5550
<td><a href="/patch/{{p.id}}/">{{p.name}}</a></td>
5651
<td>{{p.id}}</td>

pgcommitfest/commitfest/templates/home.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ <h3>{%if user.is_authenticated%}Open patches you are subscribed to{%elif p.is_op
151151
{%endifchanged%}
152152

153153
{%if grouping%}
154-
{%ifchanged p.topic%}
155-
<tr><th colspan="{%if user.is_authenticated %}13{%else%}12{%endif%}">{{p.topic}}</th></tr>
154+
{%ifchanged p.group_name%}
155+
<tr><th colspan="{%if user.is_authenticated %}13{%else%}12{%endif%}">{{p.group_name}}</th></tr>
156156
{%endifchanged%}
157157
{%endif%}
158158
<tr>

pgcommitfest/commitfest/templates/patch.html

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,6 @@
7575
Unknown
7676
{%endif%}
7777
</tr>
78-
<tr>
79-
<th>Topic</th>
80-
<td>{{patch.topic}}</td>
81-
</tr>
8278
<tr>
8379
<th>Tags</th>
8480
<td>

pgcommitfest/commitfest/tests/test_lookups.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,31 @@
33

44
import pytest
55

6-
from pgcommitfest.commitfest.models import Committer, Patch, PatchOnCommitFest, Topic
6+
from pgcommitfest.commitfest.models import Committer, Patch, PatchOnCommitFest
77

88
pytestmark = pytest.mark.django_db
99

1010

1111
@pytest.fixture
12-
def topic():
13-
"""Create a test topic."""
14-
return Topic.objects.create(topic="General")
15-
16-
17-
@pytest.fixture
18-
def patches_with_users(users, open_cf, topic):
12+
def patches_with_users(users, open_cf):
1913
"""Create patches with authors and reviewers in a commitfest."""
2014
# Alice is an author on patch 1
21-
patch1 = Patch.objects.create(name="Test Patch 1", topic=topic)
15+
patch1 = Patch.objects.create(name="Test Patch 1")
2216
patch1.authors.add(users["alice"])
2317
PatchOnCommitFest.objects.create(
2418
patch=patch1, commitfest=open_cf, enterdate=datetime.now()
2519
)
2620

2721
# Bob is a reviewer on patch 2
28-
patch2 = Patch.objects.create(name="Test Patch 2", topic=topic)
22+
patch2 = Patch.objects.create(name="Test Patch 2")
2923
patch2.reviewers.add(users["bob"])
3024
PatchOnCommitFest.objects.create(
3125
patch=patch2, commitfest=open_cf, enterdate=datetime.now()
3226
)
3327

3428
# Dave is a committer on patch 3
3529
dave_committer = Committer.objects.create(user=users["dave"])
36-
patch3 = Patch.objects.create(
37-
name="Test Patch 3", topic=topic, committer=dave_committer
38-
)
30+
patch3 = Patch.objects.create(name="Test Patch 3", committer=dave_committer)
3931
PatchOnCommitFest.objects.create(
4032
patch=patch3, commitfest=open_cf, enterdate=datetime.now()
4133
)

pgcommitfest/commitfest/tests/test_refresh_thread.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pytest
55

66
from pgcommitfest.commitfest.ajax import refresh_single_thread
7-
from pgcommitfest.commitfest.models import MailThread, Patch, Topic
7+
from pgcommitfest.commitfest.models import MailThread, Patch
88

99
pytestmark = pytest.mark.django_db
1010

@@ -14,7 +14,6 @@ def test_refresh_single_thread_updates_patch_lastmail():
1414
old_date = datetime(2024, 1, 1)
1515
new_date = datetime(2024, 6, 15)
1616

17-
topic = Topic.objects.create(topic="Test")
1817
thread = MailThread.objects.create(
1918
messageid="old@example.com",
2019
subject="Test",
@@ -25,7 +24,7 @@ def test_refresh_single_thread_updates_patch_lastmail():
2524
latestsubject="Test",
2625
latestmsgid="old@example.com",
2726
)
28-
p = Patch.objects.create(name="Test Patch", topic=topic, lastmail=old_date)
27+
p = Patch.objects.create(name="Test Patch", lastmail=old_date)
2928
p.mailthread_set.add(thread)
3029

3130
api_response = [

pgcommitfest/commitfest/views.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ def patchlist(request, cf, personalized=False):
442442
)
443443
THEN 'Patches that are ready for your review'
444444
ELSE 'Blocked on others'
445-
END AS topic,
445+
END AS group_name,
446446
cf.id AS cf_id,
447447
cf.name AS cf_name,
448448
cf.status AS cf_status,
@@ -465,7 +465,7 @@ def patchlist(request, cf, personalized=False):
465465
joins_str = "INNER JOIN commitfest_commitfest cf ON poc.commitfest_id=cf.id"
466466
groupby_str = "cf.id,"
467467
else:
468-
columns_str = "t.topic as topic,"
468+
columns_str = ""
469469
joins_str = ""
470470
groupby_str = ""
471471

@@ -507,7 +507,7 @@ def patchlist(request, cf, personalized=False):
507507
orderby_str = "poc.commitfest_id DESC, lastmail"
508508
else:
509509
if personalized:
510-
# First we sort by topic, to have the grouping work.
510+
# First we sort by group_name, to have the grouping work.
511511
# Then we show non-failing patches first, and the ones that are
512512
# shortest failing we show first. We consider patches in a closed
513513
# commitfest, as if they are failing since that commitfest was
@@ -516,7 +516,7 @@ def patchlist(request, cf, personalized=False):
516516
# progress" commitfest before ones in the "Open" commitfest.
517517
# And then to break ties, we put ones with the most recent email at
518518
# the top.
519-
orderby_str = """topic DESC,
519+
orderby_str = """group_name DESC,
520520
COALESCE(
521521
branch.failing_since,
522522
CASE WHEN cf.status = %(cf_closed_status)s
@@ -526,7 +526,7 @@ def patchlist(request, cf, personalized=False):
526526
lastmail DESC"""
527527
whereparams["cf_closed_status"] = CommitFest.STATUS_CLOSED
528528
else:
529-
orderby_str = "topic, created"
529+
orderby_str = "created"
530530
sortkey = 0
531531

532532
if not has_filter and sortkey == 0 and request.GET:
@@ -584,13 +584,12 @@ def patchlist(request, cf, personalized=False):
584584
)
585585
FROM commitfest_patch p
586586
INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id=p.id
587-
INNER JOIN commitfest_topic t ON t.id=p.topic_id
588587
{joins_str}
589588
LEFT JOIN auth_user committer ON committer.id=p.committer_id
590589
LEFT JOIN commitfest_targetversion v ON p.targetversion_id=v.id
591590
LEFT JOIN commitfest_cfbotbranch branch ON branch.patch_id=p.id
592591
WHERE {where_str}
593-
GROUP BY p.id, poc.id, {groupby_str} committer.id, t.id, v.version, branch.patch_id
592+
GROUP BY p.id, poc.id, {groupby_str} committer.id, v.version, branch.patch_id
594593
ORDER BY is_open DESC, {orderby_str}""",
595594
params,
596595
)
@@ -648,7 +647,6 @@ def commitfest(request, cfid):
648647
"all_tags": {t.id: t for t in Tag.objects.all()},
649648
"has_filter": patch_list.has_filter,
650649
"title": f"{cf.title} ({cf.periodstring})",
651-
"grouping": patch_list.sortkey == 0,
652650
"sortkey": patch_list.sortkey,
653651
"openpatchids": [p["id"] for p in patch_list.patches if p["is_open"]],
654652
"header_activity": "Activity log",

0 commit comments

Comments
 (0)