# Copyright The IETF Trust 2016-2020, All Rights Reserved # -*- coding: utf-8 -*- import datetime import debug # pyflakes:ignore from pyquery import PyQuery from django.urls import reverse as urlreverse from ietf.review.policies import get_reviewer_queue_policy from ietf.utils.test_utils import login_testing_unauthorized, TestCase, reload_db_objects from ietf.doc.models import TelechatDocEvent, LastCallDocEvent, State from ietf.iesg.models import TelechatDate from ietf.person.models import Person from ietf.review.models import ReviewerSettings, UnavailablePeriod, ReviewSecretarySettings,NextReviewerInTeam from ietf.review.utils import suggested_review_requests_for_team from ietf.name.models import ReviewResultName, ReviewRequestStateName, ReviewAssignmentStateName, \ ReviewTypeName import ietf.group.views from ietf.utils.mail import outbox, empty_outbox, get_payload_text from ietf.dbtemplate.factories import DBTemplateFactory from ietf.person.factories import PersonFactory, EmailFactory from ietf.doc.factories import DocumentFactory from ietf.group.factories import RoleFactory, ReviewTeamFactory, GroupFactory from ietf.review.factories import ReviewRequestFactory, ReviewerSettingsFactory, ReviewAssignmentFactory from django.utils.html import escape class ReviewTests(TestCase): def test_review_requests(self): review_req = ReviewRequestFactory(state_id='assigned') assignment = ReviewAssignmentFactory(review_request=review_req, state_id='assigned', reviewer=EmailFactory(), assigned_on = review_req.time) group = review_req.team for url in [urlreverse(ietf.group.views.review_requests, kwargs={ 'acronym': group.acronym }), urlreverse(ietf.group.views.review_requests, kwargs={ 'acronym': group.acronym , 'group_type': group.type_id})]: r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, review_req.doc.name) self.assertContains(r, assignment.reviewer.person.name) url = urlreverse(ietf.group.views.review_requests, kwargs={ 'acronym': group.acronym }) # close request, listed under closed review_req.state = ReviewRequestStateName.objects.get(slug="completed") review_req.result = ReviewResultName.objects.get(slug="ready") review_req.save() r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, review_req.doc.name) def test_suggested_review_requests(self): review_req = ReviewRequestFactory(state_id='assigned') assignment = ReviewAssignmentFactory(review_request=review_req, state_id='assigned') doc = review_req.doc team = review_req.team # put on telechat e = TelechatDocEvent.objects.create( type="scheduled_for_telechat", by=Person.objects.get(name="(System)"), doc=doc, rev=doc.rev, telechat_date=TelechatDate.objects.all().first().date, ) doc.rev = "10" doc.save_with_history([e]) prev_rev = "{:02}".format(int(doc.rev) - 1) # blocked by existing request review_req.requested_rev = "" review_req.save() self.assertEqual(len(suggested_review_requests_for_team(team)), 0) # ... but not to previous version review_req.requested_rev = prev_rev review_req.save() suggestions = suggested_review_requests_for_team(team) self.assertEqual(len(suggestions), 1) self.assertEqual(suggestions[0].doc, doc) self.assertEqual(suggestions[0].team, team) self.assertFalse(getattr(suggestions[0], 'in_lc_and_telechat', None)) # blocked by non-versioned refusal review_req.requested_rev = "" review_req.state = ReviewRequestStateName.objects.get(slug="no-review-document") review_req.save() self.assertEqual(list(suggested_review_requests_for_team(team)), []) # blocked by versioned refusal review_req.state = ReviewRequestStateName.objects.get(slug="no-review-version") review_req.save() self.assertEqual(list(suggested_review_requests_for_team(team)), []) # blocked by completion review_req.state = ReviewRequestStateName.objects.get(slug="assigned") review_req.save() assignment.state = ReviewAssignmentStateName.objects.get(slug="completed") assignment.reviewed_rev = review_req.doc.rev assignment.save() self.assertEqual(list(suggested_review_requests_for_team(team)), []) # ... but not to previous version assignment.reviewed_rev = prev_rev assignment.save() self.assertEqual(len(suggested_review_requests_for_team(team)), 1) def test_suggested_review_requests_on_lc_and_telechat(self): review_req = ReviewRequestFactory(state_id='assigned') doc = review_req.doc team = review_req.team # put on telechat TelechatDocEvent.objects.create( type="scheduled_for_telechat", by=Person.objects.get(name="(System)"), doc=doc, rev=doc.rev, telechat_date=TelechatDate.objects.all().first().date, ) # Put on last call as well doc.states.add(State.objects.get(type="draft-iesg", slug="lc", used=True)) LastCallDocEvent.objects.create( doc=doc, expires=datetime.datetime.now() + datetime.timedelta(days=365), by=Person.objects.get(name="(System)"), rev=doc.rev ) suggestions = suggested_review_requests_for_team(team) self.assertEqual(len(suggestions), 1) self.assertEqual(suggestions[0].doc, doc) self.assertEqual(suggestions[0].team, team) self.assertTrue(suggestions[0].in_lc_and_telechat) def test_reviewer_overview(self): team = ReviewTeamFactory() reviewer = RoleFactory(name_id='reviewer',group=team,person__user__username='reviewer').person ReviewerSettingsFactory(person=reviewer,team=team) review_req1 = ReviewRequestFactory(state_id='completed',team=team) secretary = Person.objects.get(user__username="secretary") ReviewAssignmentFactory(review_request = review_req1, reviewer=reviewer.email()) PersonFactory(user__username='plain') ReviewAssignmentFactory( review_request__doc=review_req1.doc, review_request__team=review_req1.team, review_request__type_id="early", review_request__deadline=datetime.date.today() + datetime.timedelta(days=30), review_request__state_id="assigned", review_request__requested_by=Person.objects.get(user__username="reviewer"), state_id = "accepted", reviewer=reviewer.email_set.first(), ) UnavailablePeriod.objects.create( team=review_req1.team, person=reviewer, start_date=datetime.date.today() - datetime.timedelta(days=10), availability="unavailable", ) settings = ReviewerSettings.objects.get(person=reviewer,team=review_req1.team) settings.skip_next = 1 settings.save() group = review_req1.team # get for url in [urlreverse(ietf.group.views.reviewer_overview, kwargs={ 'acronym': group.acronym }), urlreverse(ietf.group.views.reviewer_overview, kwargs={ 'acronym': group.acronym, 'group_type': group.type_id })]: r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, reviewer.name) self.assertContains(r, review_req1.doc.name) # without a login, reason for being unavailable should not be seen self.assertNotContains(r, "Availability") url = urlreverse(ietf.group.views.reviewer_overview, kwargs={ 'acronym': group.acronym }) self.client.login(username="plain", password="plain+password") r = self.client.get(url) self.assertEqual(r.status_code, 200) # not on review team, should not see reason for being unavailable self.assertNotContains(r, "Availability") self.client.login(username="reviewer", password="reviewer+password") r = self.client.get(url) self.assertEqual(r.status_code, 200) # review team members can see reason for being unavailable self.assertContains(r, "Availability") self.client.login(username="secretary", password="secretary+password") r = self.client.get(url) self.assertEqual(r.status_code, 200) # secretariat can see reason for being unavailable self.assertContains(r, "Availability") # add one closed review with no response and see it is visible review_req2 = ReviewRequestFactory(state_id='completed',team=team) ReviewAssignmentFactory( review_request__doc=review_req2.doc, review_request__team=review_req2.team, review_request__type_id="lc", review_request__deadline=datetime.date.today() - datetime.timedelta(days=30), review_request__state_id="assigned", review_request__requested_by=Person.objects.get(user__username="reviewer"), state_id = "no-response", reviewer=reviewer.email_set.first(), ) self.client.login(username="secretary", password="secretary+password") r = self.client.get(url) self.assertEqual(r.status_code, 200) # We should see the new document with status of no response self.assertContains(r, "No Response") self.assertContains(r, review_req1.doc.name) self.assertContains(r, review_req2.doc.name) # None of the reviews should be completed this time, # note that "Days Since Completed has soft hypens in it, so it # will not match self.assertNotContains(r, "Completed") # add multiple completed reviews review_req3 = ReviewRequestFactory(state_id='completed', team=team) ReviewAssignmentFactory( review_request__doc=review_req3.doc, review_request__time=datetime.date.today() - datetime.timedelta(days=30), review_request__team=review_req3.team, review_request__type_id="telechat", review_request__deadline=datetime.date.today() - datetime.timedelta(days=25), review_request__state_id="completed", review_request__requested_by=Person.objects.get(user__username="reviewer"), state_id = "completed", reviewer=reviewer.email_set.first(), assigned_on=datetime.date.today() - datetime.timedelta(days=30) ) r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, "Completed") self.assertContains(r, review_req1.doc.name) self.assertContains(r, review_req2.doc.name) self.assertContains(r, review_req3.doc.name) # Few more to make sure we hit the limit reqs = [ReviewRequestFactory(state_id='completed', team=team) for i in range(10)] for i in range(10): ReviewAssignmentFactory( review_request__doc=reqs[i].doc, review_request__time=datetime.date.today() - datetime.timedelta(days=i*30), review_request__team=reqs[i].team, review_request__type_id="telechat", review_request__deadline=datetime.date.today() - datetime.timedelta(days=i*20), review_request__state_id="completed", review_request__requested_by=Person.objects.get(user__username="reviewer"), state_id = "completed", reviewer=reviewer.email_set.first(), assigned_on=datetime.date.today() - datetime.timedelta(days=i*30) ) r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, "Completed") self.assertContains(r, review_req1.doc.name) self.assertContains(r, review_req2.doc.name) self.assertContains(r, review_req3.doc.name) # The default is 10 completed entries, we had one before + one No response # so we should have 8 more here for i in range(10): if i < 8: self.assertContains(r, reqs[i].doc.name) else: self.assertNotContains(r, reqs[i].doc.name) # Change the limit to be 15 instead of 10, so we should get all secretary_settings = ReviewSecretarySettings(team=team, person=secretary) secretary_settings.max_items_to_show_in_reviewer_list = 15 secretary_settings.save() r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, review_req1.doc.name) self.assertContains(r, review_req2.doc.name) self.assertContains(r, review_req3.doc.name) for i in range(10): self.assertContains(r, reqs[i].doc.name) # Change the time limit to 100 days, so we should only get 3 completed secretary_settings.days_to_show_in_reviewer_list = 100 secretary_settings.save() r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, review_req1.doc.name) self.assertContains(r, review_req2.doc.name) self.assertContains(r, review_req3.doc.name) for i in range(10): if i < 4: self.assertContains(r, reqs[i].doc.name) else: self.assertNotContains(r, reqs[i].doc.name) # Add assigned items, they should be visible as long as they # are withing time period review_req4 = ReviewRequestFactory(state_id='completed', team=team) ReviewAssignmentFactory( review_request__doc=review_req4.doc, review_request__time=datetime.date.today() - datetime.timedelta(days=80), review_request__team=review_req4.team, review_request__type_id="lc", review_request__deadline=datetime.date.today() - datetime.timedelta(days=60), review_request__state_id="assigned", review_request__requested_by=Person.objects.get(user__username="reviewer"), state_id = "accepted", reviewer=reviewer.email_set.first(), assigned_on=datetime.date.today() - datetime.timedelta(days=80) ) review_req5 = ReviewRequestFactory(state_id='completed', team=team) ReviewAssignmentFactory( review_request__doc=review_req5.doc, review_request__time=datetime.date.today() - datetime.timedelta(days=120), review_request__team=review_req5.team, review_request__type_id="lc", review_request__deadline=datetime.date.today() - datetime.timedelta(days=100), review_request__state_id="assigned", review_request__requested_by=Person.objects.get(user__username="reviewer"), state_id = "accepted", reviewer=reviewer.email_set.first(), assigned_on=datetime.date.today() - datetime.timedelta(days=120) ) r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, review_req1.doc.name) self.assertContains(r, review_req2.doc.name) self.assertContains(r, review_req3.doc.name) self.assertContains(r, review_req4.doc.name) self.assertNotContains(r, review_req5.doc.name) for i in range(10): if i < 4: self.assertContains(r, reqs[i].doc.name) else: self.assertNotContains(r, reqs[i].doc.name) # Change the limit to be 1 instead of 10, so we should only one entry secretary_settings.max_items_to_show_in_reviewer_list = 1 secretary_settings.save() r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertContains(r, review_req1.doc.name) self.assertContains(r, review_req2.doc.name) self.assertNotContains(r, review_req3.doc.name) for i in range(10): self.assertNotContains(r, reqs[i].doc.name) self.assertContains(r, review_req4.doc.name) self.assertNotContains(r, review_req5.doc.name) def test_manage_review_requests(self): group = ReviewTeamFactory() RoleFactory(name_id='reviewer',group=group,person__user__username='reviewer').person marsperson = RoleFactory(name_id='reviewer',group=group,person=PersonFactory(name="Mars Anders Chairman",user__username='marschairman')).person doc_author = PersonFactory() review_req1 = ReviewRequestFactory(doc__pages=2,doc__shepherd=marsperson.email(),team=group, doc__authors=[doc_author]) review_req2 = ReviewRequestFactory(team=group) review_req3 = ReviewRequestFactory(team=group) RoleFactory(name_id='chair',group=review_req1.doc.group,person=marsperson) unassigned_url = urlreverse(ietf.group.views.manage_review_requests, kwargs={ 'acronym': group.acronym, 'group_type': group.type_id, "assignment_status": "unassigned" }) login_testing_unauthorized(self, "secretary", unassigned_url) # Need one more person in review team one so we can test incrementing skip_count without immediately decrementing it another_reviewer = PersonFactory.create(name = "Extra TestReviewer") # needs to be lexically greater than the exsting one another_reviewer.role_set.create(name_id='reviewer', email=another_reviewer.email(), group=review_req1.team) ReviewerSettingsFactory(team=review_req3.team, person = another_reviewer) yet_another_reviewer = PersonFactory.create(name = "YetAnotherExtra TestReviewer") # needs to be lexically greater than the exsting one yet_another_reviewer.role_set.create(name_id='reviewer', email=yet_another_reviewer.email(), group=review_req1.team) ReviewerSettingsFactory(team=review_req3.team, person = yet_another_reviewer) # get r = self.client.get(unassigned_url) self.assertEqual(r.status_code, 200) self.assertContains(r, escape(review_req1.doc.name)) self.assertContains(r, escape(doc_author.name)) # Test that conflicts are detected r = self.client.post(unassigned_url, { "reviewrequest": [str(review_req3.pk)], "r{}-existing_reviewer".format(review_req3.pk): "", "r{}-action".format(review_req3.pk): "assign", "r{}-reviewer".format(review_req3.pk): another_reviewer.email_set.first().pk, "r{}-add_skip".format(review_req3.pk): 1, "action": "save", }) self.assertContains(r, "2 requests opened") r = self.client.post(unassigned_url, { "reviewrequest": [str(review_req1.pk),str(review_req2.pk),str(review_req3.pk)], "r{}-existing_reviewer".format(review_req3.pk): "", "r{}-action".format(review_req3.pk): "assign", "r{}-reviewer".format(review_req3.pk): another_reviewer.email_set.first().pk, "r{}-add_skip".format(review_req3.pk): 1, # Should be ignored, setting review_type only applies to suggested reviews "r{}-review_type".format(review_req3.pk): ReviewTypeName.objects.get(slug='telechat').pk, "action": "save", }) self.assertEqual(r.status_code, 302) review_req3 = reload_db_objects(review_req3) settings = ReviewerSettings.objects.filter(team=review_req3.team, person=another_reviewer).first() self.assertEqual(settings.skip_next,1) self.assertEqual(review_req3.state_id, "assigned") self.assertEqual(review_req3.type_id, "lc") def test_manage_review_requests_assign_suggested_reviews(self): doc = DocumentFactory() team = ReviewTeamFactory() # put on telechat TelechatDocEvent.objects.create( type="scheduled_for_telechat", by=Person.objects.get(name="(System)"), doc=doc, rev=doc.rev, telechat_date=TelechatDate.objects.all().first().date, ) # Put on last call as well doc.states.add(State.objects.get(type="draft-iesg", slug="lc", used=True)) LastCallDocEvent.objects.create( doc=doc, expires=datetime.datetime.now() + datetime.timedelta(days=365), by=Person.objects.get(name="(System)"), rev=doc.rev ) reviewer = RoleFactory(name_id='reviewer', group=team, person__user__username='reviewer') unassigned_url = urlreverse(ietf.group.views.manage_review_requests, kwargs={ 'acronym': team.acronym, 'group_type': team.type_id, "assignment_status": "unassigned" }) login_testing_unauthorized(self, "secretary", unassigned_url) # get r = self.client.get(unassigned_url) self.assertEqual(r.status_code, 200) self.assertContains(r, doc.name) # Submit as lc review r = self.client.post(unassigned_url, { "rlc-{}-action".format(doc.name): "assign", "rlc-{}-review_type".format(doc.name): ReviewTypeName.objects.get(slug='lc').pk, "rlc-{}-reviewer".format(doc.name): reviewer.person.email_set.first().pk, "action": "save", }) self.assertEqual(r.status_code, 302) review_request = doc.reviewrequest_set.get() self.assertEqual(review_request.type_id, 'lc') # Clean up and try again as telechat review_request.reviewassignment_set.all().delete() review_request.delete() r = self.client.post(unassigned_url, { "rlc-{}-action".format(doc.name): "assign", "rlc-{}-review_type".format(doc.name): ReviewTypeName.objects.get(slug='telechat').pk, "rlc-{}-reviewer".format(doc.name): reviewer.person.email_set.first().pk, "action": "save", }) self.assertEqual(r.status_code, 302) review_request = doc.reviewrequest_set.get() self.assertEqual(review_request.type_id, 'telechat') def test_email_open_review_assignments(self): review_req1 = ReviewRequestFactory() review_assignment_completed = ReviewAssignmentFactory(review_request=review_req1,reviewer=EmailFactory(person__user__username='marschairman'), state_id='completed', reviewed_rev=0) ReviewAssignmentFactory(review_request=review_req1,reviewer=review_assignment_completed.reviewer) TelechatDocEvent.objects.create(telechat_date=datetime.date.today(), type='scheduled_for_telechat', by=review_assignment_completed.reviewer.person, doc=review_req1.doc, rev=0) DBTemplateFactory.create(path='/group/defaults/email/open_assignments.txt', type_id='django', content = """ {% autoescape off %}Subject: Open review assignments in {{group.acronym}} The following reviewers have assignments:{% for r in review_assignments %}{% ifchanged r.section %} {{r.section}} {% if r.section == 'Early review requests:' %}Reviewer Due Draft{% else %}Reviewer LC end Draft{% endif %}{% endifchanged %} {{ r.reviewer.person.plain_name|ljust:"22" }} {% if r.section == 'Early review requests:' %}{{ r.review_request.deadline|date:"Y-m-d" }}{% else %}{{ r.lastcall_ends|default:"None " }}{% endif %} {{ r.review_request.doc.name }}-{% if r.review_request.requested_rev %}{{ r.review_request.requested_rev }}{% else %}{{ r.review_request..doc.rev }}{% endif %} {{ r.earlier_reviews }}{% endfor %} {% if rotation_list %}Next in the reviewer rotation: {% for p in rotation_list %} {{ p }} {% endfor %}{% endif %}{% endautoescape %} """) group = review_req1.team url = urlreverse(ietf.group.views.email_open_review_assignments, kwargs={ 'acronym': group.acronym }) login_testing_unauthorized(self, "secretary", url) url = urlreverse(ietf.group.views.email_open_review_assignments, kwargs={ 'acronym': group.acronym, 'group_type': group.type_id }) r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) generated_text = q("[name=body]").text() # The document should be listed both for the telechat, and in the last call section, # i.e. the document name is expected twice in the output (#2118) self.assertEqual(generated_text.count(review_req1.doc.name), 2) self.assertEqual(generated_text.count('(-0 lc reviewed)'), 2) # previous completed assignment self.assertTrue(str(Person.objects.get(user__username="marschairman")) in generated_text) empty_outbox() r = self.client.post(url, { "to": 'toaddr@bogus.test', "cc": 'ccaddr@bogus.test', "reply_to": 'replytoaddr@bogus.test', "frm" : 'fromaddr@bogus.test', "subject": "Test subject", "body": "Test body", "action": "email", }) self.assertEqual(r.status_code, 302) self.assertEqual(len(outbox), 1) self.assertIn('toaddr', outbox[0]["To"]) self.assertIn('ccaddr', outbox[0]["Cc"]) self.assertIn('replytoaddr', outbox[0]["Reply-To"]) self.assertIn('fromaddr', outbox[0]["From"]) self.assertEqual(outbox[0]["subject"], "Test subject") self.assertIn("Test body", get_payload_text(outbox[0])) def test_change_reviewer_settings(self): reviewer = ReviewerSettingsFactory(person__user__username='reviewer',expertise='Some expertise').person review_req = ReviewRequestFactory() assignment = ReviewAssignmentFactory(review_request=review_req,reviewer=reviewer.email()) RoleFactory(name_id='reviewer',group=review_req.team,person=assignment.reviewer.person) RoleFactory(name_id='secr',group=review_req.team) url = urlreverse(ietf.group.views.change_reviewer_settings, kwargs={ "acronym": review_req.team.acronym, "reviewer_email": assignment.reviewer_id, }) login_testing_unauthorized(self, reviewer.user.username, url) url = urlreverse(ietf.group.views.change_reviewer_settings, kwargs={ "group_type": review_req.team.type_id, "acronym": review_req.team.acronym, "reviewer_email": assignment.reviewer_id, }) # get r = self.client.get(url) self.assertEqual(r.status_code, 200) self.assertEqual(r.context['period_form']['start_date'].initial, datetime.date.today()) # set settings empty_outbox() r = self.client.post(url, { "action": "change_settings", "min_interval": "7", "filter_re": "test-[regexp]", "remind_days_before_deadline": "6", "remind_days_open_reviews": "8", "request_assignment_next": "1", "expertise": "Some expertise", }) self.assertEqual(r.status_code, 302) settings = ReviewerSettings.objects.get(person=reviewer, team=review_req.team) self.assertEqual(settings.min_interval, 7) self.assertEqual(settings.filter_re, "test-[regexp]") self.assertEqual(settings.skip_next, 0) self.assertEqual(settings.remind_days_before_deadline, 6) self.assertEqual(settings.remind_days_open_reviews, 8) self.assertEqual(settings.expertise, "Some expertise") self.assertEqual(len(outbox), 1) self.assertTrue("reviewer availability" in outbox[0]["subject"].lower()) msg_content = get_payload_text(outbox[0]) self.assertTrue("Frequency changed", msg_content) self.assertTrue("Skip next", msg_content) self.assertTrue("requested to be the next person", msg_content) # Normal reviewer should not be able to change skip_next r = self.client.post(url, { "action": "change_settings", "min_interval": "7", "filter_re": "test-[regexp]", "remind_days_before_deadline": "6", "skip_next" : "2", }) self.assertEqual(r.status_code, 302) settings = ReviewerSettings.objects.get(person=reviewer, team=review_req.team) self.assertEqual(settings.skip_next, 0) # add unavailable period start_date = datetime.date.today() + datetime.timedelta(days=10) empty_outbox() r = self.client.post(url, { "action": "add_period", 'start_date': start_date.isoformat(), 'end_date': "", 'availability': "unavailable", 'reason': "Whimsy", }) self.assertEqual(r.status_code, 302) period = UnavailablePeriod.objects.get(person=reviewer, team=review_req.team, start_date=start_date) self.assertEqual(period.end_date, None) self.assertEqual(period.availability, "unavailable") self.assertEqual(len(outbox), 1) msg_content = get_payload_text(outbox[0]) self.assertTrue(start_date.isoformat(), msg_content) self.assertTrue("indefinite", msg_content) self.assertTrue("Whimsy", msg_content) self.assertEqual(period.reason, "Whimsy") # end unavailable period empty_outbox() end_date = start_date + datetime.timedelta(days=10) r = self.client.post(url, { "action": "end_period", 'period_id': period.pk, 'end_date': end_date.isoformat(), }) self.assertEqual(r.status_code, 302) period = reload_db_objects(period) self.assertEqual(period.end_date, end_date) self.assertEqual(len(outbox), 1) msg_content = get_payload_text(outbox[0]) self.assertTrue('Set end date', msg_content) self.assertTrue(start_date.isoformat(), msg_content) self.assertTrue(end_date.isoformat(), msg_content) self.assertTrue("indefinite", msg_content) # delete unavailable period empty_outbox() r = self.client.post(url, { "action": "delete_period", 'period_id': period.pk, }) self.assertEqual(r.status_code, 302) self.assertEqual(UnavailablePeriod.objects.filter(person=reviewer, team=review_req.team, start_date=start_date).count(), 0) self.assertEqual(len(outbox), 1) msg_content = get_payload_text(outbox[0]) self.assertTrue('Removed', msg_content) self.assertTrue(start_date.isoformat(), msg_content) self.assertTrue(end_date.isoformat(), msg_content) # secretaries and the secretariat should be able to change skip_next for username in ["secretary","reviewsecretary"]: skip_next_val = {'secretary':'3','reviewsecretary':'4'}[username] self.client.login(username=username,password=username+"+password") r = self.client.post(url, { "action": "change_settings", "min_interval": "7", "filter_re": "test-[regexp]", "remind_days_before_deadline": "6", "skip_next" : skip_next_val, }) self.assertEqual(r.status_code, 302) settings = ReviewerSettings.objects.get(person=reviewer, team=review_req.team) self.assertEqual(settings.skip_next, int(skip_next_val)) def test_change_review_secretary_settings(self): review_req = ReviewRequestFactory() secretary = RoleFactory(name_id='secr',group=review_req.team,person__user__username='reviewsecretary').person url = urlreverse(ietf.group.views.change_review_secretary_settings, kwargs={ "acronym": review_req.team.acronym, }) login_testing_unauthorized(self, secretary.user.username, url) url = urlreverse(ietf.group.views.change_review_secretary_settings, kwargs={ "group_type": review_req.team.type_id, "acronym": review_req.team.acronym, }) # get r = self.client.get(url) self.assertEqual(r.status_code, 200) # set settings r = self.client.post(url, { "remind_days_before_deadline": "6", "max_items_to_show_in_reviewer_list": 10, "days_to_show_in_reviewer_list": 365 }) self.assertEqual(r.status_code, 302) settings = ReviewSecretarySettings.objects.get(person=secretary, team=review_req.team) self.assertEqual(settings.remind_days_before_deadline, 6) self.assertEqual(settings.max_items_to_show_in_reviewer_list, 10) self.assertEqual(settings.days_to_show_in_reviewer_list, 365) class BulkAssignmentTests(TestCase): def test_rotation_queue_update(self): group = ReviewTeamFactory.create() empty_outbox() reviewers = [RoleFactory.create(group=group,name_id='reviewer') for i in range(6)] # pyflakes:ignore secretary = RoleFactory.create(group=group,name_id='secr') docs = [DocumentFactory.create(type_id='draft',group=None) for i in range(4)] requests = [ReviewRequestFactory(team=group,doc=docs[i]) for i in range(4)] policy = get_reviewer_queue_policy(group) rot_list = policy.default_reviewer_rotation_list() expected_ending_head_of_rotation = rot_list[3] unassigned_url = urlreverse(ietf.group.views.manage_review_requests, kwargs={ 'acronym': group.acronym, 'group_type': group.type_id, "assignment_status": "unassigned" }) postdict = {} postdict['reviewrequest'] = [r.id for r in requests] # assignments that affect the first 3 reviewers in queue for i in range(3): postdict['r{}-existing_reviewer'.format(requests[i].pk)] = '' postdict['r{}-action'.format(requests[i].pk)] = 'assign' postdict['r{}-reviewer'.format(requests[i].pk)] = rot_list[i].email_address() # and one out of order assignment postdict['r{}-existing_reviewer'.format(requests[3].pk)] = '' postdict['r{}-action'.format(requests[3].pk)] = 'assign' postdict['r{}-reviewer'.format(requests[3].pk)] = rot_list[5].email_address() postdict['action'] = 'save' self.client.login(username=secretary.person.user.username,password=secretary.person.user.username+'+password') r = self.client.post(unassigned_url, postdict) self.assertEqual(r.status_code,302) self.assertEqual(expected_ending_head_of_rotation, policy.default_reviewer_rotation_list()[0]) self.assertMailboxContains(outbox, subject='Last Call assignment', text='Requested by', count=4) class ResetNextReviewerInTeamTests(TestCase): def test_reviewer_overview_navigation(self): group = ReviewTeamFactory(settings__reviewer_queue_policy_id = 'RotateAlphabetically') url = urlreverse(ietf.group.views.reviewer_overview, kwargs={ 'acronym': group.acronym }) r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) self.assertFalse(q('#reset_next_reviewer')) self.client.login(username="secretary", password="secretary+password") r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) self.assertTrue(q('#reset_next_reviewer')) group.reviewteamsettings.reviewer_queue_policy_id='LeastRecentlyUsed' group.reviewteamsettings.save() r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) self.assertFalse(q('#reset_next_reviewer')) def test_reset_next_reviewer(self): PersonFactory(user__username='plain') for group in (GroupFactory(), ReviewTeamFactory(settings__reviewer_queue_policy_id='LeastRecentlyUsed')): url = urlreverse('ietf.group.views.reset_next_reviewer', kwargs=dict(acronym=group.acronym)) r = self.client.get(url) self.assertEqual(r.status_code, 302) self.client.login(username='plain',password='plain+password') r = self.client.get(url) self.assertEqual(r.status_code, 404) self.client.logout() group = ReviewTeamFactory(settings__reviewer_queue_policy_id='RotateAlphabetically') secr = RoleFactory(name_id='secr',group=group).person reviewers = RoleFactory.create_batch(10, name_id='reviewer',group=group) NextReviewerInTeam.objects.create(team = group, next_reviewer=reviewers[4].person) target_index = 6 url = urlreverse('ietf.group.views.reset_next_reviewer', kwargs=dict(acronym=group.acronym)) for user in (secr.user.username, 'secretary'): login_testing_unauthorized(self,user,url) r = self.client.get(url) self.assertEqual(r.status_code,200) r = self.client.post(url,{'next_reviewer':reviewers[target_index].person.pk}) self.assertEqual(r.status_code,302) self.assertEqual(NextReviewerInTeam.objects.get(team=group).next_reviewer, reviewers[target_index].person) self.client.logout() target_index += 2