Skip to content

Commit 58dd25f

Browse files
committed
Split up open review requests on the review team page in assigned and
unassigned requests to make it easier to just work with the unassigned ones. Use same split on the manage reviews page which is now two pages. - Legacy-Id: 12266
1 parent 19a3f10 commit 58dd25f

7 files changed

Lines changed: 136 additions & 85 deletions

File tree

ietf/group/tests_review.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,12 @@ def test_manage_review_requests(self):
148148

149149
group = review_req1.team
150150

151-
url = urlreverse(ietf.group.views_review.manage_review_requests, kwargs={ 'acronym': group.acronym })
151+
url = urlreverse(ietf.group.views_review.manage_review_requests, kwargs={ 'acronym': group.acronym, "assignment_status": "assigned" })
152152

153153
login_testing_unauthorized(self, "secretary", url)
154154

155-
url = urlreverse(ietf.group.views_review.manage_review_requests, kwargs={ 'acronym': group.acronym, 'group_type': group.type_id })
155+
assigned_url = urlreverse(ietf.group.views_review.manage_review_requests, kwargs={ 'acronym': group.acronym, 'group_type': group.type_id, "assignment_status": "assigned" })
156+
unassigned_url = urlreverse(ietf.group.views_review.manage_review_requests, kwargs={ 'acronym': group.acronym, 'group_type': group.type_id, "assignment_status": "unassigned" })
156157

157158
review_req2 = ReviewRequest.objects.create(
158159
doc=review_req1.doc,
@@ -201,14 +202,14 @@ def test_manage_review_requests(self):
201202
)
202203

203204
# get
204-
r = self.client.get(url)
205+
r = self.client.get(assigned_url)
205206
self.assertEqual(r.status_code, 200)
206207
self.assertTrue(review_req1.doc.name in unicontent(r))
207208

