Skip to content
Draft
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
6 changes: 3 additions & 3 deletions pgcommitfest/commitfest/templates/patch.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@
{%endif%}
{%if cfbot_branch %}
<button class="btn btn-secondary" title="This adds the following to your clipboard (needs to be run in an existing git repo):
git remote add commitfest https://github.com/postgresql-cfbot/postgresql.git
git fetch commitfest cf/{{patch.id}}
git checkout commitfest/cf/{{patch.id}}" onclick="addGitCheckoutToClipboard({{patch.id}})">Copy git checkout commands</button>
git remote add commitfest https://github.com/postgresql-cfbot/postgresql.git
git fetch commitfest cf/{{patch.id}}
git checkout commitfest/cf/{{patch.id}}" onclick="addGitCheckoutToClipboard({{patch.id}})">Copy git checkout commands</button>
{%endif%}
</a>
</td>
Expand Down
113 changes: 99 additions & 14 deletions pgcommitfest/commitfest/templates/patchsearch.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,119 @@
{%load commitfest %}
{%block contents%}

<form method="GET" action="/search/" class="d-flex gap-2" style="margin-bottom: 10px">
<div class="form-group">
<input type="text" class="form-control" id="searchterm" name="searchterm" placeholder="Global search">
<!-- Search term input -->
<form method="GET" action="/search/" class="d-flex gap-2 mb-3">
<div class="form-group flex-grow-1">
<input type="text" class="form-control" id="searchterm" name="searchterm" value="{{searchterm}}" placeholder="Email Message-ID or keywords" required>
</div>
<button type="submit" class="btn btn-secondary">Search</button>
<button type="submit" class="btn btn-secondary">
<i class="bi bi-search"></i> Search
</button>
</form>

<!-- Filter form (same as other pages) -->
<div id="collapseFilters" class="collapse show">
<form id="filterform" method="GET" action="/search/" style="margin-bottom: 0px">
<input type="hidden" name="searchterm" value="{{searchterm}}">
<table class="table" style="margin-bottom: 0px">
<thead>
<tr>
{%for f in form%}
{%if not f.is_hidden%}
<td>{{f.label}}</td>
{%else%}
<td></td>
{%endif%}
{%endfor%}
<td></td>
</tr>
</thead>
<tbody>
<tr>
{%for f in form%}
<td>
{%if not f.name in form.selectize_fields%}{{f|field_class:"form-control"}}{%else%}{{f}}{%endif%}
</td>
{%endfor%}
<td>
<input type="submit" class="btn btn-secondary" value="Filter">
<a class="btn btn-secondary" href="/search/?searchterm={{searchterm}}">Clear</a>
</td>
</tr>
</tbody>
</table>
</form>
</div>

{% if patches %}
<p class="text-muted">Found {{patches|length}} patch{% if patches|length != 1 %}es{% endif %} matching "{{searchterm}}"</p>
{% endif %}

