Let review teams opt in to poking a responsible AD when unhappy reviews are submitted. Fixes #2544. Commit ready for merge.

- Legacy-Id: 15675
This commit is contained in:
Robert Sparks 2018-11-02 20:18:06 +00:00
parent 542a85d949
commit c7bf147b1d
10 changed files with 10689 additions and 10526 deletions

View file

@ -573,7 +573,7 @@ class ReviewTests(TestCase):
test_file.name = "unnamed"
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "upload",
@ -627,7 +627,7 @@ class ReviewTests(TestCase):
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
@ -649,6 +649,30 @@ class ReviewTests(TestCase):
self.assertTrue(settings.MAILING_LIST_ARCHIVE_URL in review_req.review.external_url)
def test_complete_notify_ad(self):
review_req, url = self.setup_complete_review_test()
review_req.team.reviewteamsettings.notify_ad_when.add(ReviewResultName.objects.get(slug='issues'))
# TODO - it's a little surprising that the factories so far didn't give this doc an ad
review_req.doc.ad = PersonFactory()
review_req.doc.save_with_history([DocEvent.objects.create(doc=review_req.doc, rev=review_req.doc.rev, by=review_req.reviewer.person, type='changed_document',desc='added an AD')])
login_testing_unauthorized(self, review_req.reviewer.person.user.username, url)
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=review_req.team, slug="issues").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
"review_content": "This is a review\nwith two lines",
"review_url": "",
"review_file": "",
})
self.assertEqual(r.status_code, 302)
self.assertEqual(len(outbox), 2)
self.assertIn('Has Issues', outbox[-1]['Subject'])
@patch('requests.get')
def test_complete_review_link_to_mailing_list(self, mock):
# Mock up the url response for the request.get() call to retrieve the mailing list url
@ -665,7 +689,7 @@ class ReviewTests(TestCase):
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "link",
@ -693,7 +717,7 @@ class ReviewTests(TestCase):
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="part-completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
@ -734,7 +758,7 @@ class ReviewTests(TestCase):
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": review_req.doc.name, "request_id": review_req.pk })
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
@ -766,7 +790,7 @@ class ReviewTests(TestCase):
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
@ -791,7 +815,7 @@ class ReviewTests(TestCase):
# revise again
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="part-completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",

View file

