Make changing skip_next on a review assignment an explicit decision of the assigner. Commit ready for merge. Fixes #2148.
- Legacy-Id: 12670
This commit is contained in:
parent
49dcf67fd5
commit
8e007ce50b
|
@ -24,6 +24,7 @@ from ietf.utils.test_utils import TestCase
|
||||||
from ietf.utils.test_data import make_test_data, make_review_data, create_person
|
from ietf.utils.test_data import make_test_data, make_review_data, create_person
|
||||||
from ietf.utils.test_utils import login_testing_unauthorized, unicontent, reload_db_objects
|
from ietf.utils.test_utils import login_testing_unauthorized, unicontent, reload_db_objects
|
||||||
from ietf.utils.mail import outbox, empty_outbox
|
from ietf.utils.mail import outbox, empty_outbox
|
||||||
|
from ietf.person.factories import PersonFactory
|
||||||
|
|
||||||
class ReviewTests(TestCase):
|
class ReviewTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -180,27 +181,29 @@ class ReviewTests(TestCase):
|
||||||
or ReviewerSettings(team=team))
|
or ReviewerSettings(team=team))
|
||||||
return settings.skip_next
|
return settings.skip_next
|
||||||
|
|
||||||
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[0].pk)
|
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[0].pk, add_skip=False)
|
||||||
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[1])
|
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[1])
|
||||||
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[1]), 0)
|
self.assertEqual(get_skip_next(reviewers[1]), 0)
|
||||||
|
|
||||||
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[1].pk)
|
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[1].pk, add_skip=True)
|
||||||
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[2])
|
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[2])
|
||||||
|
self.assertEqual(get_skip_next(reviewers[1]), 1)
|
||||||
|
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
||||||
|
|
||||||
# skip reviewer 2
|
# skip reviewer 2
|
||||||
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[3].pk)
|
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[3].pk, add_skip=True)
|
||||||
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[2])
|
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[2])
|
||||||
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[1]), 0)
|
self.assertEqual(get_skip_next(reviewers[1]), 1)
|
||||||
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[3]), 1)
|
self.assertEqual(get_skip_next(reviewers[3]), 1)
|
||||||
|
|
||||||
# pick reviewer 2, use up reviewer 3's skip_next
|
# pick reviewer 2, use up reviewer 3's skip_next
|
||||||
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[2].pk)
|
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[2].pk, add_skip=False)
|
||||||
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[4])
|
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[4])
|
||||||
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[1]), 0)
|
self.assertEqual(get_skip_next(reviewers[1]), 1)
|
||||||
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[3]), 0)
|
self.assertEqual(get_skip_next(reviewers[3]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[4]), 0)
|
self.assertEqual(get_skip_next(reviewers[4]), 0)
|
||||||
|
@ -209,7 +212,7 @@ class ReviewTests(TestCase):
|
||||||
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[4].pk)
|
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[4].pk)
|
||||||
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[0])
|
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[0])
|
||||||
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[1]), 0)
|
self.assertEqual(get_skip_next(reviewers[1]), 1)
|
||||||
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[3]), 0)
|
self.assertEqual(get_skip_next(reviewers[3]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[4]), 0)
|
self.assertEqual(get_skip_next(reviewers[4]), 0)
|
||||||
|
@ -220,13 +223,13 @@ class ReviewTests(TestCase):
|
||||||
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[0].pk)
|
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[0].pk)
|
||||||
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[2])
|
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[2])
|
||||||
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[1]), 0)
|
self.assertEqual(get_skip_next(reviewers[1]), 1) # don't consume that skip while the reviewer is unavailable
|
||||||
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
self.assertEqual(get_skip_next(reviewers[2]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[3]), 0)
|
self.assertEqual(get_skip_next(reviewers[3]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[4]), 0)
|
self.assertEqual(get_skip_next(reviewers[4]), 0)
|
||||||
|
|
||||||
# pick unavailable anyway
|
# pick unavailable anyway
|
||||||
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[1].pk)
|
possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id=reviewers[1].pk, add_skip=False)
|
||||||
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[2])
|
self.assertEqual(NextReviewerInTeam.objects.get(team=team).next_reviewer, reviewers[2])
|
||||||
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
self.assertEqual(get_skip_next(reviewers[0]), 0)
|
||||||
self.assertEqual(get_skip_next(reviewers[1]), 1)
|
self.assertEqual(get_skip_next(reviewers[1]), 1)
|
||||||
|
@ -283,6 +286,10 @@ class ReviewTests(TestCase):
|
||||||
reviewer_settings.skip_next = 1
|
reviewer_settings.skip_next = 1
|
||||||
reviewer_settings.save()
|
reviewer_settings.save()
|
||||||
|
|
||||||
|
# Need one more person in review team one so we can test incrementing skip_count without immediately decrementing it
|
||||||
|
another_reviewer = PersonFactory.create()
|
||||||
|
another_reviewer.role_set.create(name_id='reviewer', email=another_reviewer.email(), group=review_req.team)
|
||||||
|
|
||||||
UnavailablePeriod.objects.create(
|
UnavailablePeriod.objects.create(
|
||||||
team=review_req.team,
|
team=review_req.team,
|
||||||
person=reviewer_email.person,
|
person=reviewer_email.person,
|
||||||
|
@ -345,7 +352,7 @@ class ReviewTests(TestCase):
|
||||||
review_req.state = ReviewRequestStateName.objects.get(slug="accepted")
|
review_req.state = ReviewRequestStateName.objects.get(slug="accepted")
|
||||||
review_req.save()
|
review_req.save()
|
||||||
reviewer = Email.objects.filter(role__name="reviewer", role__group=review_req.team, person=rotation_list[1]).first()
|
reviewer = Email.objects.filter(role__name="reviewer", role__group=review_req.team, person=rotation_list[1]).first()
|
||||||
r = self.client.post(assign_url, { "action": "assign", "reviewer": reviewer.pk })
|
r = self.client.post(assign_url, { "action": "assign", "reviewer": reviewer.pk, "add_skip": 1 })
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
|
|
||||||
review_req = reload_db_objects(review_req)
|
review_req = reload_db_objects(review_req)
|
||||||
|
@ -354,6 +361,7 @@ class ReviewTests(TestCase):
|
||||||
self.assertEqual(len(outbox), 2)
|
self.assertEqual(len(outbox), 2)
|
||||||
self.assertTrue("cancelled your assignment" in outbox[0].get_payload(decode=True).decode("utf-8"))
|
self.assertTrue("cancelled your assignment" in outbox[0].get_payload(decode=True).decode("utf-8"))
|
||||||
self.assertTrue("assigned" in outbox[1].get_payload(decode=True).decode("utf-8"))
|
self.assertTrue("assigned" in outbox[1].get_payload(decode=True).decode("utf-8"))
|
||||||
|
self.assertEqual(ReviewerSettings.objects.get(person=reviewer.person).skip_next, 1)
|
||||||
|
|
||||||
def test_accept_reviewer_assignment(self):
|
def test_accept_reviewer_assignment(self):
|
||||||
doc = make_test_data()
|
doc = make_test_data()
|
||||||
|
|
|
@ -253,6 +253,7 @@ def close_request(request, name, request_id):
|
||||||
|
|
||||||
class AssignReviewerForm(forms.Form):
|
class AssignReviewerForm(forms.Form):
|
||||||
reviewer = PersonEmailChoiceField(empty_label="(None)", required=False)
|
reviewer = PersonEmailChoiceField(empty_label="(None)", required=False)
|
||||||
|
add_skip = forms.BooleanField(label='Skip next time', required=False)
|
||||||
|
|
||||||
def __init__(self, review_req, *args, **kwargs):
|
def __init__(self, review_req, *args, **kwargs):
|
||||||
super(AssignReviewerForm, self).__init__(*args, **kwargs)
|
super(AssignReviewerForm, self).__init__(*args, **kwargs)
|
||||||
|
@ -271,7 +272,8 @@ def assign_reviewer(request, name, request_id):
|
||||||
form = AssignReviewerForm(review_req, request.POST)
|
form = AssignReviewerForm(review_req, request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
reviewer = form.cleaned_data["reviewer"]
|
reviewer = form.cleaned_data["reviewer"]
|
||||||
assign_review_request_to_reviewer(request, review_req, reviewer)
|
add_skip = form.cleaned_data["add_skip"]
|
||||||
|
assign_review_request_to_reviewer(request, review_req, reviewer, add_skip)
|
||||||
|
|
||||||
return redirect(review_request, name=review_req.doc.name, request_id=review_req.pk)
|
return redirect(review_request, name=review_req.doc.name, request_id=review_req.pk)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -20,6 +20,7 @@ from ietf.name.models import ReviewTypeName, ReviewResultName, ReviewRequestStat
|
||||||
import ietf.group.views_review
|
import ietf.group.views_review
|
||||||
from ietf.utils.mail import outbox, empty_outbox
|
from ietf.utils.mail import outbox, empty_outbox
|
||||||
from ietf.dbtemplate.factories import DBTemplateFactory
|
from ietf.dbtemplate.factories import DBTemplateFactory
|
||||||
|
from ietf.person.factories import PersonFactory
|
||||||
|
|
||||||
class ReviewTests(TestCase):
|
class ReviewTests(TestCase):
|
||||||
def test_review_requests(self):
|
def test_review_requests(self):
|
||||||
|
@ -205,6 +206,10 @@ class ReviewTests(TestCase):
|
||||||
deadline=datetime.date.today() - datetime.timedelta(days=80),
|
deadline=datetime.date.today() - datetime.timedelta(days=80),
|
||||||
reviewer=review_req1.reviewer,
|
reviewer=review_req1.reviewer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Need one more person in review team one so we can test incrementing skip_count without immediately decrementing it
|
||||||
|
another_reviewer = PersonFactory.create()
|
||||||
|
another_reviewer.role_set.create(name_id='reviewer', email=another_reviewer.email(), group=review_req1.team)
|
||||||
|
|
||||||
# get
|
# get
|
||||||
r = self.client.get(assigned_url)
|
r = self.client.get(assigned_url)
|
||||||
|
@ -257,6 +262,7 @@ class ReviewTests(TestCase):
|
||||||
"r{}-existing_reviewer".format(review_req2.pk): review_req2.reviewer_id or "",
|
"r{}-existing_reviewer".format(review_req2.pk): review_req2.reviewer_id or "",
|
||||||
"r{}-action".format(review_req2.pk): "assign",
|
"r{}-action".format(review_req2.pk): "assign",
|
||||||
"r{}-reviewer".format(review_req2.pk): new_reviewer.pk,
|
"r{}-reviewer".format(review_req2.pk): new_reviewer.pk,
|
||||||
|
"r{}-add_skip".format(review_req2.pk) : 1,
|
||||||
|
|
||||||
"action": "save",
|
"action": "save",
|
||||||
})
|
})
|
||||||
|
@ -280,6 +286,8 @@ class ReviewTests(TestCase):
|
||||||
self.assertEqual(review_req1.state_id, "no-response")
|
self.assertEqual(review_req1.state_id, "no-response")
|
||||||
self.assertEqual(review_req2.state_id, "requested")
|
self.assertEqual(review_req2.state_id, "requested")
|
||||||
self.assertEqual(review_req2.reviewer, new_reviewer)
|
self.assertEqual(review_req2.reviewer, new_reviewer)
|
||||||
|
settings = ReviewerSettings.objects.filter(team=review_req2.team, person=new_reviewer.person).first()
|
||||||
|
self.assertEqual(settings.skip_next,1)
|
||||||
self.assertEqual(review_req3.state_id, "requested")
|
self.assertEqual(review_req3.state_id, "requested")
|
||||||
|
|
||||||
def test_email_open_review_assignments(self):
|
def test_email_open_review_assignments(self):
|
||||||
|
|
|
@ -188,6 +188,7 @@ class ManageReviewRequestForm(forms.Form):
|
||||||
action = forms.ChoiceField(choices=ACTIONS, widget=forms.HiddenInput, required=False)
|
action = forms.ChoiceField(choices=ACTIONS, widget=forms.HiddenInput, required=False)
|
||||||
close = forms.ModelChoiceField(queryset=close_review_request_states(), required=False)
|
close = forms.ModelChoiceField(queryset=close_review_request_states(), required=False)
|
||||||
reviewer = PersonEmailChoiceField(empty_label="(None)", required=False, label_with="person")
|
reviewer = PersonEmailChoiceField(empty_label="(None)", required=False, label_with="person")
|
||||||
|
add_skip = forms.BooleanField(required=False)
|
||||||
|
|
||||||
def __init__(self, review_req, *args, **kwargs):
|
def __init__(self, review_req, *args, **kwargs):
|
||||||
if not "prefix" in kwargs:
|
if not "prefix" in kwargs:
|
||||||
|
@ -307,7 +308,7 @@ def manage_review_requests(request, acronym, group_type=None, assignment_status=
|
||||||
for review_req in review_requests:
|
for review_req in review_requests:
|
||||||
action = review_req.form.cleaned_data.get("action")
|
action = review_req.form.cleaned_data.get("action")
|
||||||
if action == "assign":
|
if action == "assign":
|
||||||
assign_review_request_to_reviewer(request, review_req, review_req.form.cleaned_data["reviewer"])
|
assign_review_request_to_reviewer(request, review_req, review_req.form.cleaned_data["reviewer"],review_req.form.cleaned_data["add_skip"])
|
||||||
elif action == "close":
|
elif action == "close":
|
||||||
close_review_request(request, review_req, review_req.form.cleaned_data["close"])
|
close_review_request(request, review_req, review_req.form.cleaned_data["close"])
|
||||||
|
|
||||||
|
|
|
@ -417,7 +417,7 @@ def email_reviewer_availability_change(request, team, reviewer_role, msg, by):
|
||||||
"by": by,
|
"by": by,
|
||||||
})
|
})
|
||||||
|
|
||||||
def assign_review_request_to_reviewer(request, review_req, reviewer):
|
def assign_review_request_to_reviewer(request, review_req, reviewer, add_skip=False):
|
||||||
assert review_req.state_id in ("requested", "accepted")
|
assert review_req.state_id in ("requested", "accepted")
|
||||||
|
|
||||||
if reviewer == review_req.reviewer:
|
if reviewer == review_req.reviewer:
|
||||||
|
@ -435,7 +435,7 @@ def assign_review_request_to_reviewer(request, review_req, reviewer):
|
||||||
review_req.save()
|
review_req.save()
|
||||||
|
|
||||||
if review_req.reviewer:
|
if review_req.reviewer:
|
||||||
possibly_advance_next_reviewer_for_team(review_req.team, review_req.reviewer.person_id)
|
possibly_advance_next_reviewer_for_team(review_req.team, review_req.reviewer.person_id, add_skip)
|
||||||
|
|
||||||
ReviewRequestDocEvent.objects.create(
|
ReviewRequestDocEvent.objects.create(
|
||||||
type="assigned_review_request",
|
type="assigned_review_request",
|
||||||
|
@ -456,7 +456,7 @@ def assign_review_request_to_reviewer(request, review_req, reviewer):
|
||||||
"%s has assigned you as a reviewer for this document." % request.user.person,
|
"%s has assigned you as a reviewer for this document." % request.user.person,
|
||||||
by=request.user.person, notify_secretary=False, notify_reviewer=True, notify_requested_by=False)
|
by=request.user.person, notify_secretary=False, notify_reviewer=True, notify_requested_by=False)
|
||||||
|
|
||||||
def possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id):
|
def possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id, add_skip=False):
|
||||||
assert assigned_review_to_person_id is not None
|
assert assigned_review_to_person_id is not None
|
||||||
|
|
||||||
rotation_list = reviewer_rotation_list(team, skip_unavailable=True, dont_skip=[assigned_review_to_person_id])
|
rotation_list = reviewer_rotation_list(team, skip_unavailable=True, dont_skip=[assigned_review_to_person_id])
|
||||||
|
@ -476,7 +476,8 @@ def possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id):
|
||||||
if assigned_review_to_person_id == reviewer_at_index(current_i):
|
if assigned_review_to_person_id == reviewer_at_index(current_i):
|
||||||
# move 1 ahead
|
# move 1 ahead
|
||||||
current_i += 1
|
current_i += 1
|
||||||
else:
|
|
||||||
|
if add_skip:
|
||||||
settings = reviewer_settings_for(assigned_review_to_person_id)
|
settings = reviewer_settings_for(assigned_review_to_person_id)
|
||||||
settings.skip_next += 1
|
settings.skip_next += 1
|
||||||
settings.save()
|
settings.save()
|
||||||
|
@ -491,7 +492,6 @@ def possibly_advance_next_reviewer_for_team(team, assigned_review_to_person_id):
|
||||||
if settings.skip_next > 0:
|
if settings.skip_next > 0:
|
||||||
settings.skip_next -= 1
|
settings.skip_next -= 1
|
||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
current_i += 1
|
current_i += 1
|
||||||
else:
|
else:
|
||||||
nr = NextReviewerInTeam.objects.filter(team=team).first() or NextReviewerInTeam(team=team)
|
nr = NextReviewerInTeam.objects.filter(team=team).first() or NextReviewerInTeam(team=team)
|
||||||
|
|
|
@ -135,13 +135,17 @@
|
||||||
|
|
||||||
<span class="reviewer-controls form-inline">
|
<span class="reviewer-controls form-inline">
|
||||||
<label for="{{ r.form.reviewer.id_for_label }}">Assign:</label>
|
<label for="{{ r.form.reviewer.id_for_label }}">Assign:</label>
|
||||||
{{ r.form.reviewer }}
|
{{ r.form.reviewer }}
|
||||||
|
{{ r.form.add_skip }} <label for="{{ r.form.add_skip.id_for_label }}">Skip next time</label>
|
||||||
<button type="button" class="btn btn-default undo" title="Cancel assignment" data-initial="{{ r.form.fields.reviewer.initial|default:"" }}">Cancel</button>
|
<button type="button" class="btn btn-default undo" title="Cancel assignment" data-initial="{{ r.form.fields.reviewer.initial|default:"" }}">Cancel</button>
|
||||||
{% if r.form.reviewer.errors %}
|
{% if r.form.reviewer.errors or r.form.add_skip.errors %}
|
||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
{% for e in r.form.reviewer.errors %}
|
{% for e in r.form.reviewer.errors %}
|
||||||
{{ e }}
|
{{ e }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% for e in r.form.add_skip.errors %}
|
||||||
|
{{ e }}
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
|
|
Loading…
Reference in a new issue