Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pgcommitfest/commitfest/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ class Meta:
"modified",
"lastmail",
"subscribers",
# TODO: Remove once we fully get rid of topics
"topic",
)

def __init__(self, *args, **kwargs):
Expand Down
82 changes: 82 additions & 0 deletions pgcommitfest/commitfest/migrations/0016_migrate_topics_to_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Generated by Django 4.2.19 on 2025-08-07 17:25

from django.db import migrations


def create_missing_tags_and_map_topics(apps, schema_editor):
"""
Create missing tags and map existing topics to appropriate tags
"""
Tag = apps.get_model("commitfest", "Tag")
Patch = apps.get_model("commitfest", "Patch")
Topic = apps.get_model("commitfest", "Topic")

# Create missing tags
Tag.objects.get_or_create(
name="SQL Commands",
defaults={
"color": "#198754",
"description": "SQL command implementation and enhancement",
},
)

Tag.objects.get_or_create(
name="System Administration",
defaults={
"color": "#495057",
"description": "System administration and configuration",
},
)

# Topic to Tag mapping
topic_tag_mapping = {
"Testing": "Testing",
"Refactoring": "Refactoring Only",
"Documentation": "Docs Only",
"Code Comments": "Comments Only",
"Bug Fixes": "Bugfix",
"Performance": "Performance",
"Security": "Security",
"Monitoring & Control": "Monitoring",
"Procedural Languages": "PL/pgSQL",
"Replication & Recovery": "Physical Replication",
"Clients": "libpq",
"SQL Commands": "SQL Commands",
"System Administration": "System Administration",
# 'Miscellaneous' and 'Server Features' are left untagged
}

# Apply tags to existing patches based on their topics
for topic_name, tag_name in topic_tag_mapping.items():
try:
topic = Topic.objects.get(topic=topic_name)
tag = Tag.objects.get(name=tag_name)

# Get all patches with this topic
patches_with_topic = Patch.objects.filter(topic=topic)

# Add the corresponding tag to each patch
for patch in patches_with_topic:
patch.tags.add(tag)

print(
f"Mapped {patches_with_topic.count()} patches from topic '{topic_name}' to tag '{tag_name}'"
)

except Topic.DoesNotExist:
print(f"Topic '{topic_name}' not found, skipping...")
except Tag.DoesNotExist:
print(f"Tag '{tag_name}' not found, skipping...")


class Migration(migrations.Migration):
dependencies = [
("commitfest", "0015_cfbot_duplicate_task_fix"),
]

operations = [
migrations.RunPython(
create_missing_tags_and_map_topics,
migrations.RunPython.noop,
),
]
23 changes: 23 additions & 0 deletions pgcommitfest/commitfest/migrations/0017_make_topic_optional.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.19 on 2026-01-04 16:09

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("commitfest", "0016_migrate_topics_to_tags"),
]

operations = [
migrations.AlterField(
model_name="patch",
name="topic",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="commitfest.topic",
),
),
]
4 changes: 3 additions & 1 deletion pgcommitfest/commitfest/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,9 @@ class Patch(models.Model, DiffableModel):
name = models.CharField(
max_length=500, blank=False, null=False, verbose_name="Description"
)
topic = models.ForeignKey(Topic, blank=False, null=False, on_delete=models.CASCADE)
# Topic is deprecated, tags are used instead. For now this field is kept
# for debugging purposes in case of problems with the migration.
topic = models.ForeignKey(Topic, blank=True, null=True, on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, related_name="patches", blank=True)

# One patch can be in multiple commitfests, if it has history
Expand Down
5 changes: 0 additions & 5 deletions pgcommitfest/commitfest/templates/commitfest.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,6 @@ <h3>{{p.is_open|yesno:"Active patches,Closed patches"}}</h3>
<tbody>
{%endifchanged%}