<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Patch</th>
<th><a href="#" style="color:#333333;" onclick="return sortpatches(5);">Patch</a>{%if sortkey == 5%}<div style="float:right;"><i class="bi bi-sort-alpha-down"></i></div>{%elif sortkey == -5%}<div style="float:right;"><i class="bi bi-sort-alpha-up"></i></div>{%endif%}</th>
<th><a href="#" style="color:#333333;" onclick="return sortpatches(4);">ID</a>{%if sortkey == 4%}<div style="float:right;"><i class="bi bi-sort-numeric-down"></i></div>{%elif sortkey == -4%}<div style="float:right;"><i class="bi bi-sort-numeric-up"></i></div>{%endif%}</th>
<th>
<a href="#" style="color:#333333;" onclick="return sortpatches(8);">CF</a>
<i class="bi bi-question-circle text-muted" style="font-size: 0.8em; margin-left: 3px;" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" title="Color coding:<br>Green = In Progress<br>Blue = Open<br>Red = Closed"></i>
{%if sortkey == 8%}<div style="float:right;"><i class="bi bi-sort-numeric-down"></i></div>{%elif sortkey == -8%}<div style="float:right;"><i class="bi bi-sort-numeric-up"></i></div>{%endif%}
</th>
<th>Status</th>
<th>Tags</th>
<th>Ver</th>
<th>Author</th>
<th>Reviewers</th>
<th>Committer</th>
<th><a href="#" style="color:#333333;" onclick="return sortpatches(3);">Num cfs</a>{%if sortkey == 3%}<div style="float:right;"><i class="bi bi-sort-numeric-down-alt"></i></div>{%elif sortkey == -3%}<div style="float:right;"><i class="bi bi-sort-numeric-up-alt"></i></div>{%endif%}</th>
<th><a href="#" style="color:#333333;" onclick="return sortpatches(2);">Latest mail</a>{%if sortkey == 2%}<div style="float:right;"><i class="bi bi-sort-down"></i></div>{%elif sortkey == -2%}<div style="float:right;"><i class="bi bi-sort-up"></i></div>{%endif%}</th>
</tr>
</thead>
<tbody>
{%for p in patches %}
{%with p.patchoncommitfest_set.all as cfs %}
<tr>
<td>{%with cfs|first as firstcf%}<a href="/{{firstcf.commitfest_id}}/{{p.id}}/">{{p}}</a>{%endwith%}</td>
<td>{%for c in cfs %}
<div style="margin-bottom: 3px;">{{c.commitfest}}: <span class="badge bg-secondary">{{c.statusstring}}</span></div>
{%endfor%}</td>
<td>{{p.authors_string|default:''}}</td>
</tr>
{%endwith%}
<tr>
<td><a href="/patch/{{p.id}}/">{{p.name}}</a></td>
<td>{{p.id}}</td>
<td>
{%with p.patchoncommitfest_set.all|first as poc%}
{%if poc%}<a href="/{{poc.commitfest.id}}/"><span class="badge bg-{{poc.commitfest.status|commitfeststatuslabel}}" title="{{poc.commitfest.status|commitfeststatusstring}}">{{poc.commitfest.name}}</span></a>{%endif%}
{%endwith%}
</td>
<td>
{%with p.patchoncommitfest_set.all|first as poc%}
{%if poc%}<span class="badge bg-{{poc.status|patchstatuslabel}}">{{poc.status|patchstatusstring}}</span>{%endif%}
{%endwith%}
</td>
<td style="width: min-content;">
{%for tag in p.tags.all%}
<a href="?searchterm={{searchterm}}&tag={{tag.id}}">
<span class="badge" style="background-color: {{tag.color}};" title="{{tag.description}}">{{tag.name}}</span>
</a>
{%endfor%}
</td>
<td>{%if p.targetversion%}<span class="badge bg-secondary">{{p.targetversion}}</span>{%endif%}</td>
<td>
{% for author in p.authors.all %}
{{author.get_full_name|default:author.username}}{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td>
{% for reviewer in p.reviewers.all %}
{{reviewer.get_full_name|default:reviewer.username}}{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td>{% if p.committer %}{{p.committer.fullname}}{% endif %}</td>
<td>{{p.patchoncommitfest_set.count}}</td>
<td style="white-space: nowrap;" title="{{p.modified}}">
{%if p.modified and userprofile.show_relative_timestamps %}{% cfwhen p.modified %}{%elif p.modified %}{{p.modified|date:"Y-m-d"}}<br/>{{p.modified|date:"H:i"}}{%endif%}
</td>
</tr>
{%endfor%}
</tbody>
</table>

{%endblock%}

{%block morescript%}
{%include "selectize_js.html" %}
{%endblock%}
98 changes: 91 additions & 7 deletions pgcommitfest/commitfest/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.db import connection, transaction
from django.db.models import Q
from django.db.models import Count, Q
from django.http import (
Http404,
HttpResponse,
Expand Down Expand Up @@ -692,25 +692,109 @@ def global_search(request):
patches = patches_by_messageid(cleaned_id)

if not patches:
patches = (
Patch.objects.select_related()
.filter(name__icontains=searchterm)
.order_by(
"created",
patches_query = (
Patch.objects.select_related("targetversion", "committer")
.prefetch_related(
"authors",
"reviewers",
"tags",
"patchoncommitfest_set__commitfest",
"mailthread_set",
)
.all()
.select_related("cfbot_branch")
.filter(name__icontains=searchterm)
)

# Apply filters using the same logic as patchlist
if request.GET.get("status", "-1") != "-1":
try:
status = int(request.GET["status"])
patches_query = patches_query.filter(
patchoncommitfest__status=status
).distinct()
except ValueError:
pass

if request.GET.get("targetversion", "-1") != "-1":
if request.GET["targetversion"] == "-2":
patches_query = patches_query.filter(targetversion_id__isnull=True)
else:
try:
ver_id = int(request.GET["targetversion"])
patches_query = patches_query.filter(targetversion_id=ver_id)
except ValueError:
pass

if request.GET.getlist("tag"):
try:
tag_ids = [int(t) for t in request.GET.getlist("tag")]
for tag_id in tag_ids:
patches_query = patches_query.filter(tags__id=tag_id)
patches_query = patches_query.distinct()
except ValueError:
pass

# Apply sorting based on sortkey parameter (adapted for Django ORM)
sortkey = request.GET.get("sortkey", "1")
if sortkey == "2": # Latest mail
patches_query = patches_query.order_by("-modified") # Use modified as proxy
elif sortkey == "-2":
patches_query = patches_query.order_by("modified")
elif sortkey == "3": # Num cfs
patches_query = patches_query.annotate(
num_cfs=Count("patchoncommitfest")
).order_by("-num_cfs")
elif sortkey == "-3":
patches_query = patches_query.annotate(
num_cfs=Count("patchoncommitfest")
).order_by("num_cfs")
elif sortkey == "4": # ID
patches_query = patches_query.order_by("id")
elif sortkey == "-4":
patches_query = patches_query.order_by("-id")
elif sortkey == "5": # Patch name
patches_query = patches_query.order_by("name")
elif sortkey == "-5":
patches_query = patches_query.order_by("-name")
elif sortkey == "8": # CF
patches_query = patches_query.order_by("patchoncommitfest__commitfest__id")
elif sortkey == "-8":
patches_query = patches_query.order_by("-patchoncommitfest__commitfest__id")
else: # Default: Created (sortkey 1)
patches_query = patches_query.order_by("created")

patches = patches_query.all()

if len(patches) == 1:
patch = patches[0]
return HttpResponseRedirect(f"/patch/{patch.id}/")

# Use the existing filter form
form = CommitFestFilterForm(request.GET)

# Get user profile for timestamp preferences
userprofile = None
if request.user.is_authenticated:
try:
from pgcommitfest.userprofile.models import UserProfile

userprofile = UserProfile.objects.get(user=request.user)
except UserProfile.DoesNotExist:
pass

return render(
request,
"patchsearch.html",
{
"patches": patches,
"title": "Patch search results",
"searchterm": searchterm,
"form": form,
"cf": None, # No specific commitfest context
"sortkey": int(request.GET.get("sortkey") or "1"),
"tags_data": get_tags_data(),
"all_tags": {t.id: t for t in Tag.objects.all()},
"userprofile": userprofile,
},
)

Expand Down