208-
# can't save: conflict
209+
# can't save assigned: conflict
209210
new_reviewer = Email.objects.get(role__name="reviewer", role__group=group, person__user__username="marschairman")
210211
# provoke conflict by posting bogus data
211-
r = self.client.post(url, {
212+
r = self.client.post(assigned_url, {
212213
"reviewrequest": [str(review_req1.pk), str(review_req2.pk), str(123456)],
213214

214215
# close
@@ -226,13 +227,21 @@ def test_manage_review_requests(self):
226227
self.assertEqual(r.status_code, 200)
227228
content = unicontent(r).lower()
228229
self.assertTrue("1 request closed" in content)
229-
self.assertTrue("1 request opened" in content)
230230
self.assertTrue("2 requests changed assignment" in content)
231231

232-
# close and assign
232+
# can't save unassigned: conflict
233+
r = self.client.post(unassigned_url, {
234+
"reviewrequest": [str(123456)],
235+
"action": "save-continue",
236+
})
237+
self.assertEqual(r.status_code, 200)
238+
content = unicontent(r).lower()
239+
self.assertTrue("1 request opened" in content)
240+
241+
# close and reassign assigned
233242
new_reviewer = Email.objects.get(role__name="reviewer", role__group=group, person__user__username="marschairman")
234-
r = self.client.post(url, {
235-
"reviewrequest": [str(review_req1.pk), str(review_req2.pk), str(review_req3.pk)],
243+
r = self.client.post(assigned_url, {
244+
"reviewrequest": [str(review_req1.pk), str(review_req2.pk)],
236245

237246
# close
238247
"r{}-existing_reviewer".format(review_req1.pk): review_req1.reviewer_id or "",
@@ -244,12 +253,20 @@ def test_manage_review_requests(self):
244253
"r{}-action".format(review_req2.pk): "assign",
245254
"r{}-reviewer".format(review_req2.pk): new_reviewer.pk,
246255

256+
"action": "save",
257+
})
258+
self.assertEqual(r.status_code, 302)
259+
260+
# no change on unassigned
261+
r = self.client.post(unassigned_url, {
262+
"reviewrequest": [str(review_req3.pk)],
263+
247264
# no change
248265
"r{}-existing_reviewer".format(review_req3.pk): review_req3.reviewer_id or "",
249266
"r{}-action".format(review_req3.pk): "",
250267
"r{}-close".format(review_req3.pk): "no-response",
251268
"r{}-reviewer".format(review_req3.pk): "",
252-
269+
253270
"action": "save",
254271
})
255272
self.assertEqual(r.status_code, 302)

ietf/group/urls_info_details.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
(r'^archives/$', 'ietf.group.views.derived_archives'),
3232
(r'^photos/$', views.group_photos),
3333
(r'^reviews/$', views_review.review_requests),
34-
(r'^reviews/manage/$', views_review.manage_review_requests),
34+
(r'^reviews/manage/(?P<assignment_status>assigned|unassigned)/$', views_review.manage_review_requests),
3535
(r'^reviews/email-assignments/$', views_review.email_open_review_assignments),
3636
(r'^reviewers/$', views_review.reviewer_overview),
3737
(r'^reviewers/(?P<reviewer_email>[\w%+-.@]+)/settings/$', views_review.change_reviewer_settings),

ietf/group/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ def construct_group_menu_context(request, group, selected, group_type, others):
213213

214214
if group.features.has_reviews and can_manage_review_requests_for_team(request.user, group):
215215
import ietf.group.views_review
216-
actions.append((u"Manage review requests", urlreverse(ietf.group.views_review.manage_review_requests, kwargs=kwargs)))
216+
actions.append((u"Manage unassigned reviews", urlreverse(ietf.group.views_review.manage_review_requests, kwargs=dict(assignment_status="unassigned", **kwargs))))
217+
actions.append((u"Manage assigned reviews", urlreverse(ietf.group.views_review.manage_review_requests, kwargs=dict(assignment_status="assigned", **kwargs))))
217218

218219
if group.state_id != "conclude" and (is_admin or can_manage):
219220
actions.append((u"Edit group", urlreverse("group_edit", kwargs=kwargs)))

ietf/group/views_review.py

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,33 +30,55 @@
3030
from ietf.utils.fields import DatepickerDateField
3131
from ietf.ietfauth.utils import user_is_person
3232

33+
def get_open_review_requests_for_team(team, assignment_status=None):
34+
open_review_requests = ReviewRequest.objects.filter(
35+
team=team,
36+
state__in=("requested", "accepted")
37+
).prefetch_related(
38+
"reviewer__person", "type", "state"
39+
).order_by("-time", "-id")
40+
41+
if assignment_status == "unassigned":
42+
open_review_requests = suggested_review_requests_for_team(team) + list(open_review_requests.filter(reviewer=None))
43+
elif assignment_status == "assigned":
44+
open_review_requests = list(open_review_requests.exclude(reviewer=None))
45+
else:
46+
open_review_requests = suggested_review_requests_for_team(team) + list(open_review_requests)
47+
48+
today = datetime.date.today()
49+
unavailable_periods = current_unavailable_periods_for_reviewers(team)
50+
for r in open_review_requests:
51+
if r.reviewer:
52+
r.reviewer_unavailable = any(p.availability == "unavailable"
53+
for p in unavailable_periods.get(r.reviewer.person_id, []))
54+
r.due = max(0, (today - r.deadline).days)
55+
56+
return open_review_requests
3357

3458
def review_requests(request, acronym, group_type=None):
3559
group = get_group_or_404(acronym, group_type)
3660
if not group.features.has_reviews:
3761
raise Http404
3862

39-
open_review_requests = list(ReviewRequest.objects.filter(
40-
team=group, state__in=("requested", "accepted")
41-
).prefetch_related("reviewer", "type", "state").order_by("-time", "-id"))
42-
43-
unavailable_periods = current_unavailable_periods_for_reviewers(group)
44-
for review_req in open_review_requests:
45-
if review_req.reviewer:
46-
review_req.reviewer_unavailable = any(p.availability == "unavailable"
47-
for p in unavailable_periods.get(review_req.reviewer.person_id, []))
63+
assigned_review_requests = []
64+
unassigned_review_requests = []
4865

49-
open_review_requests = suggested_review_requests_for_team(group) + open_review_requests
66+
for r in get_open_review_requests_for_team(group):
67+
if r.reviewer:
68+
assigned_review_requests.append(r)
69+
else:
70+
unassigned_review_requests.append(r)
5071

51-
today = datetime.date.today()
52-
for r in open_review_requests:
53-
r.due = max(0, (today - r.deadline).days)
72+
open_review_requests = [
73+
("Unassigned", unassigned_review_requests),
74+
("Assigned", assigned_review_requests),
75+
]
5476

5577
closed_review_requests = ReviewRequest.objects.filter(
5678
team=group,
5779
).exclude(
5880
state__in=("requested", "accepted")
59-
).prefetch_related("reviewer", "type", "state", "doc").order_by("-time", "-id")
81+
).prefetch_related("reviewer__person", "type", "state", "doc", "result").order_by("-time", "-id")
6082

6183
since_choices = [
6284
(None, "1 month"),
@@ -192,26 +214,15 @@ def __init__(self, review_req, *args, **kwargs):
192214

193215

194216
@login_required
195-
def manage_review_requests(request, acronym, group_type=None):
217+
def manage_review_requests(request, acronym, group_type=None, assignment_status=None):
196218
group = get_group_or_404(acronym, group_type)
197219
if not group.features.has_reviews:
198220
raise Http404
199221

200222
if not can_manage_review_requests_for_team(request.user, group):
201223
return HttpResponseForbidden("You do not have permission to perform this action")
202224

203-
unavailable_periods = current_unavailable_periods_for_reviewers(group)
204-
205-
open_review_requests = list(ReviewRequest.objects.filter(
206-
team=group, state__in=("requested", "accepted")
207-
).prefetch_related("reviewer", "type", "state").order_by("-time", "-id"))
208-
209-
for review_req in open_review_requests:
210-
if review_req.reviewer:
211-
review_req.reviewer_unavailable = any(p.availability == "unavailable"
212-
for p in unavailable_periods.get(review_req.reviewer.person_id, []))
213-
214-
review_requests = suggested_review_requests_for_team(group) + open_review_requests
225+
review_requests = get_open_review_requests_for_team(group, assignment_status=assignment_status)
215226

216227
document_requests = extract_revision_ordered_review_requests_for_documents_and_replaced(
217228
ReviewRequest.objects.filter(state__in=("part-completed", "completed"), team=group).prefetch_related("result"),
@@ -221,10 +232,12 @@ def manage_review_requests(request, acronym, group_type=None):
221232
# we need a mutable query dict for resetting upon saving with
222233
# conflicts
223234
query_dict = request.POST.copy() if request.method == "POST" else None
235+
224236
for req in review_requests:
237+
# add previous requests
225238
l = []
226-
# take all on the latest reviewed rev
227239
for r in document_requests.get(req.doc_id, []):
240+
# take all on the latest reviewed rev
228241
if l and l[0].reviewed_rev:
229242
if r.doc_id == l[0].doc_id and r.reviewed_rev:
230243
if int(r.reviewed_rev) > int(l[0].reviewed_rev):
@@ -292,18 +305,28 @@ def manage_review_requests(request, acronym, group_type=None):
292305
kwargs["group_type"] = group_type
293306

294307
if form_action == "save-continue":
308+
if assignment_status:
309+
kwargs["assignment_status"] = assignment_status
310+
295311
return redirect(manage_review_requests, **kwargs)
296312
else:
297313
import ietf.group.views_review
298314
return redirect(ietf.group.views_review.review_requests, **kwargs)
299315

316+
other_assignment_status = {
317+
"unassigned": "assigned",
318+
"assigned": "unassigned",
319+
}.get(assignment_status)
320+
300321
return render(request, 'group/manage_review_requests.html', {
301322
'group': group,
302323
'review_requests': review_requests,
303324
'newly_closed': newly_closed,
304325
'newly_opened': newly_opened,
305326
'newly_assigned': newly_assigned,
306327
'saving': saving,
328+
'assignment_status': assignment_status,
329+
'other_assignment_status': other_assignment_status,
307330
})
308331

309332
class EmailOpenAssignmentsForm(forms.Form):
@@ -327,16 +350,21 @@ def email_open_review_assignments(request, acronym, group_type=None):
327350
reviewer=None,
328351
).prefetch_related("reviewer", "type", "state", "doc").distinct().order_by("deadline", "reviewer"))
329352

353+
back_url = request.GET.get("next")
354+
if not back_url:
355+
kwargs = { "acronym": group.acronym }
356+
if group_type:
357+
kwargs["group_type"] = group_type
358+
359+
import ietf.group.views_review
360+
back_url = urlreverse(ietf.group.views_review.review_requests, kwargs=kwargs)
361+
330362
if request.method == "POST" and request.POST.get("action") == "email":
331363
form = EmailOpenAssignmentsForm(request.POST)
332364
if form.is_valid():
333365
send_mail_text(request, form.cleaned_data["to"], None, form.cleaned_data["subject"], form.cleaned_data["body"])
334366

335-
kwargs = { "acronym": group.acronym }
336-
if group_type:
337-
kwargs["group_type"] = group_type
338-
339-
return redirect(manage_review_requests, **kwargs)
367+
return HttpResponseRedirect(back_url)
340368
else:
341369
to = group.list_email
342370
subject = "Open review assignments in {}".format(group.acronym)
@@ -356,6 +384,7 @@ def email_open_review_assignments(request, acronym, group_type=None):
356384
'group': group,
357385
'review_requests': review_requests,
358386
'form': form,
387+
'back_url': back_url,
359388
})
360389

361390

ietf/templates/group/email_open_review_assignments.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ <h1>Email summary of assigned review requests for {{ group.acronym }}</h1>
1616
{% bootstrap_form form %}
1717

1818
{% buttons %}
19-
<a href="{% url "ietf.group.views_review.manage_review_requests" group_type=group.type_id acronym=group.acronym %}" class="btn btn-default pull-right">Cancel</a>
19+
<a href="{{ back_url }}" class="btn btn-default pull-right">Cancel</a>
2020
<button class="btn btn-primary" type="submit" name="action" value="email">Send to team mailing list</button>
2121
{% endbuttons %}
2222
</form>

ietf/templates/group/manage_review_requests.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313
{% block content %}
1414
{% origin %}
1515

16-
<h1>Manage open review requests for {{ group.acronym }}</h1>
16+
<h1>Manage {{ assignment_status }} open review requests for {{ group.acronym }}</h1>
1717

1818
<p>Other options:
1919
<a href="{% url "ietf.group.views_review.reviewer_overview" group_type=group.type_id acronym=group.acronym %}">Reviewers in team</a>
20-
- <a href="{% url "ietf.group.views_review.email_open_review_assignments" group_type=group.type_id acronym=group.acronym %}">Email open assignments summary</a>
20+
- <a href="{% url "ietf.group.views_review.email_open_review_assignments" group_type=group.type_id acronym=group.acronym %}?next={{ request.get_full_path|urlencode }}">Email open assignments summary</a>
21+
{% if other_assignment_status %}
22+
- <a href="{% url "ietf.group.views_review.manage_review_requests" group_type=group.type_id acronym=group.acronym assignment_status=other_assignment_status %}">Manage {{ other_assignment_status }} reviews</a>
23+
{% endif %}
2124
</p>
2225

2326
{% if newly_closed > 0 or newly_opened > 0 or newly_assigned > 0 %}
@@ -136,7 +139,7 @@ <h1>Manage open review requests for {{ group.acronym }}</h1>
136139
{% endbuttons %}
137140
</form>
138141
{% else %}
139-
<p>There are currently no open requests.</p>
142+
<p>There are currently no {{ assignment_status }} open requests.</p>
140143
{% endif %}
141144
{% endblock %}
142145

ietf/templates/group/review_requests.html

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,47 @@
1717
<h1 class="pull-right"><a href="{% url "ietf.stats.views.review_stats" %}" class="icon-link">&nbsp;<span class="small fa fa-bar-chart">&nbsp;</span></a></h1>
1818
{% endif %}
1919

20-
<h2>Open review requests</h2>
20+
{% for label, review_requests in open_review_requests %}
21+
{% if review_requests %}
2122

22-
{% if open_review_requests %}
23-
<table class="table table-condensed table-striped tablesorter">
24-
<thead>
25-
<tr>
26-
<th>Request</th>
27-
<th>Type</th>
28-
<th>Requested</th>
29-
<th>Deadline</th>
30-
<th>Reviewer</th>
31-
</tr>
32-
</thead>
33-
<tbody>
34-
{% for r in open_review_requests %}
23+
<h2>{{ label }} open review requests</h2>
24+
25+
<table class="table table-condensed table-striped tablesorter">
26+
<thead>
3527
<tr>
36-
<td>{% if r.pk != None %}<a href="{% url "ietf.doc.views_review.review_request" name=r.doc.name request_id=r.pk %}">{% endif %}{{ r.doc.name }}-{% if r.requested_rev %}{{ r.requested_rev }}{% else %}{{ r.doc.rev }}{% endif %}{% if r.pk != None %}</a>{% endif %}</td>
37-
<td>{{ r.type.name }}</td>
38-
<td>{% if r.pk %}{{ r.time|date:"Y-m-d" }}{% else %}<em>auto-suggested</em>{% endif %}</td>
39-
<td>
40-
{{ r.deadline|date:"Y-m-d" }}
41-
{% if r.due %}<span class="label label-warning" title="{{ r.due }} day{{ r.due|pluralize }} past deadline">{{ r.due }} day{{ r.due|pluralize }}</span>{% endif %}
42-
</td>
43-
<td>
28+
<th>Request</th>
29+
<th>Type</th>
30+
<th>Requested</th>
31+
<th>Deadline</th>
32+
{% if review_requests.0.reviewer %}
33+
<th>Reviewer</th>
34+
{% endif %}
35+
</tr>
36+
</thead>
37+
<tbody>
38+
{% for r in review_requests %}
39+
<tr>
40+
<td>{% if r.pk != None %}<a href="{% url "ietf.doc.views_review.review_request" name=r.doc.name request_id=r.pk %}">{% endif %}{{ r.doc.name }}-{% if r.requested_rev %}{{ r.requested_rev }}{% else %}{{ r.doc.rev }}{% endif %}{% if r.pk != None %}</a>{% endif %}</td>
41+
<td>{{ r.type.name }}</td>
42+
<td>{% if r.pk %}{{ r.time|date:"Y-m-d" }}{% else %}<em>auto-suggested</em>{% endif %}</td>
43+
<td>
44+
{{ r.deadline|date:"Y-m-d" }}
45+
{% if r.due %}<span class="label label-warning" title="{{ r.due }} day{{ r.due|pluralize }} past deadline">{{ r.due }} day{{ r.due|pluralize }}</span>{% endif %}
46+
</td>
4447
{% if r.reviewer %}
45-
{{ r.reviewer.person }}
46-
{% if r.state_id == "accepted" %}<span class="label label-default">Accepted</span>{% endif %}
47-
{% if r.reviewer_unavailable %}<span class="label label-danger">Unavailable</span>{% endif %}
48-
{% elif r.pk != None %}
49-
<em>not yet assigned</em>
48+
<td>
49+
{{ r.reviewer.person }}
50+
{% if r.state_id == "accepted" %}<span class="label label-default">Accepted</span>{% endif %}
51+
{% if r.reviewer_unavailable %}<span class="label label-danger">Unavailable</span>{% endif %}
52+
</td>
5053
{% endif %}
51-
</td>
52-
</tr>
53-
{% endfor %}
54-
</tbody>
55-
</table>
56-
57-
{% else %}
58-
<p>There are currently no open requests.</p>
59-
{% endif %}
54+
</tr>
55+
{% endfor %}
56+
</tbody>
57+
</table>
58+
59+
{% endif %}
60+
{% endfor %}
6061

6162
<h2 id="closed-review-requests">Closed review requests</h2>
6263

0 commit comments

Comments
 (0)