{%if grouping%}
{%ifchanged p.topic%}
<tr><th colspan="{%if user.is_staff%}13{%else%}12{%endif%}">{{p.topic}}</th></tr>
{%endifchanged%}
{%endif%}
<tr>
<td><a href="/patch/{{p.id}}/">{{p.name}}</a></td>
<td>{{p.id}}</td>
Expand Down
4 changes: 2 additions & 2 deletions pgcommitfest/commitfest/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ <h3>{%if user.is_authenticated%}Open patches you are subscribed to{%elif p.is_op
{%endifchanged%}

{%if grouping%}
{%ifchanged p.topic%}
<tr><th colspan="{%if user.is_authenticated %}13{%else%}12{%endif%}">{{p.topic}}</th></tr>
{%ifchanged p.group_name%}
<tr><th colspan="{%if user.is_authenticated %}13{%else%}12{%endif%}">{{p.group_name}}</th></tr>
{%endifchanged%}
{%endif%}
<tr>
Expand Down
4 changes: 0 additions & 4 deletions pgcommitfest/commitfest/templates/patch.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@
Unknown
{%endif%}
</tr>
<tr>
<th>Topic</th>
<td>{{patch.topic}}</td>
</tr>
<tr>
<th>Tags</th>
<td>
Expand Down
18 changes: 5 additions & 13 deletions pgcommitfest/commitfest/tests/test_lookups.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,31 @@

import pytest

from pgcommitfest.commitfest.models import Committer, Patch, PatchOnCommitFest, Topic
from pgcommitfest.commitfest.models import Committer, Patch, PatchOnCommitFest

pytestmark = pytest.mark.django_db


@pytest.fixture
def topic():
"""Create a test topic."""
return Topic.objects.create(topic="General")


@pytest.fixture
def patches_with_users(users, open_cf, topic):
def patches_with_users(users, open_cf):
"""Create patches with authors and reviewers in a commitfest."""
# Alice is an author on patch 1
patch1 = Patch.objects.create(name="Test Patch 1", topic=topic)
patch1 = Patch.objects.create(name="Test Patch 1")
patch1.authors.add(users["alice"])
PatchOnCommitFest.objects.create(
patch=patch1, commitfest=open_cf, enterdate=datetime.now()
)

# Bob is a reviewer on patch 2
patch2 = Patch.objects.create(name="Test Patch 2", topic=topic)
patch2 = Patch.objects.create(name="Test Patch 2")
patch2.reviewers.add(users["bob"])
PatchOnCommitFest.objects.create(
patch=patch2, commitfest=open_cf, enterdate=datetime.now()
)

# Dave is a committer on patch 3
dave_committer = Committer.objects.create(user=users["dave"])
patch3 = Patch.objects.create(
name="Test Patch 3", topic=topic, committer=dave_committer
)
patch3 = Patch.objects.create(name="Test Patch 3", committer=dave_committer)
PatchOnCommitFest.objects.create(
patch=patch3, commitfest=open_cf, enterdate=datetime.now()
)
Expand Down
5 changes: 2 additions & 3 deletions pgcommitfest/commitfest/tests/test_refresh_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

from pgcommitfest.commitfest.ajax import refresh_single_thread
from pgcommitfest.commitfest.models import MailThread, Patch, Topic
from pgcommitfest.commitfest.models import MailThread, Patch

pytestmark = pytest.mark.django_db

Expand All @@ -14,7 +14,6 @@ def test_refresh_single_thread_updates_patch_lastmail():
old_date = datetime(2024, 1, 1)
new_date = datetime(2024, 6, 15)

topic = Topic.objects.create(topic="Test")
thread = MailThread.objects.create(
messageid="[email protected]",
subject="Test",
Expand All @@ -25,7 +24,7 @@ def test_refresh_single_thread_updates_patch_lastmail():
latestsubject="Test",
latestmsgid="[email protected]",
)
p = Patch.objects.create(name="Test Patch", topic=topic, lastmail=old_date)
p = Patch.objects.create(name="Test Patch", lastmail=old_date)
p.mailthread_set.add(thread)

api_response = [
Expand Down
14 changes: 6 additions & 8 deletions pgcommitfest/commitfest/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def patchlist(request, cf, personalized=False):
)
THEN 'Patches that are ready for your review'
ELSE 'Blocked on others'
END AS topic,
END AS group_name,
cf.id AS cf_id,
cf.name AS cf_name,
cf.status AS cf_status,
Expand All @@ -465,7 +465,7 @@ def patchlist(request, cf, personalized=False):
joins_str = "INNER JOIN commitfest_commitfest cf ON poc.commitfest_id=cf.id"
groupby_str = "cf.id,"
else:
columns_str = "t.topic as topic,"
columns_str = ""
joins_str = ""
groupby_str = ""

Expand Down Expand Up @@ -507,7 +507,7 @@ def patchlist(request, cf, personalized=False):
orderby_str = "poc.commitfest_id DESC, lastmail"
else:
if personalized:
# First we sort by topic, to have the grouping work.
# First we sort by group_name, to have the grouping work.
# Then we show non-failing patches first, and the ones that are
# shortest failing we show first. We consider patches in a closed
# commitfest, as if they are failing since that commitfest was
Expand All @@ -516,7 +516,7 @@ def patchlist(request, cf, personalized=False):
# progress" commitfest before ones in the "Open" commitfest.
# And then to break ties, we put ones with the most recent email at
# the top.
orderby_str = """topic DESC,
orderby_str = """group_name DESC,
COALESCE(
branch.failing_since,
CASE WHEN cf.status = %(cf_closed_status)s
Expand All @@ -526,7 +526,7 @@ def patchlist(request, cf, personalized=False):
lastmail DESC"""
whereparams["cf_closed_status"] = CommitFest.STATUS_CLOSED
else:
orderby_str = "topic, created"
orderby_str = "created"
sortkey = 0

if not has_filter and sortkey == 0 and request.GET:
Expand Down Expand Up @@ -584,13 +584,12 @@ def patchlist(request, cf, personalized=False):
)
FROM commitfest_patch p
INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id=p.id
INNER JOIN commitfest_topic t ON t.id=p.topic_id
{joins_str}
LEFT JOIN auth_user committer ON committer.id=p.committer_id
LEFT JOIN commitfest_targetversion v ON p.targetversion_id=v.id
LEFT JOIN commitfest_cfbotbranch branch ON branch.patch_id=p.id
WHERE {where_str}
GROUP BY p.id, poc.id, {groupby_str} committer.id, t.id, v.version, branch.patch_id
GROUP BY p.id, poc.id, {groupby_str} committer.id, v.version, branch.patch_id
ORDER BY is_open DESC, {orderby_str}""",
params,
)
Expand Down Expand Up @@ -648,7 +647,6 @@ def commitfest(request, cfid):
"all_tags": {t.id: t for t in Tag.objects.all()},
"has_filter": patch_list.has_filter,
"title": f"{cf.title} ({cf.periodstring})",
"grouping": patch_list.sortkey == 0,
"sortkey": patch_list.sortkey,
"openpatchids": [p["id"] for p in patch_list.patches if p["is_open"]],
"header_activity": "Activity log",
Expand Down
Loading