Skip to content

Commit 6bbc41d

Browse files
andrepapotivictor-accarini
authored andcommitted
views: manage users in patch attention list
* List view: show number of users interested in a specific patch * Detail view: show a list of users who are interested in the patch * Allow users to add/remove interest in a patch * Allow managers to remove interested user from a patch Signed-off-by: Victor Accarini <[email protected]>
1 parent 3d96133 commit 6bbc41d

File tree

8 files changed

+155
-4
lines changed

8 files changed

+155
-4
lines changed

htdocs/css/style.css

+24
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,18 @@ table.patch-meta tr th, table.patch-meta tr td {
288288
text-decoration: underline;
289289
}
290290

291+
.patchinterest {
292+
display: inline-block;
293+
border-radius: 7px;
294+
min-width: 0.9em;
295+
padding: 0 2px;
296+
text-align: center;
297+
}
298+
299+
.patchinterest.exists {
300+
background-color: #82ca9d;
301+
}
302+
291303
.patchlistchecks {
292304
display: inline-block;
293305
border-radius: 7px;
@@ -344,6 +356,18 @@ table.patch-meta tr th, table.patch-meta tr td {
344356
font-family: "DejaVu Sans Mono", fixed;
345357
}
346358

359+
.submission-attention-set {
360+
display: flex;
361+
flex-wrap: wrap;
362+
align-items: center;
363+
gap: 8px;
364+
}
365+
366+
button[class*=interest-action] {
367+
padding: 0.2em 0.5em;
368+
border-radius: 4px;
369+
}
370+
347371
div[class^="comment-status-bar-"] {
348372
display: flex;
349373
flex-wrap: wrap;

patchwork/forms.py

+19
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,27 @@ def save(self, instance, commit=True):
252252
if field.is_no_change(data[f.name]):
253253
continue
254254

255+
if f.name == 'review_status':
256+
if data[f.name]:
257+
self.instance.planning_to_review.add(self.user)
258+
else:
259+
self.instance.planning_to_review.remove(self.user)
260+
continue
261+
255262
setattr(instance, f.name, data[f.name])
256263

257264
if commit:
258265
instance.save()
259266
return instance
267+
268+
def review_status_only(self):
269+
review_status_only = True
270+
field_names = set(self.fields.keys())
271+
field_names.discard({'review_status', 'action'})
272+
273+
for field_name in field_names:
274+
data = self.data.get(field_name, '*')
275+
if data != '*':
276+
review_status_only = False
277+
278+
return review_status_only

patchwork/models.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ def save(self, *args, **kwargs):
582582

583583
self.refresh_tag_counts()
584584

585-
def is_editable(self, user):
585+
def is_editable(self, user, declare_interest_only=False):
586586
if not user.is_authenticated:
587587
return False
588588

@@ -593,7 +593,8 @@ def is_editable(self, user):
593593
if self.project.is_editable(user):
594594
self._edited_by = user
595595
return True
596-
return False
596+
597+
return declare_interest_only
597598

598599
@staticmethod
599600
def filter_unique_checks(checks):

patchwork/templates/patchwork/partials/patch-list.html

+5
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@
8080
<span title="Success / Warning / Fail">S/W/F</span>
8181
</th>
8282

83+
<th>
84+
<span title="Declared review interest">Review Interest</span>
85+
</th>
86+
8387
<th>
8488
{% if not order.editable %}
8589
{% if order.name == "date" %}
@@ -182,6 +186,7 @@
182186
</td>
183187
<td id="patch-tags:{{patch.id}}" class="text-nowrap">{{ patch|patch_tags }}</td>
184188
<td id="patch-checks:{{patch.id}}" class="text-nowrap">{{ patch|patch_checks }}</td>
189+
<td id="patch-interest:{{patch.id}}" class="text-nowrap">{{ patch|patch_interest }}</td>
185190
<td id="patch-date:{{patch.id}}" class="text-nowrap">{{ patch.date|date:"Y-m-d" }}</td>
186191
<td id="patch-submitter:{{patch.id}}">{{ patch.submitter|personify:project }}</td>
187192
<td id="patch-delegate:{{patch.id}}">{{ patch.delegate.username }}</td>

patchwork/templates/patchwork/submission.html

+28
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,34 @@ <h2>Message</h2>
187187
</pre>
188188
</div>
189189

190+
<div class="submission-attention-set">
191+
<h2>Users pending actions</h2>
192+
{% if user.is_authenticated and user not in attention_set %}
193+
<form id="declare-review-form" method="post">
194+
{% csrf_token %}
195+
<input type="hidden" name="action" value="add-interest">
196+
<button type="submit" class="btn-default interest-action-add">Declare interest</button>
197+
</form>
198+
{% endif %}
199+
</div>
200+
{% if attention_set %}
201+
<ul>
202+
{% for set_user in attention_set %}
203+
<li>
204+
<form id="declare-review-form" method="post">
205+
{% csrf_token %}
206+
<input type="hidden" name="action" value="remove-interest">
207+
<input type="hidden" name="attention_set" value="{{set_user.id}}">
208+
{{ set_user.username }} ({{ set_user.email }})
209+
{% if set_user == user or is_maintainer %}
210+
<button type="submit" class="btn-link"><span class="glyphicon glyphicon-trash" ></span></button>
211+
{% endif %}
212+
</form>
213+
</li>
214+
{% endfor %}
215+
</ul>
216+
{% endif %}
217+
190218
{% for item in comments %}
191219
{% if forloop.first %}
192220
<h2>Comments</h2>

patchwork/templatetags/patch.py

+15
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,18 @@ def patch_commit_display(patch):
7575
return mark_safe(
7676
'<a href="%s">%s</a>' % (escape(fmt.format(commit)), escape(commit))
7777
)
78+
79+
80+
@register.filter(name='patch_interest')
81+
def patch_interest(patch):
82+
reviews = patch.attention_set.count()
83+
review_title = (
84+
f'has {reviews} interested reviewers'
85+
if reviews > 0
86+
else 'no interested reviewers'
87+
)
88+
review_class = 'exists' if reviews > 0 else ''
89+
return mark_safe(
90+
'<span class="patchinterest %s" title="%s">%s</span>'
91+
% (review_class, review_title, reviews if reviews > 0 else '-')
92+
)

patchwork/views/__init__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,9 @@ def generic_list(
242242
if data and data.get('form', '') == 'patch-list-form':
243243
data_tmp = data
244244

245-
properties_form = MultiplePatchForm(project, data=data_tmp)
245+
properties_form = MultiplePatchForm(
246+
project, data=data_tmp, user=request.user
247+
)
246248
create_bundle_form = CreateBundleForm()
247249

248250
if request.method == 'POST' and data.get('form') == 'patch-list-form':
@@ -344,7 +346,7 @@ def process_multiplepatch_form(request, form, action, patches, context):
344346

345347
changed_patches = 0
346348
for patch in patches:
347-
if not patch.is_editable(request.user):
349+
if not patch.is_editable(request.user, form.review_status_only()):
348350
errors.append(
349351
"You don't have permissions to edit patch '%s'" % patch.name
350352
)

patchwork/views/patch.py

+57
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# SPDX-License-Identifier: GPL-2.0-or-later
55

66
from django.contrib import messages
7+
from django.contrib.auth.models import User
78
from django.http import Http404
89
from django.http import HttpResponse
910
from django.http import HttpResponseForbidden
@@ -16,6 +17,7 @@
1617
from patchwork.forms import PatchForm
1718
from patchwork.models import Cover
1819
from patchwork.models import Patch
20+
from patchwork.models import PatchAttentionSet
1921
from patchwork.models import Project
2022
from patchwork.views import generic_list
2123
from patchwork.views import set_bundle
@@ -61,6 +63,10 @@ def patch_detail(request, project_id, msgid):
6163

6264
editable = patch.is_editable(request.user)
6365
context = {'project': patch.project}
66+
is_maintainer = (
67+
request.user.is_authenticated
68+
and project in request.user.profile.maintainer_projects.all()
69+
)
6470

6571
form = None
6672
create_bundle_form = None
@@ -80,6 +86,50 @@ def patch_detail(request, project_id, msgid):
8086
errors = set_bundle(
8187
request, project, action, request.POST, [patch]
8288
)
89+
elif action in ['add-interest', 'remove-interest']:
90+
if request.user.is_authenticated:
91+
if action == 'add-interest':
92+
PatchAttentionSet.objects.get_or_create(
93+
patch=patch, user=request.user
94+
)
95+
message = (
96+
'You have declared interest in reviewing this patch'
97+
)
98+
else:
99+
user_id = request.POST.get('attention_set')
100+
101+
if is_maintainer or user_id == str(request.user.id):
102+
rm_user = User.objects.get(pk=user_id)
103+
PatchAttentionSet.objects.filter(
104+
patch=patch, user=rm_user
105+
).delete()
106+
107+
rm_user_name = (
108+
f"'{rm_user.username}'"
109+
if rm_user != request.user
110+
else 'yourself'
111+
)
112+
message = (
113+
f"You removed {rm_user_name} from patch's "
114+
'attention list'
115+
)
116+
117+
patch.save()
118+
messages.success(
119+
request,
120+
message,
121+
)
122+
else:
123+
messages.error(
124+
request,
125+
"You can't remove another user interest in this "
126+
'patch',
127+
)
128+
else:
129+
messages.error(
130+
request,
131+
'You must be logged in to change the user attention list.',
132+
)
83133

84134
elif not editable:
85135
return HttpResponseForbidden()
@@ -93,6 +143,13 @@ def patch_detail(request, project_id, msgid):
93143
if request.user.is_authenticated:
94144
context['bundles'] = request.user.bundles.all()
95145

146+
attention_set = [
147+
data.user for data in PatchAttentionSet.objects.filter(patch=patch)
148+
]
149+
150+
context['attention_set'] = attention_set
151+
context['is_maintainer'] = is_maintainer
152+
96153
comments = patch.comments.all()
97154
comments = comments.select_related('submitter')
98155
comments = comments.only(

0 commit comments

Comments
 (0)