Skip to content

Commit 6a6d1c3

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 8657ebf commit 6a6d1c3

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,
@@ -699,25 +699,148 @@ def global_search(request):
699699
patches = patches_by_messageid(cleaned_id)
700700

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

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

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

0 commit comments

Comments
 (0)