Skip to content

Commit c8a372c

Browse files
committed
Update the global search page with all the fancy filters
The global search page did not allow filtering by any of the things that other pages allowed. This adds that filtering and sorting, as well as a bunch of additional columns. It doesn't show CI status and stats for now.
1 parent 3681c99 commit c8a372c

File tree

2 files changed

+235
-21
lines changed

2 files changed

+235
-21
lines changed

pgcommitfest/commitfest/templates/patchsearch.html

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,125 @@
22
{%load commitfest %}
33
{%block contents%}
44

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

15+
<!-- Filter form (same as other pages) -->
16+
<div id="collapseFilters" class="collapse show">
17+
<form id="filterform" method="GET" action="/search/" style="margin-bottom: 0px">
18+
<input type="hidden" name="searchterm" value="{{searchterm}}">
19+
<table class="table" style="margin-bottom: 0px">
20+
<thead>
21+
<tr>
22+
{%for f in form%}
23+
{%if f.name == 'text'%}
24+
{# Skip the text field as we use searchterm instead #}
25+
{%elif not f.is_hidden%}
26+
<td>{{f.label}}</td>
27+
{%endif%}
28+
{%endfor%}
29+
<td></td>
30+
</tr>
31+
</thead>
32+
<tbody>
33+
<tr>
34+
{%for f in form%}
35+
{%if f.name == 'text'%}
36+
{# Skip the text field as we use searchterm instead #}
37+
{%elif f.is_hidden%}
38+
{{f}}
39+
{%else%}
40+
<td>
41+
{%if not f.name in form.selectize_fields%}{{f|field_class:"form-control"}}{%else%}{{f}}{%endif%}
42+
</td>
43+
{%endif%}
44+
{%endfor%}
45+
<td>
46+
<input type="submit" class="btn btn-secondary" value="Filter">
47+
<a class="btn btn-secondary" href="/search/?searchterm={{searchterm}}">Clear</a>
48+
</td>
49+
</tr>
50+
</tbody>
51+
</table>
52+
</form>
53+
</div>
54+
55+
{% if patches %}
56+
<p class="text-muted">Found {{patches|length}} patch{% if patches|length != 1 %}es{% endif %} matching "{{searchterm}}"</p>
57+
{% endif %}
58+
1259
<table class="table table-striped table-bordered table-hover">
1360
<thead>
1461
<tr>
15-
<th>Patch</th>
62+
<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>
63+
<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>
64+
<th>
65+
<a href="#" style="color:#333333;" onclick="return sortpatches(8);">CF</a>
66+
<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>
67+
{%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%}
68+
</th>
1669
<th>Status</th>
70+
<th>Tags</th>
71+
<th>Ver</th>
1772
<th>Author</th>
73+
<th>Reviewers</th>
74+
<th>Committer</th>
75+
<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>
76+
<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>
1877
</tr>
1978
</thead>
2079
<tbody>
2180
{%for p in patches %}
22-
{%with p.patchoncommitfest_set.all as cfs %}
23-
<tr>
24-
<td>{%with cfs|first as firstcf%}<a href="/{{firstcf.commitfest_id}}/{{p.id}}/">{{p}}</a>{%endwith%}</td>
25-
<td>{%for c in cfs %}
26-
<div style="margin-bottom: 3px;">{{c.commitfest}}: <span class="badge bg-secondary">{{c.statusstring}}</span></div>
27-
{%endfor%}</td>
28-
<td>{{p.authors_string|default:''}}</td>
29-
</tr>
30-
{%endwith%}
81+
<tr>
82+
<td><a href="/patch/{{p.id}}/">{{p.name}}</a></td>
83+
<td>{{p.id}}</td>
84+
<td>
85+
{%with p.patchoncommitfest_set.all|first as poc%}
86+
{%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%}
87+
{%endwith%}
88+
</td>
89+
<td>
90+
{%with p.patchoncommitfest_set.all|first as poc%}
91+
{%if poc%}<span class="badge bg-{{poc.status|patchstatuslabel}}">{{poc.status|patchstatusstring}}</span>{%endif%}
92+
{%endwith%}
93+
</td>
94+
<td style="width: min-content;">
95+
{%for tag in p.tags.all%}
96+
<a href="?searchterm={{searchterm}}&tag={{tag.id}}">
97+
<span class="badge" style="background-color: {{tag.color}};" title="{{tag.description}}">{{tag.name}}</span>
98+
</a>
99+
{%endfor%}
100+
</td>
101+
<td>{%if p.targetversion%}<span class="badge bg-secondary">{{p.targetversion}}</span>{%endif%}</td>
102+
<td>
103+
{% for author in p.authors.all %}
104+
{{author.get_full_name|default:author.username}}{% if not forloop.last %}, {% endif %}
105+
{% endfor %}
106+
</td>
107+
<td>
108+
{% for reviewer in p.reviewers.all %}
109+
{{reviewer.get_full_name|default:reviewer.username}}{% if not forloop.last %}, {% endif %}
110+
{% endfor %}
111+
</td>
112+
<td>{% if p.committer %}{{p.committer.fullname}}{% endif %}</td>
113+
<td>{{p.patchoncommitfest_set.count}}</td>
114+
<td style="white-space: nowrap;" title="{{p.modified}}">
115+
{%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%}
116+
</td>
117+
</tr>
31118
{%endfor%}
32119
</tbody>
33120
</table>
34121

35122
{%endblock%}
123+
124+
{%block morescript%}
125+
{%include "selectize_js.html" %}
126+
{%endblock%}

pgcommitfest/commitfest/views.py

Lines changed: 130 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from django.contrib.auth.decorators import login_required
44
from django.contrib.auth.models import User
55
from django.db import connection, transaction
6-
from django.db.models import Q
6+
from django.db.models import Count, Q
77
from django.http import (
88
Http404,
99
HttpResponse,
@@ -700,25 +700,148 @@ def global_search(request):
700700
patches = patches_by_messageid(cleaned_id)
701701

702702
if not patches:
703-
patches = (
704-
Patch.objects.select_related()
705-
.filter(name__icontains=searchterm)
706-
.order_by(
707-
"created",
703+
patches_query = (
704+
Patch.objects.select_related("targetversion", "committer")
705+
.prefetch_related(
706+
"authors",
707+
"reviewers",
708+
"tags",
709+
"patchoncommitfest_set__commitfest",
710+
"mailthread_set",
708711
)
709-
.all()
712+
.select_related("cfbot_branch")
713+
.filter(name__icontains=searchterm)
710714
)
711715

716+
# Apply filters using the same logic as patchlist
717+
if request.GET.get("status", "-1") != "-1":
718+
try:
719+
status = int(request.GET["status"])
720+
patches_query = patches_query.filter(
721+
patchoncommitfest__status=status
722+
).distinct()
723+
except ValueError:
724+
pass
725+
726+
if request.GET.get("targetversion", "-1") != "-1":
727+
if request.GET["targetversion"] == "-2":
728+
patches_query = patches_query.filter(targetversion_id__isnull=True)
729+
else:
730+
try:
731+
ver_id = int(request.GET["targetversion"])
732+
patches_query = patches_query.filter(targetversion_id=ver_id)
733+
except ValueError:
734+
pass
735+
736+
if request.GET.getlist("tag"):
737+
try:
738+
tag_ids = [int(t) for t in request.GET.getlist("tag")]
739+
for tag_id in tag_ids:
740+
patches_query = patches_query.filter(tags__id=tag_id)
741+
patches_query = patches_query.distinct()
742+
except ValueError:
743+
pass
744+
745+
# Apply author filter
746+
if request.GET.get("author", "-1") != "-1":
747+
if request.GET["author"] == "-2":
748+
patches_query = patches_query.filter(authors__isnull=True)
749+
elif request.GET["author"] == "-3":
750+
# Filter for current user's patches
751+
if not request.user.is_authenticated:
752+
return HttpResponseRedirect(
753+
f"{settings.LOGIN_URL}?next={request.path}?searchterm={searchterm}"
754+
)
755+
patches_query = patches_query.filter(authors=request.user)
756+
else:
757+
try:
758+
author_id = int(request.GET["author"])
759+
patches_query = patches_query.filter(authors__id=author_id)
760+
except ValueError:
761+
pass
762+
763+
# Apply reviewer filter
764+
if request.GET.get("reviewer", "-1") != "-1":
765+
if request.GET["reviewer"] == "-2":
766+
patches_query = patches_query.filter(reviewers__isnull=True)
767+
elif request.GET["reviewer"] == "-3":
768+
# Filter for current user's reviews
769+
if not request.user.is_authenticated:
770+
return HttpResponseRedirect(
771+
f"{settings.LOGIN_URL}?next={request.path}?searchterm={searchterm}"
772+
)
773+
patches_query = patches_query.filter(reviewers=request.user)
774+
else:
775+
try:
776+
reviewer_id = int(request.GET["reviewer"])
777+
patches_query = patches_query.filter(reviewers__id=reviewer_id)
778+
except ValueError:
779+
pass
780+
781+
# Ensure distinct results after filtering on many-to-many relationships
782+
patches_query = patches_query.distinct()
783+
784+
# Apply sorting based on sortkey parameter (adapted for Django ORM)
785+
sortkey = request.GET.get("sortkey", "1")
786+
if sortkey == "2": # Latest mail
787+
patches_query = patches_query.order_by("-modified") # Use modified as proxy
788+
elif sortkey == "-2":
789+
patches_query = patches_query.order_by("modified")
790+
elif sortkey == "3": # Num cfs
791+
patches_query = patches_query.annotate(
792+
num_cfs=Count("patchoncommitfest")
793+
).order_by("-num_cfs")
794+
elif sortkey == "-3":
795+
patches_query = patches_query.annotate(
796+
num_cfs=Count("patchoncommitfest")
797+
).order_by("num_cfs")
798+
elif sortkey == "4": # ID
799+
patches_query = patches_query.order_by("id")
800+
elif sortkey == "-4":
801+
patches_query = patches_query.order_by("-id")
802+
elif sortkey == "5": # Patch name
803+
patches_query = patches_query.order_by("name")
804+
elif sortkey == "-5":
805+
patches_query = patches_query.order_by("-name")
806+
elif sortkey == "8": # CF
807+
patches_query = patches_query.order_by("patchoncommitfest__commitfest__id")
808+
elif sortkey == "-8":
809+
patches_query = patches_query.order_by("-patchoncommitfest__commitfest__id")
810+
else: # Default: Created (sortkey 1)
811+
patches_query = patches_query.order_by("created")
812+
813+
patches = patches_query.all()
814+
712815
if len(patches) == 1:
713816
patch = patches[0]
714817
return HttpResponseRedirect(f"/patch/{patch.id}/")
715818

819+
# Use the existing filter form
820+
form = CommitFestFilterForm(request.GET)
821+
822+
# Get user profile for timestamp preferences
823+
userprofile = None
824+
if request.user.is_authenticated:
825+
try:
826+
from pgcommitfest.userprofile.models import UserProfile
827+
828+
userprofile = UserProfile.objects.get(user=request.user)
829+
except UserProfile.DoesNotExist:
830+
pass
831+
716832
return render(
717833
request,
718834
"patchsearch.html",
719835
{
720836
"patches": patches,
721837
"title": "Patch search results",
838+
"searchterm": searchterm,
839+
"form": form,
840+
"cf": None, # No specific commitfest context
841+
"sortkey": int(request.GET.get("sortkey") or "1"),
842+
"tags_data": get_tags_data(),
843+
"all_tags": {t.id: t for t in Tag.objects.all()},
844+
"userprofile": userprofile,
722845
},
723846
)
724847

0 commit comments

Comments
 (0)