@ -12,6 +12,7 @@ import debug # pyflakes:ignore
from django.http import HttpResponseForbidden, JsonResponse
from django.shortcuts import render, get_object_or_404, redirect
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.utils.html import mark_safe
from django.core.exceptions import ValidationError
@ -25,6 +26,7 @@ from ietf.review.models import ReviewRequest
from ietf.group.models import Group
from ietf.ietfauth.utils import is_authorized_in_doc_stream, user_is_person, has_role
from ietf.message.models import Message
from ietf.message.utils import infer_message
from ietf.person.fields import PersonEmailChoiceField, SearchablePersonField
from ietf.review.utils import (active_review_teams, assign_review_request_to_reviewer,
can_request_review_of_doc, can_manage_review_requests_for_team,
@ -423,7 +425,7 @@ class CompleteReviewForm(forms.Form):
" ".join("<a class=\"rev label label-default {0}\" title=\"{2:%Y-%m-%d}\">{1}</a>".format('', *r)
for i, r in enumerate(known_revisions)))
self.fields["result"].queryset = self.fields["result"].queryset.filter(reviewteamsettings__group=review_req.team)
self.fields["result"].queryset = self.fields["result"].queryset.filter(reviewteamsettings_review_results_set__group=review_req.team)
def format_submission_choice(label):
if revising_review:
@ -643,6 +645,19 @@ def complete_review(request, name, request_id):
review.external_url = mailarch.construct_message_url(list_name, email.utils.unquote(msg["Message-ID"]))
review.save_with_history([close_event])
if review_req.result in review_req.team.reviewteamsettings.notify_ad_when.all():
(to, cc) = gather_address_lists('review_notify_ad',review_req = review_req)
msg_txt = render_to_string("review/notify_ad.txt", {
"to": to,
"cc": cc,
"review_req": review_req,
"settings": settings,
})
msg = infer_message(msg_txt)
msg.by = request.user.person
msg.save()
send_mail_message(request, msg)
return redirect("ietf.doc.views_doc.document_main", name=review_req.review.name)
else:
initial={

View file

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-11-02 11:34
from __future__ import unicode_literals
from django.db import migrations
def forward(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger','MailTrigger')
Recipient = apps.get_model('mailtrigger', 'Recipient')
Recipient.objects.create(
slug = 'review_doc_ad',
desc = "The reviewed document's responsible area director",
template = '{% if review_req.doc.ad %}{{review_req.doc.ad.email_address}}{% endif %}'
)
review_notify_ad = MailTrigger.objects.create(
slug = 'review_notify_ad',
desc = 'Recipients when a team notifies area directors when a review with one of a certain set of results (typically results indicating problem) is submitted',
)
review_notify_ad.to.set(Recipient.objects.filter(slug='review_doc_ad'))
def reverse(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger','MailTrigger')
Recipient = apps.get_model('mailtrigger', 'Recipient')
MailTrigger.objects.filter(slug='review_notify_ad').delete()
Recipient.objects.filter(slug='review_doc_ad').delete()
class Migration(migrations.Migration):
dependencies = [
('mailtrigger', '0002_conflrev_changes'),
('review', '0003_add_notify_ad_when'),
]
operations = [
migrations.RunPython(forward, reverse)
]

File diff suppressed because it is too large Load diff

View file

@ -63,6 +63,6 @@ class ReviewTeamSettingsAdmin(admin.ModelAdmin):
list_display = ["group", ]
search_fields = ["group__acronym", ]
raw_id_fields = ["group", ]
filter_horizontal = ["review_types", "review_results", ]
filter_horizontal = ["review_types", "review_results", "notify_ad_when"]
admin.site.register(ReviewTeamSettings, ReviewTeamSettingsAdmin)

View file

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-11-02 10:10
from __future__ import unicode_literals
from django.db import migrations, models
import ietf.review.models
class Migration(migrations.Migration):
dependencies = [
('name', '0004_add_prefix_to_doctypenames'),
('review', '0002_unavailableperiod_reason'),
]
operations = [
migrations.AddField(
model_name='reviewteamsettings',
name='notify_ad_when',
field=models.ManyToManyField(related_name='reviewteamsettings_notify_ad_set', to='name.ReviewResultName'),
),
migrations.AlterField(
model_name='reviewteamsettings',
name='review_results',
field=models.ManyToManyField(default=ietf.review.models.get_default_review_results, related_name='reviewteamsettings_review_results_set', to='name.ReviewResultName'),
),
]

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-11-02 10:20
from __future__ import unicode_literals
from django.db import migrations
def forward(apps, schema_editor):
ReviewTeamSettings = apps.get_model('review','ReviewTeamSettings')
ReviewTeamSettings.objects.get(group__acronym='secdir').notify_ad_when.set(['serious-issues', 'issues', 'not-ready'])
def reverse(apps, schema_editor):
ReviewTeamSettings = apps.get_model('review','ReviewTeamSettings')
ReviewTeamSettings.objects.get(group__acronym='secdir').notify_ad_when.set([])
class Migration(migrations.Migration):
dependencies = [
('review', '0003_add_notify_ad_when'),
]
operations = [
migrations.RunPython(forward, reverse)
]

View file

@ -160,7 +160,8 @@ class ReviewTeamSettings(models.Model):
group = OneToOneField(Group)
autosuggest = models.BooleanField(default=True, verbose_name="Automatically suggest possible review requests")
review_types = models.ManyToManyField(ReviewTypeName, default=get_default_review_types)
review_results = models.ManyToManyField(ReviewResultName, default=get_default_review_results)
review_results = models.ManyToManyField(ReviewResultName, default=get_default_review_results, related_name='reviewteamsettings_review_results_set')
notify_ad_when = models.ManyToManyField(ReviewResultName, related_name='reviewteamsettings_notify_ad_set')
def __unicode__(self):
return u"%s" % (self.group.acronym,)

View file

@ -157,6 +157,7 @@ class ReviewTeamSettingsResource(ModelResource):
group = ToOneField(GroupResource, 'group')
review_types = ToManyField(ReviewTypeNameResource, 'review_types', null=True)
review_results = ToManyField(ReviewResultNameResource, 'review_results', null=True)
notify_ad_when = ToManyField(ReviewResultNameResource, 'notify_ad_when', null = True)
class Meta:
queryset = ReviewTeamSettings.objects.all()
serializer = api.Serializer()
@ -168,6 +169,7 @@ class ReviewTeamSettingsResource(ModelResource):
"group": ALL_WITH_RELATIONS,
"review_types": ALL_WITH_RELATIONS,
"review_results": ALL_WITH_RELATIONS,
"notify_ad_when": ALL_WITH_RELATIONS,
}
api.review.register(ReviewTeamSettingsResource())

View file

@ -0,0 +1,12 @@
{% load ietf_filters %}{% autoescape off %}From: {{settings.DEFAULT_FROM_EMAIL}}
To: {{to}}{% if cc %}
Cc: {{cc}}{% endif %}
Subject: "{{review_req.result}}" review submitted for {{review_req.doc}}{% if review_req.reviewed_rev %}-{{review_req.reviewed_rev}}{% endif %}
{{review_req.reviewer.person.name}} has submitted a "{{review_req.result}}" review result for {{review_req.doc}}{% if review_req.reviewed_rev %}-{{review_req.reviewed_rev}}{% endif %}.
The review is available at {{settings.IDTRACKER_BASE_URL}}{% url 'ietf.doc.views_doc.document_main' name=review_req.review.name %}
The document is available at {{settings.IDTRACKER_BASE_URL}}{% url 'ietf.doc.views_doc.document_main' name=review_req.doc.name %}
{% endautoescape %}