Checkpoint. Lots of tests will still fail. Reordered some migrations. Brought about half of the ietf.doc.views_review views into line with the new models. Next step is the other half.

- Legacy-Id: 16012
This commit is contained in:
Robert Sparks 2019-03-07 23:08:14 +00:00
parent 82025f9dca
commit b85052fe63
17 changed files with 11461 additions and 11248 deletions

View file

@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-05 11:39
from __future__ import unicode_literals
from django.db import migrations
def forward(apps, schema_editor):
DBTemplate = apps.get_model('dbtemplate', 'DBTemplate')
DBTemplate.objects.filter(id=186).update(content="""I am the assigned Gen-ART reviewer for this draft. The General Area
Review Team (Gen-ART) reviews all IETF documents being processed
by the IESG for the IETF Chair. Please treat these comments just
like any other last call comments.
For more information, please see the FAQ at
<https://trac.ietf.org/trac/gen/wiki/GenArtfaq>.
Document: {{ assignment.review_request.doc.name }}-??
Reviewer: {{ assignment.reviewer.person.plain_name }}
Review Date: {{ today }}
IETF LC End Date: {% if assignment.review_request.doc.most_recent_ietflc %}{{ assignment.review_request.doc.most_recent_ietflc.expires|date:"Y-m-d" }}{% else %}None{% endif %}
IESG Telechat date: {{ assignment.review_request.doc.telechat_date|default:'Not scheduled for a telechat' }}
Summary:
Major issues:
Minor issues:
Nits/editorial comments:
""")
DBTemplate.objects.filter(id=187).update(content="""I am the assigned Gen-ART reviewer for this draft. The General Area
Review Team (Gen-ART) reviews all IETF documents being processed
by the IESG for the IETF Chair. Please wait for direction from your
document shepherd or AD before posting a new version of the draft.
For more information, please see the FAQ at
<https://trac.ietf.org/trac/gen/wiki/GenArtfaq>.
Document: {{ assignment.review_request.doc.name }}-??
Reviewer: {{ assignment.reviewer.person.plain_name }}
Review Date: {{ today }}
IETF LC End Date: {% if assignment.review_req.doc.most_recent_ietflc %}{{ assignment.review_request.doc.most_recent_ietflc.expires|date:"Y-m-d" }}{% else %}None{% endif %}
IESG Telechat date: {{ assignment.review_request.doc.telechat_date|default:'Not scheduled for a telechat' }}
Summary:
Major issues:
Minor issues:
Nits/editorial comments:
""")
def reverse(apps, schema_editor):
DBTemplate = apps.get_model('dbtemplate','DBTemplate')
DBTemplate.objects.filter(id=186).update(content="""I am the assigned Gen-ART reviewer for this draft. The General Area
Review Team (Gen-ART) reviews all IETF documents being processed
by the IESG for the IETF Chair. Please treat these comments just
like any other last call comments.
For more information, please see the FAQ at
<https://trac.ietf.org/trac/gen/wiki/GenArtfaq>.
Document: {{ review_req.doc.name }}-??
Reviewer: {{ review_req.reviewer.person.plain_name }}
Review Date: {{ today }}
IETF LC End Date: {% if review_req.doc.most_recent_ietflc %}{{ review_req.doc.most_recent_ietflc.expires|date:"Y-m-d" }}{% else %}None{% endif %}
IESG Telechat date: {{ review_req.doc.telechat_date|default:'Not scheduled for a telechat' }}
Summary:
Major issues:
Minor issues:
Nits/editorial comments:
""")
DBTemplate.objects.filter(id=187).update(content="""I am the assigned Gen-ART reviewer for this draft. The General Area
Review Team (Gen-ART) reviews all IETF documents being processed
by the IESG for the IETF Chair. Please wait for direction from your
document shepherd or AD before posting a new version of the draft.
For more information, please see the FAQ at
<https://trac.ietf.org/trac/gen/wiki/GenArtfaq>.
Document: {{ review_req.doc.name }}-??
Reviewer: {{ review_req.reviewer.person.plain_name }}
Review Date: {{ today }}
IETF LC End Date: {% if review_req.doc.most_recent_ietflc %}{{ review_req.doc.most_recent_ietflc.expires|date:"Y-m-d" }}{% else %}None{% endif %}
IESG Telechat date: {{ review_req.doc.telechat_date|default:'Not scheduled for a telechat' }}
Summary:
Major issues:
Minor issues:
Nits/editorial comments:
""")
class Migration(migrations.Migration):
dependencies = [
('dbtemplate', '0002_auto_20180220_1052'),
('doc','0011_reviewassignmentdocevent'),
]
operations = [
migrations.RunPython(forward, reverse)
]

View file

@ -11,7 +11,7 @@ class Migration(migrations.Migration):
dependencies = [
('review', '0009_refactor_review_request'),
('name', '0006_adjust_statenames'),
('name', '0005_reviewassignmentstatename'),
('doc', '0010_auto_20190225_1302'),
]

View file

@ -18,13 +18,13 @@ import debug # pyflakes:ignore
import ietf.review.mailarch
from ietf.doc.factories import NewRevisionDocEventFactory, WgDraftFactory, WgRfcFactory
from ietf.doc.models import DocumentAuthor, RelatedDocument, DocEvent, ReviewRequestDocEvent
from ietf.doc.models import DocumentAuthor, RelatedDocument, DocEvent, ReviewRequestDocEvent, ReviewAssignmentDocEvent
from ietf.group.factories import RoleFactory, ReviewTeamFactory
from ietf.group.models import Group
from ietf.message.models import Message
from ietf.name.models import ReviewResultName, ReviewRequestStateName, ReviewTypeName
from ietf.name.models import ReviewResultName, ReviewRequestStateName, ReviewAssignmentStateName, ReviewTypeName
from ietf.person.models import Email, Person
from ietf.review.factories import ReviewRequestFactory
from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory
from ietf.review.models import (ReviewRequest, ReviewerSettings,
ReviewWish, UnavailablePeriod, NextReviewerInTeam)
from ietf.review.utils import reviewer_rotation_list, possibly_advance_next_reviewer_for_team
@ -60,7 +60,8 @@ class ReviewTests(TestCase):
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
RoleFactory(group=review_team3,person__user__username='reviewsecretary3',person__user__email='reviewsecretary3@example.com',name_id='secr')
ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
ReviewAssignmentFactory(review_request = req, reviewer = rev_role.person.email_set.first(), state_id='accepted')
url = urlreverse('ietf.doc.views_review.request_review', kwargs={ "name": doc.name })
login_testing_unauthorized(self, "ad", url)
@ -135,7 +136,8 @@ class ReviewTests(TestCase):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request=review_req, reviewer=rev_role.person.email_set.first(), state_id='accepted')
# move the review request to a doubly-replaced document to
# check we can fish it out
@ -156,7 +158,8 @@ class ReviewTests(TestCase):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request = review_req, reviewer = rev_role.person.email_set.first(), state_id='accepted')
url = urlreverse('ietf.doc.views_review.review_request', kwargs={ "name": doc.name, "request_id": review_req.pk })
@ -436,13 +439,14 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request = review_req, reviewer=rev_role.person.email_set.first(), state_id='accepted')
reject_url = urlreverse('ietf.doc.views_review.reject_reviewer_assignment', kwargs={ "name": doc.name, "request_id": review_req.pk })
reject_url = urlreverse('ietf.doc.views_review.reject_reviewer_assignment', kwargs={ "name": doc.name, "assignment_id": assignment.pk })
# follow link
req_url = urlreverse('ietf.doc.views_review.review_request', kwargs={ "name": doc.name, "request_id": review_req.pk })
req_url = urlreverse('ietf.doc.views_review.review_request', kwargs={ "name": doc.name, "request_id": assignment.review_request.pk })
self.client.login(username="reviewsecretary", password="reviewsecretary+password")
r = self.client.get(req_url)
self.assertEqual(r.status_code, 200)
@ -453,19 +457,18 @@ class ReviewTests(TestCase):
login_testing_unauthorized(self, "reviewsecretary", reject_url)
r = self.client.get(reject_url)
self.assertEqual(r.status_code, 200)
self.assertTrue(unicode(review_req.reviewer.person) in unicontent(r))
self.assertTrue(unicode(assignment.reviewer.person) in unicontent(r))
# reject
empty_outbox()
r = self.client.post(reject_url, { "action": "reject", "message_to_secretary": "Test message" })
self.assertEqual(r.status_code, 302)
review_req = reload_db_objects(review_req)
self.assertEqual(review_req.state_id, "rejected")
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.state_id, "rejected")
e = doc.latest_event()
self.assertEqual(e.type, "closed_review_request")
self.assertTrue("rejected" in e.desc)
self.assertEqual(ReviewRequest.objects.filter(doc=review_req.doc, team=review_req.team, state="requested").count(), 1)
self.assertEqual(len(outbox), 1)
self.assertTrue("Test message" in outbox[0].get_payload(decode=True).decode("utf-8"))
@ -509,7 +512,8 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request=review_req, reviewer=rev_role.person.email_set.first(), state_id='accepted')
# test URL construction
query_urls = ietf.review.mailarch.construct_query_urls(review_req)
@ -525,7 +529,7 @@ class ReviewTests(TestCase):
real_fn = ietf.review.mailarch.construct_query_urls
ietf.review.mailarch.construct_query_urls = lambda review_req, query=None: { "query_data_url": "file://" + os.path.abspath(mbox_path) }
url = urlreverse('ietf.doc.views_review.search_mail_archive', kwargs={ "name": doc.name, "request_id": review_req.pk })
url = urlreverse('ietf.doc.views_review.search_mail_archive', kwargs={ "name": doc.name, "assignment_id": assignment.pk })
login_testing_unauthorized(self, "reviewsecretary", url)
r = self.client.get(url)
@ -555,7 +559,7 @@ class ReviewTests(TestCase):
f.write('Content-Type: text/html\n\n<html><body><div class="xtr"><div class="xtd no-results">No results found</div></div>')
ietf.review.mailarch.construct_query_urls = lambda review_req, query=None: { "query_data_url": "file://" + os.path.abspath(no_result_path) }
url = urlreverse('ietf.doc.views_review.search_mail_archive', kwargs={ "name": doc.name, "request_id": review_req.pk })
url = urlreverse('ietf.doc.views_review.search_mail_archive', kwargs={ "name": doc.name, "assignment_id": assignment.pk })
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
@ -572,18 +576,19 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request=review_req, state_id='accepted', reviewer=rev_role.person.email_set.first())
for r in ReviewResultName.objects.filter(slug__in=("issues", "ready")):
review_req.team.reviewteamsettings.review_results.add(r)
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": doc.name, "request_id": review_req.pk })
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": doc.name, "assignment_id": review_req.pk })
return review_req, url
return assignment, url
def test_complete_review_upload_content(self):
review_req, url = self.setup_complete_review_test()
assignment, url = self.setup_complete_review_test()
login_testing_unauthorized(self, review_req.reviewer.person.user.username, url)
login_testing_unauthorized(self, assignment.reviewer.person.user.username, url)
# get
r = self.client.get(url)
@ -611,9 +616,9 @@ class ReviewTests(TestCase):
test_file.name = "unnamed"
r = self.client.post(url, data={
"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,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="ready").pk,
"state": ReviewAssignmentStateName.objects.get(slug="completed").pk,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "upload",
"review_content": "",
"review_url": "",
@ -621,25 +626,25 @@ class ReviewTests(TestCase):
})
self.assertEqual(r.status_code, 302)
review_req = reload_db_objects(review_req)
self.assertEqual(review_req.state_id, "completed")
self.assertEqual(review_req.result_id, "ready")
self.assertEqual(review_req.reviewed_rev, review_req.doc.rev)
self.assertTrue(review_req.team.acronym.lower() in review_req.review.name)
self.assertTrue(review_req.doc.rev in review_req.review.name)
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.state_id, "completed")
self.assertEqual(assignment.result_id, "ready")
self.assertEqual(assignment.reviewed_rev, assignment.review_request.doc.rev)
self.assertTrue(assignment.review_request.team.acronym.lower() in assignment.review.name)
self.assertTrue(assignment.review_request.doc.rev in assignment.review.name)
with open(os.path.join(self.review_subdir, review_req.review.name + ".txt")) as f:
with open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
self.assertEqual(f.read(), "This is a review\nwith two lines")
self.assertEqual(len(outbox), 1)
self.assertTrue(review_req.team.list_email in outbox[0]["To"])
self.assertTrue(review_req.reviewer.role_set.filter(group=review_req.team,name='reviewer').first().email.address in outbox[0]["From"])
self.assertTrue(assignment.review_request.team.list_email in outbox[0]["To"])
self.assertTrue(assignment.reviewer.role_set.filter(group=assignment.review_request.team,name='reviewer').first().email.address in outbox[0]["From"])
self.assertTrue("This is a review" in outbox[0].get_payload(decode=True).decode("utf-8"))
self.assertTrue(settings.MAILING_LIST_ARCHIVE_URL in review_req.review.external_url)
self.assertTrue(settings.MAILING_LIST_ARCHIVE_URL in assignment.review.external_url)
# Check that the review has the reviewer as author
self.assertEqual(review_req.reviewer, review_req.review.documentauthor_set.first().email)
self.assertEqual(assignment.reviewer, assignment.review.documentauthor_set.first().email)
# Check that we have a copy of the outgoing message
msgid = outbox[0]["Message-ID"]
@ -649,25 +654,25 @@ class ReviewTests(TestCase):
self.assertEqual(outbox[0].get_payload(decode=True).decode(str(outbox[0].get_charset())), message.body)
# check the review document page
url = urlreverse('ietf.doc.views_doc.document_main', kwargs={ "name": review_req.review.name })
url = urlreverse('ietf.doc.views_doc.document_main', kwargs={ "name": assignment.review.name })
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
content = unicontent(r)
self.assertTrue("{} Review".format(review_req.type.name) in content)
self.assertTrue("{} Review".format(assignment.review_request.type.name) in content)
self.assertTrue("This is a review" in content)
def test_complete_review_enter_content(self):
review_req, url = self.setup_complete_review_test()
assignment, url = self.setup_complete_review_test()
login_testing_unauthorized(self, review_req.reviewer.person.user.username, url)
login_testing_unauthorized(self, assignment.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="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="ready").pk,
"state": ReviewAssignmentStateName.objects.get(slug="completed").pk,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "enter",
"review_content": "This is a review\nwith two lines",
"review_url": "",
@ -675,32 +680,32 @@ class ReviewTests(TestCase):
})
self.assertEqual(r.status_code, 302)
review_req = reload_db_objects(review_req)
self.assertEqual(review_req.state_id, "completed")
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.state_id, "completed")
with open(os.path.join(self.review_subdir, review_req.review.name + ".txt")) as f:
with open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
self.assertEqual(f.read(), "This is a review\nwith two lines")
self.assertEqual(len(outbox), 1)
self.assertTrue(review_req.team.list_email in outbox[0]["To"])
self.assertTrue(assignment.review_request.team.list_email in outbox[0]["To"])
self.assertTrue("This is a review" in outbox[0].get_payload(decode=True).decode("utf-8"))
self.assertTrue(settings.MAILING_LIST_ARCHIVE_URL in review_req.review.external_url)
self.assertTrue(settings.MAILING_LIST_ARCHIVE_URL in assignment.review.external_url)
def test_complete_notify_ad_because_team_settings(self):
review_req, url = self.setup_complete_review_test()
review_req.team.reviewteamsettings.notify_ad_when.add(ReviewResultName.objects.get(slug='issues'))
assignment, url = self.setup_complete_review_test()
assignment.review_request.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)
assignment.review_request.doc.ad = PersonFactory()
assignment.review_request.doc.save_with_history([DocEvent.objects.create(doc=assignment.review_request.doc, rev=assignment.review_request.doc.rev, by=assignment.reviewer.person, type='changed_document',desc='added an AD')])
login_testing_unauthorized(self, assignment.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,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="issues").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "enter",
"review_content": "This is a review\nwith two lines",
"review_url": "",
@ -713,17 +718,17 @@ class ReviewTests(TestCase):
self.assertIn('settings indicated', outbox[-1].get_payload(decode=True).decode("utf-8"))
def test_complete_notify_ad_because_checkbox(self):
review_req, url = self.setup_complete_review_test()
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)
assignment, url = self.setup_complete_review_test()
assignment.review_request.doc.ad = PersonFactory()
assignment.review_request.doc.save_with_history([DocEvent.objects.create(doc=assignment.review_request.doc, rev=assignment.review_request.doc.rev, by=assignment.reviewer.person, type='changed_document',desc='added an AD')])
login_testing_unauthorized(self, assignment.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,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="issues").pk,
"state": ReviewAssignmentStateName.objects.get(slug="completed").pk,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "enter",
"review_content": "This is a review\nwith two lines",
"review_url": "",
@ -745,16 +750,16 @@ class ReviewTests(TestCase):
mock.return_value = response
# Run the test
review_req, url = self.setup_complete_review_test()
assignment, url = self.setup_complete_review_test()
login_testing_unauthorized(self, review_req.reviewer.person.user.username, url)
login_testing_unauthorized(self, assignment.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="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="ready").pk,
"state": ReviewAssignmentStateName.objects.get(slug="completed").pk,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "link",
"review_content": response.content,
"review_url": "http://example.com/testreview/",
@ -762,27 +767,27 @@ class ReviewTests(TestCase):
})
self.assertEqual(r.status_code, 302)
review_req = reload_db_objects(review_req)
self.assertEqual(review_req.state_id, "completed")
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.state_id, "completed")
with open(os.path.join(self.review_subdir, review_req.review.name + ".txt")) as f:
with open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
self.assertEqual(f.read(), "This is a review\nwith two lines")
self.assertEqual(len(outbox), 0)
self.assertTrue("http://example.com" in review_req.review.external_url)
self.assertTrue("http://example.com" in assignment.review.external_url)
def test_partially_complete_review(self):
review_req, url = self.setup_complete_review_test()
assignment, url = self.setup_complete_review_test()
login_testing_unauthorized(self, review_req.reviewer.person.user.username, url)
login_testing_unauthorized(self, assignment.reviewer.person.user.username, url)
# partially complete
empty_outbox()
r = self.client.post(url, data={
"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,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="ready").pk,
"state": ReviewAssignmentStateName.objects.get(slug="part-completed").pk,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "enter",
"review_content": "This is a review with a somewhat long line spanning over 80 characters to test word wrapping\nand another line",
})
@ -790,16 +795,15 @@ class ReviewTests(TestCase):
review_req = reload_db_objects(review_req)
self.assertEqual(review_req.state_id, "part-completed")
self.assertTrue(review_req.doc.rev in review_req.review.name)
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.state_id, "part-completed")
self.assertTrue(assignment.review_request.doc.rev in assignment.review.name)
self.assertEqual(len(outbox), 2)
self.assertTrue("reviewsecretary@example.com" in outbox[0]["To"])
self.assertTrue("partially" in outbox[0]["Subject"].lower())
self.assertTrue("new review request" in outbox[0].get_payload(decode=True).decode("utf-8"))
self.assertTrue(review_req.team.list_email in outbox[1]["To"])
self.assertTrue(assignment.review_request.team.list_email in outbox[1]["To"])
self.assertTrue("partial review" in outbox[1]["Subject"].lower())
body = outbox[1].get_payload(decode=True).decode("utf-8")
self.assertTrue("This is a review" in body)
@ -809,30 +813,27 @@ class ReviewTests(TestCase):
self.assertTrue(not any( len(line) > 100 for line in body.splitlines() ))
self.assertTrue(any( len(line) > 80 for line in body.splitlines() ))
first_review = review_req.review
first_reviewer = review_req.reviewer
first_review = assignment.review
first_reviewer = assignment.reviewer
# complete
review_req = ReviewRequest.objects.get(state="requested", doc=review_req.doc, team=review_req.team)
self.assertEqual(review_req.reviewer, None)
review_req.reviewer = first_reviewer # same reviewer, so we can test uniquification
review_req.save()
assignment = assignment.review_request.reviewassignment_set.create(state_id="requested", reviewer=assignment.reviewer)
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": review_req.doc.name, "request_id": review_req.pk })
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": assignment.review_request.doc.name, "assignment_id": assignment.pk })
r = self.client.post(url, data={
"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,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="ready").pk,
"state": ReviewAssignmentStateName.objects.get(slug="completed").pk,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "enter",
"review_content": "This is another review with a really, really, really, really, really, really, really, really, really, really long line.",
})
self.assertEqual(r.status_code, 302)
review_req = reload_db_objects(review_req)
self.assertEqual(review_req.state_id, "completed")
self.assertTrue(review_req.doc.rev in review_req.review.name)
second_review = review_req.review
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.state_id, "completed")
self.assertTrue(assignment.review_request.doc.rev in assignment.review.name)
second_review = assignment.review
self.assertTrue(first_review.name != second_review.name)
self.assertTrue(second_review.name.endswith("-2")) # uniquified
@ -844,18 +845,18 @@ class ReviewTests(TestCase):
def test_revise_review_enter_content(self):
review_req, url = self.setup_complete_review_test()
review_req.state = ReviewRequestStateName.objects.get(slug="no-response")
review_req.save()
assignment, url = self.setup_complete_review_test()
assignment.state = ReviewAssignmentStateName.objects.get(slug="no-response")
assignment.save()
login_testing_unauthorized(self, review_req.reviewer.person.user.username, url)
login_testing_unauthorized(self, assignment.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="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="ready").pk,
"state": ReviewAssignmentStateName.objects.get(slug="completed").pk,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "enter",
"review_content": "This is a review\nwith two lines",
"review_url": "",
@ -865,12 +866,12 @@ class ReviewTests(TestCase):
})
self.assertEqual(r.status_code, 302)
review_req = reload_db_objects(review_req)
self.assertEqual(review_req.state_id, "completed")
event = ReviewRequestDocEvent.objects.get(type="closed_review_request", review_request=review_req)
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.state_id, "completed")
event = ReviewAssignmentDocEvent.objects.get(type="closed_review_request", review_assignment=assignment)
self.assertEqual(event.time, datetime.datetime(2012, 12, 24, 12, 13, 14))
with open(os.path.join(self.review_subdir, review_req.review.name + ".txt")) as f:
with open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
self.assertEqual(f.read(), "This is a review\nwith two lines")
self.assertEqual(len(outbox), 0)
@ -878,9 +879,9 @@ class ReviewTests(TestCase):
# revise again
empty_outbox()
r = self.client.post(url, data={
"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,
"result": ReviewResultName.objects.get(reviewteamsettings_review_results_set__group=assignment.review_request.team, slug="ready").pk,
"state": ReviewAssignmentStateName.objects.get(slug="part-completed").pk,
"reviewed_rev": assignment.review_request.doc.rev,
"review_submission": "enter",
"review_content": "This is a revised review",
"review_url": "",
@ -890,9 +891,9 @@ class ReviewTests(TestCase):
})
self.assertEqual(r.status_code, 302)
review_req = reload_db_objects(review_req)
self.assertEqual(review_req.review.rev, "01")
event = ReviewRequestDocEvent.objects.get(type="closed_review_request", review_request=review_req)
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.review.rev, "01")
event = ReviewAssignmentDocEvent.objects.get(type="closed_review_request", review_assignment=assignment)
self.assertEqual(event.time, datetime.datetime(2013, 12, 24, 11, 11, 11))
self.assertEqual(len(outbox), 0)
@ -902,7 +903,8 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request = review_req, reviewer = rev_role.person.email_set.first(), state_id='accepted')
url = urlreverse('ietf.doc.views_review.edit_comment', kwargs={ "name": doc.name, "request_id": review_req.pk })
@ -923,7 +925,8 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,reviewer=rev_role.person.email_set.first(),deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request = review_req, reviewer = rev_role.person.email_set.first(), state_id='accepted')
url = urlreverse('ietf.doc.views_review.edit_deadline', kwargs={ "name": doc.name, "request_id": review_req.pk })

View file

@ -7,9 +7,9 @@ urlpatterns = [
url(r'^(?P<request_id>[0-9]+)/login/$', views_review.review_request_forced_login),
url(r'^(?P<request_id>[0-9]+)/close/$', views_review.close_request),
url(r'^(?P<request_id>[0-9]+)/assignreviewer/$', views_review.assign_reviewer),
url(r'^(?P<request_id>[0-9]+)/rejectreviewerassignment/$', views_review.reject_reviewer_assignment),
url(r'^(?P<request_id>[0-9]+)/complete/$', views_review.complete_review),
url(r'^(?P<request_id>[0-9]+)/searchmailarchive/$', views_review.search_mail_archive),
url(r'^(?P<assignment_id>[0-9]+)/rejectreviewerassignment/$', views_review.reject_reviewer_assignment),
url(r'^(?P<assignment_id>[0-9]+)/complete/$', views_review.complete_review),
url(r'^(?P<assignment_id>[0-9]+)/searchmailarchive/$', views_review.search_mail_archive),
url(r'^(?P<request_id>[0-9]+)/editcomment/$', views_review.edit_comment),
url(r'^(?P<request_id>[0-9]+)/editdeadline/$', views_review.edit_deadline),
]

View file

@ -598,7 +598,8 @@ def document_main(request, name, rev=None):
# If we want to go back to using markup_txt.markup_unicode, call it explicitly here like this:
# content = markup_txt.markup_unicode(content, split=False, width=80)
review_req = ReviewRequest.objects.filter(review=doc.name).first()
# TODO - verify if this shows other reviews from the same team - I bet it doesn't
review_req = ReviewRequest.objects.filter(reviewassignment__review=doc.name).first()
other_reviews = []
if review_req:

View file

@ -20,7 +20,7 @@ from django.template.loader import render_to_string, TemplateDoesNotExist
from django.urls import reverse as urlreverse
from ietf.doc.models import (Document, NewRevisionDocEvent, State, DocAlias,
LastCallDocEvent, ReviewRequestDocEvent, DocumentAuthor)
LastCallDocEvent, ReviewRequestDocEvent, ReviewAssignmentDocEvent, DocumentAuthor)
from ietf.name.models import ReviewRequestStateName, ReviewAssignmentStateName, ReviewResultName, DocTypeName
from ietf.review.models import ReviewRequest, ReviewAssignment
from ietf.group.models import Group
@ -184,7 +184,6 @@ def review_request(request, name, request_id):
doc = get_object_or_404(Document, name=name)
review_req = get_object_or_404(ReviewRequest, pk=request_id)
is_reviewer = review_req.reviewer and user_is_person(request.user, review_req.reviewer.person)
can_manage_request = can_manage_review_requests_for_team(request.user, review_req.team)
can_close_request = (review_req.state_id in ["requested", "accepted"]
@ -194,37 +193,38 @@ def review_request(request, name, request_id):
can_assign_reviewer = (review_req.state_id in ["requested", "accepted"]
and can_manage_request)
can_accept_reviewer_assignment = (review_req.state_id == "requested"
and review_req.reviewer
and (is_reviewer or can_manage_request))
can_reject_reviewer_assignment = (review_req.state_id in ["requested", "accepted"]
and review_req.reviewer
and (is_reviewer or can_manage_request))
can_complete_review = (review_req.state_id in ["requested", "accepted", "overtaken", "no-response", "part-completed", "completed"]
and review_req.reviewer
and (is_reviewer or can_manage_request))
can_edit_comment = can_request_review_of_doc(request.user, doc)
can_edit_deadline = can_edit_comment
if request.method == "POST" and request.POST.get("action") == "accept" and can_accept_reviewer_assignment:
review_req.state = ReviewRequestStateName.objects.get(slug="accepted")
review_req.save()
assignments = review_req.reviewassignment_set.all()
for assignment in assignments:
assignment.is_reviewer = user_is_person(request.user, assignment.reviewer.person)
assignment.can_accept_reviewer_assignment = (assignment.state_id == "requested"
and (assignment.is_reviewer or can_manage_request))
assignment.can_reject_reviewer_assignment = (assignment.state_id in ["requested", "accepted"]
and (assignment.is_reviewer or can_manage_request))
assignment.can_complete_review = (assignment.state_id in ["requested", "accepted", "overtaken", "no-response", "part-completed", "completed"]
and (assignment.is_reviewer or can_manage_request))
if request.method == "POST" and request.POST.get("action") == "accept":
for assignment in assignments:
if assignment.can_accept_reviewer_assignment:
assignment.state = ReviewAssignmentStateName.objects.get(slug="accepted")
assignment.save()
return redirect(review_request, name=review_req.doc.name, request_id=review_req.pk)
return render(request, 'doc/review/review_request.html', {
'doc': doc,
'review_req': review_req,
'can_close_request': can_close_request,
'can_reject_reviewer_assignment': can_reject_reviewer_assignment,
'can_assign_reviewer': can_assign_reviewer,
'can_accept_reviewer_assignment': can_accept_reviewer_assignment,
'can_complete_review': can_complete_review,
'can_edit_comment': can_edit_comment,
'can_edit_deadline': can_edit_deadline,
'assignments': assignments,
})
@ -327,20 +327,19 @@ def reject_reviewer_assignment(request, name, assignment_id):
review_assignment.state = ReviewAssignmentStateName.objects.get(slug="rejected")
review_assignment.save()
# TODO: this needs to be reworked as a ReviewAssignmentDocEvent
#ReviewRequestDocEvent.objects.create(
# type="closed_review_request",
# doc=review_req.doc,
# rev=review_req.doc.rev,
# by=request.user.person,
# desc="Assignment of request for {} review by {} to {} was rejected".format(
# review_req.type.name,
# review_req.team.acronym.upper(),
# review_req.reviewer.person,
# ),
# review_request=review_req,
# state=review_req.state,
#)
ReviewAssignmentDocEvent.objects.create(
type="closed_review_request",
doc=review_assignment.review_request.doc,
rev=review_assignment.review_request.doc.rev,
by=request.user.person,
desc="Assignment of request for {} review by {} to {} was rejected".format(
review_assignment.review_request.type.name,
review_assignment.review_request.team.acronym.upper(),
review_assignment.reviewer.person,
),
review_assignment=review_assignment,
state=review_assignment.state,
)
msg = render_to_string("review/reviewer_assignment_rejected.txt", {
"by": request.user.person,
@ -360,7 +359,7 @@ def reject_reviewer_assignment(request, name, assignment_id):
})
class CompleteReviewForm(forms.Form):
state = forms.ModelChoiceField(queryset=ReviewRequestStateName.objects.filter(slug__in=("completed", "part-completed")).order_by("-order"), widget=forms.RadioSelect, initial="completed")
state = forms.ModelChoiceField(queryset=ReviewAssignmentStateName.objects.filter(slug__in=("completed", "part-completed")).order_by("-order"), widget=forms.RadioSelect, initial="completed")
reviewed_rev = forms.CharField(label="Reviewed revision", max_length=4)
result = forms.ModelChoiceField(queryset=ReviewResultName.objects.filter(used=True), widget=forms.RadioSelect, empty_label=None)
ACTIONS = [
@ -378,16 +377,16 @@ class CompleteReviewForm(forms.Form):
cc = MultiEmailField(required=False, help_text="Email addresses to send to in addition to the review team list")
email_ad = forms.BooleanField(label="Send extra email to the responsible AD suggesting early attention", required=False)
def __init__(self, review_req, is_reviewer, *args, **kwargs):
self.review_req = review_req
def __init__(self, assignment, is_reviewer, *args, **kwargs):
self.assignment = assignment
super(CompleteReviewForm, self).__init__(*args, **kwargs)
doc = self.review_req.doc
doc = self.assignment.review_request.doc
known_revisions = NewRevisionDocEvent.objects.filter(doc=doc).order_by("time", "id").values_list("rev", "time", flat=False)
revising_review = review_req.state_id not in ["requested", "accepted"]
revising_review = assignment.state_id not in ["requested", "accepted"]
if not revising_review:
self.fields["state"].choices = [
@ -399,7 +398,7 @@ class CompleteReviewForm(forms.Form):
reviewed_rev_class = []
for r in known_revisions:
last_version = r[0]
if r[1] < review_req.time:
if r[1] < assignment.review_request.time:
kwargs["initial"]["reviewed_rev"] = r[0]
reviewed_rev_class.append('reviewer-doc-past')
else:
@ -423,13 +422,13 @@ 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_review_results_set__group=review_req.team)
self.fields["result"].queryset = self.fields["result"].queryset.filter(reviewteamsettings_review_results_set__group=assignment.review_request.team)
def format_submission_choice(label):
if revising_review:
label = label.replace(" (automatically posts to {mailing_list})", "")
return label.format(mailing_list=review_req.team.list_email or "[error: team has no mailing list set]")
return label.format(mailing_list=assignment.review_request.team.list_email or "[error: team has no mailing list set]")
self.fields["review_submission"].choices = [ (k, format_submission_choice(label)) for k, label in self.fields["review_submission"].choices]
@ -440,7 +439,7 @@ class CompleteReviewForm(forms.Form):
del self.fields["completion_time"]
def clean_reviewed_rev(self):
return clean_doc_revision(self.review_req.doc, self.cleaned_data.get("reviewed_rev"))
return clean_doc_revision(self.assignment.review_request.doc, self.cleaned_data.get("reviewed_rev"))
def clean_review_content(self):
return self.cleaned_data["review_content"].replace("\r", "")
@ -458,7 +457,7 @@ class CompleteReviewForm(forms.Form):
return url
def clean(self):
if "@" in self.review_req.reviewer.person.ascii:
if "@" in self.assignment.reviewer.person.ascii:
raise forms.ValidationError("Reviewer name must be filled in (the ASCII version is currently \"{}\" - since it contains an @ sign the name is probably still the original email address).".format(self.review_req.reviewer.person.ascii))
def require_field(f):
@ -474,40 +473,37 @@ class CompleteReviewForm(forms.Form):
require_field("review_url")
@login_required
def complete_review(request, name, request_id):
def complete_review(request, name, assignment_id):
doc = get_object_or_404(Document, name=name)
review_req = get_object_or_404(ReviewRequest, pk=request_id)
assignment = get_object_or_404(ReviewAssignment, pk=assignment_id)
revising_review = review_req.state_id not in ["requested", "accepted"]
revising_review = assignment.state_id not in ["requested", "accepted"]
if not review_req.reviewer:
return redirect(review_request, name=review_req.doc.name, request_id=review_req.pk)
is_reviewer = user_is_person(request.user, review_req.reviewer.person)
can_manage_request = can_manage_review_requests_for_team(request.user, review_req.team)
is_reviewer = user_is_person(request.user, assignment.reviewer.person)
can_manage_request = can_manage_review_requests_for_team(request.user, assignment.review_request.team)
if not (is_reviewer or can_manage_request):
return HttpResponseForbidden("You do not have permission to perform this action")
(to, cc) = gather_address_lists('review_completed',review_req = review_req)
(to, cc) = gather_address_lists('review_completed', review_req = assignment.review_request)
if request.method == "POST":
form = CompleteReviewForm(review_req, is_reviewer,
form = CompleteReviewForm(assignment, is_reviewer,
request.POST, request.FILES)
if form.is_valid():
review_submission = form.cleaned_data['review_submission']
review = review_req.review
review = assignment.review
if not review:
# create review doc
for i in range(1, 100):
name_components = [
"review",
strip_prefix(review_req.doc.name, "draft-"),
strip_prefix(assignment.review_request.doc.name, "draft-"),
form.cleaned_data["reviewed_rev"],
review_req.team.acronym,
review_req.type.slug,
xslugify(review_req.reviewer.person.ascii_parts()[3]),
assignment.review_request.team.acronym,
assignment.review_request.type.slug,
xslugify(assignment.reviewer.person.ascii_parts()[3]),
datetime.date.today().isoformat(),
]
if i > 1:
@ -520,10 +516,10 @@ def complete_review(request, name, request_id):
break
review.type = DocTypeName.objects.get(slug="review")
review.group = review_req.team
review.group = assignment.review_request.team
review.rev = "00" if not review.rev else "{:02}".format(int(review.rev) + 1)
review.title = "{} Review of {}-{}".format(review_req.type.name, review_req.doc.name, form.cleaned_data["reviewed_rev"])
review.title = "{} Review of {}-{}".format(assignment.review_request.type.name, assignment.review_request.doc.name, form.cleaned_data["reviewed_rev"])
review.time = datetime.datetime.now()
if review_submission == "link":
review.external_url = form.cleaned_data['review_url']
@ -551,63 +547,56 @@ def complete_review(request, name, request_id):
with open(filename, 'wb') as destination:
destination.write(encoded_content)
# close review request
review_req.state = form.cleaned_data["state"]
review_req.reviewed_rev = form.cleaned_data["reviewed_rev"]
review_req.result = form.cleaned_data["result"]
review_req.review = review
review_req.save()
need_to_email_review = review_submission != "link" and review_req.team.list_email and not revising_review
desc = "Request for {} review by {} {}: {}. Reviewer: {}.".format(
review_req.type.name,
review_req.team.acronym.upper(),
review_req.state.name,
review_req.result.name,
review_req.reviewer.person,
)
if need_to_email_review:
desc += " " + "Sent review to list."
completion_datetime = datetime.datetime.now()
if "completion_date" in form.cleaned_data:
completion_datetime = datetime.datetime.combine(form.cleaned_data["completion_date"], form.cleaned_data.get("completion_time") or datetime.time.min)
close_event = ReviewRequestDocEvent.objects.filter(type="closed_review_request", review_request=review_req).first()
if not close_event:
close_event = ReviewRequestDocEvent(type="closed_review_request", review_request=review_req)
# complete assignment
assignment.state = form.cleaned_data["state"]
assignment.reviewed_rev = form.cleaned_data["reviewed_rev"]
assignment.result = form.cleaned_data["result"]
assignment.review = review
assignment.completed_on = completion_datetime
assignment.save()
close_event.doc = review_req.doc
close_event.rev = review_req.doc.rev
need_to_email_review = review_submission != "link" and assignment.review_request.team.list_email and not revising_review
desc = "Request for {} review by {} {}: {}. Reviewer: {}.".format(
assignment.review_request.type.name,
assignment.review_request.team.acronym.upper(),
assignment.state.name,
assignment.result.name,
assignment.reviewer.person,
)
if need_to_email_review:
desc += " " + "Sent review to list."
close_event = ReviewAssignmentDocEvent.objects.filter(type="closed_review_request", review_assignment=assignment).first()
if not close_event:
close_event = ReviewAssignmentDocEvent(type="closed_review_request", review_assignment=assignment)
close_event.doc = assignment.review_request.doc
close_event.rev = assignment.review_request.doc.rev
close_event.by = request.user.person
close_event.desc = desc
close_event.state = review_req.state
close_event.state = assignment.state
close_event.time = completion_datetime
close_event.save()
if review_req.state_id == "part-completed" and not revising_review:
existing_open_reqs = ReviewRequest.objects.filter(doc=review_req.doc, team=review_req.team, state__in=("requested", "accepted"))
if assignment.state_id == "part-completed" and not revising_review:
existing_assignments = ReviewAssignment.objects.filter(review_request__doc=assignment.review_request.doc, review_request__team=assignment.review_request.team, state__in=("requested", "accepted", "completed"))
new_review_req_url = new_review_req = None
if not existing_open_reqs:
new_review_req = make_new_review_request_from_existing(review_req)
new_review_req.save()
new_review_req_url = urlreverse("ietf.doc.views_review.review_request", kwargs={ "name": new_review_req.doc.name, "request_id": new_review_req.pk })
new_review_req_url = request.build_absolute_uri(new_review_req_url)
subject = "Review of {}-{} completed partially".format(review_req.doc.name, review_req.reviewed_rev)
subject = "Review of {}-{} completed partially".format(assignment.review_request.doc.name, assignment.reviewed_rev)
msg = render_to_string("review/partially_completed_review.txt", {
"new_review_req_url": new_review_req_url,
"existing_open_reqs": existing_open_reqs,
"existing_assignments": existing_assignments,
"by": request.user.person,
})
email_review_request_change(request, review_req, subject, msg, request.user.person, notify_secretary=True, notify_reviewer=False, notify_requested_by=False)
# TODO: replumb this function to work with assignments
email_review_request_change(request, assignment.review_request, subject, msg, request.user.person, notify_secretary=True, notify_reviewer=False, notify_requested_by=False)
role = request.user.person.role_set.filter(group=review_req.team,name='reviewer').first()
role = request.user.person.role_set.filter(group=assignment.review_request.team,name='reviewer').first()
if role and role.email.active:
author_email = role.email
frm = role.formatted_email()
@ -618,10 +607,10 @@ def complete_review(request, name, request_id):
if need_to_email_review:
# email the review
subject = "{} {} {} of {}-{}".format(review_req.team.acronym.capitalize(),review_req.type.name.lower(),"partial review" if review_req.state_id == "part-completed" else "review", review_req.doc.name, review_req.reviewed_rev)
related_groups = [ review_req.team, ]
if review_req.doc.group:
related_groups.append(review_req.doc.group)
subject = "{} {} {} of {}-{}".format(assignment.review_request.team.acronym.capitalize(),assignment.review_request.type.name.lower(),"partial review" if assignment.state_id == "part-completed" else "review", assignment.review_request.doc.name, assignment.reviewed_rev)
related_groups = [ assignment.review_request.team, ]
if assignment.review_request.doc.group:
related_groups.append(assignment.review_request.doc.group)
msg = Message.objects.create(
by=request.user.person,
subject=subject,
@ -629,26 +618,26 @@ def complete_review(request, name, request_id):
to=", ".join(to),
cc=form.cleaned_data["cc"],
body = render_to_string("review/completed_review.txt", {
"review_req": review_req,
"assignment": assignment,
"content": encoded_content.decode("utf-8"),
}),
)
msg.related_groups.add(*related_groups)
msg.related_docs.add(review_req.doc)
msg.related_docs.add(assignment.review_request.doc)
msg = send_mail_message(request, msg)
list_name = mailarch.list_name_from_email(review_req.team.list_email)
list_name = mailarch.list_name_from_email(assignment.review_request.team.list_email)
if list_name:
review.external_url = mailarch.construct_message_url(list_name, email.utils.unquote(msg["Message-ID"]))
review.save_with_history([close_event])
if form.cleaned_data['email_ad'] or review_req.result in review_req.team.reviewteamsettings.notify_ad_when.all():
(to, cc) = gather_address_lists('review_notify_ad',review_req = review_req).as_strings()
if form.cleaned_data['email_ad'] or assignment.result in assignment.review_request.team.reviewteamsettings.notify_ad_when.all():
(to, cc) = gather_address_lists('review_notify_ad',review_req = assignment.review_request).as_strings()
msg_txt = render_to_string("review/notify_ad.txt", {
"to": to,
"cc": cc,
"review_req": review_req,
"assignment": assignment,
"settings": settings,
"explicit_request": form.cleaned_data['email_ad'],
})
@ -657,42 +646,41 @@ def complete_review(request, name, request_id):
msg.save()
send_mail_message(request, msg)
return redirect("ietf.doc.views_doc.document_main", name=review_req.review.name)
return redirect("ietf.doc.views_doc.document_main", name=assignment.review.name)
else:
initial={
"reviewed_rev": review_req.reviewed_rev,
"result": review_req.result_id,
"reviewed_rev": assignment.reviewed_rev,
"result": assignment.result_id,
"cc": ", ".join(cc),
}
try:
initial['review_content'] = render_to_string('/group/%s/review/content_templates/%s.txt' % (review_req.team.acronym, review_req.type.slug), {'review_req':review_req,'today':datetime.date.today()})
initial['review_content'] = render_to_string('/group/%s/review/content_templates/%s.txt' % (assignment.review_request.team.acronym, assignment.review_request.type.slug), {'assignment':assignment,'today':datetime.date.today()})
except TemplateDoesNotExist:
pass
form = CompleteReviewForm(review_req, is_reviewer, initial=initial)
form = CompleteReviewForm(assignment, is_reviewer, initial=initial)
mail_archive_query_urls = mailarch.construct_query_urls(review_req)
mail_archive_query_urls = mailarch.construct_query_urls(assignment.review_request)
return render(request, 'doc/review/complete_review.html', {
'doc': doc,
'review_req': review_req,
'assignment': assignment,
'form': form,
'mail_archive_query_urls': mail_archive_query_urls,
'revising_review': revising_review,
})
def search_mail_archive(request, name, request_id):
#doc = get_object_or_404(Document, name=name)
review_req = get_object_or_404(ReviewRequest, pk=request_id)
def search_mail_archive(request, name, assignment_id):
assignment = get_object_or_404(ReviewAssignment, pk=assignment_id)
is_reviewer = user_is_person(request.user, review_req.reviewer.person)
can_manage_request = can_manage_review_requests_for_team(request.user, review_req.team)
is_reviewer = user_is_person(request.user, assignment.reviewer.person)
can_manage_request = can_manage_review_requests_for_team(request.user, assignment.review_request.team)
if not (is_reviewer or can_manage_request):
return HttpResponseForbidden("You do not have permission to perform this action")
res = mailarch.construct_query_urls(review_req, query=request.GET.get("query"))
res = mailarch.construct_query_urls(assignment.review_request, query=request.GET.get("query"))
if not res:
return JsonResponse({ "error": "Couldn't do lookup in mail archive - don't know where to look"})

File diff suppressed because it is too large Load diff

View file

@ -85,6 +85,7 @@ class Migration(migrations.Migration):
dependencies = [
('name', '0005_reviewassignmentstatename'),
('doc','0011_reviewassignmentdocevent'),
]
operations = [

View file

@ -1,7 +1,7 @@
import factory
import datetime
from ietf.review.models import ReviewTeamSettings, ReviewRequest, ReviewerSettings
from ietf.review.models import ReviewTeamSettings, ReviewRequest, ReviewAssignment, ReviewerSettings
from ietf.name.models import ReviewTypeName, ReviewResultName
class ReviewTeamSettingsFactory(factory.DjangoModelFactory):
@ -39,6 +39,14 @@ class ReviewRequestFactory(factory.DjangoModelFactory):
deadline = datetime.datetime.today()+datetime.timedelta(days=14)
requested_by = factory.SubFactory('ietf.person.factories.PersonFactory')
class ReviewAssignmentFactory(factory.DjangoModelFactory):
class Meta:
model = ReviewAssignment
review_request = factory.SubFactory('ietf.review.factories.ReviewRequestFactory')
state_id = 'requested'
reviewer = factory.SubFactory('ietf.person.factories.EmailFactory')
class ReviewerSettingsFactory(factory.DjangoModelFactory):
class Meta:
model = ReviewerSettings

View file

@ -11,7 +11,7 @@ class Migration(migrations.Migration):
dependencies = [
('doc', '0009_move_non_url_externalurls_to_uploaded_filename'),
('name', '0006_adjust_statenames'),
('name', '0005_reviewassignmentstatename'),
('person', '0008_auto_20181014_1448'),
('review', '0008_remove_reviewrequest_old_id'),
]

View file

@ -701,7 +701,8 @@ def extract_revision_ordered_review_requests_for_documents_and_replaced(review_r
replaces = extract_complete_replaces_ancestor_mapping_for_docs(names)
requests_for_each_doc = defaultdict(list)
for r in review_request_queryset.filter(doc__in=set(e for l in replaces.itervalues() for e in l) | names).order_by("-reviewed_rev", "-time", "-id").iterator():
# TODO : See if this function is still meeting the need. I removed "-reviewed_rev" from the order_by below. The whole thing may need to be operating on ReviewAssignments?
for r in review_request_queryset.filter(doc__in=set(e for l in replaces.itervalues() for e in l) | names).order_by("-time", "-id").iterator():
requests_for_each_doc[r.doc_id].append(r)
# now collect in breadth-first order to keep the revision order intact

View file

@ -446,7 +446,11 @@ table tbody.meta th:nth-child(2), table tbody.meta td:nth-child(2) {
width: 9em;
}
table tbody.panel-meta th, table tbody.panel-meta td { border-top: 0; }
table tbody.panel-meta th:first-child, table tbody.panel-meta td:first-child {
text-align: right;
width: 9em;
}
td.area-director div { border-bottom: solid #ccc 1px; }

View file

@ -11,14 +11,14 @@
{% block content %}
{% origin %}
<h1>{% if revising_review %}Revise{% else %}Complete{% endif %} review<br>
<small>{{ review_req.doc.name }}</small>
<small>{{ assignment.review_request.doc.name }}</small>
</h1>
<p>
<div><strong>Review type:</strong> {{ review_req.team.acronym }} - {{ review_req.type }} review </div>
<div><strong>Requested version for review:</strong> {{ review_req.requested_rev|default:"Current" }} </div>
<div><strong>Requsted:</strong> {{ review_req.time|date:"Y-m-d" }} </div>
<div><strong>Reviewer:</strong> {{ review_req.reviewer.person.name }}</div>
<div><strong>Review type:</strong> {{ assignment.review_request.team.acronym }} - {{ assignment.review_request.type }} review </div>
<div><strong>Requested version for review:</strong> {{ assignment.review_request.requested_rev|default:"Current" }} </div>
<div><strong>Requested:</strong> {{ assignment.review_request.time|date:"Y-m-d" }} </div>
<div><strong>Reviewer:</strong> {{ assignment.reviewer.person.name }}</div>
</p>
{% if not revising_review %}
@ -37,7 +37,7 @@
{% bootstrap_form form layout="horizontal" %}
{% buttons %}
<a class="btn btn-default" href="{% url "ietf.doc.views_review.review_request" name=doc.canonical_name request_id=review_req.pk %}">Cancel</a>
<a class="btn btn-default" href="{% url "ietf.doc.views_review.review_request" name=doc.canonical_name request_id=assignment.review_request.pk %}">Cancel</a>
<button type="submit" class="btn btn-primary">{% if revising_review %}Revise{% else %}Complete{% endif %} review</button>
{% endbuttons %}
@ -95,7 +95,7 @@
{% block js %}
<script src="{% static 'bootstrap-datepicker/js/bootstrap-datepicker.min.js' %}"></script>
<script>
var searchMailArchiveUrl = "{% url "ietf.doc.views_review.search_mail_archive" name=review_req.doc.name request_id=review_req.pk %}";
var searchMailArchiveUrl = "{% url "ietf.doc.views_review.search_mail_archive" name=assignment.review_request.doc.name assignment_id=assignment.pk %}";
</script>
<script src="{% static 'ietf/js/complete-review.js' %}"></script>
{% endblock %}

View file

@ -1,4 +1,5 @@
{# Copyright The IETF Trust 2017, All Rights Reserved #}{% load origin %}{% origin %}
{# Copyright The IETF Trust 2017, All Rights Reserved #}{% load origin bootstrap3 %}{% origin %}
<p>I got included</p>
<table class="table table-condensed">
<tbody class="meta">
<tr>
@ -68,11 +69,19 @@
</tr>
{% endif %}
{% if doc.time %}
<tr>
<th></th>
<th>Other Reviews</th>
<th>Draft last updated</th>
<td>{{ doc.time|date:"Y-m-d" }}</td>
</tr>
{% endif %}
<tr>
<th></th>
<th>Reviews from other teams</th>
<td>
{% for req in review_req.other_completed_requests %}
{% for req in review_req.other_completed_requests %} {# TODO: align this with new models #}
{% if req.reviewer == review_req.reviewer %}<strong>{% endif %}
<a href="{% url "ietf.doc.views_review.review_request" name=req.doc request_id=req.id %}">{{req.team.acronym|capfirst}} {{req.type.name}} review of -{{req.reviewed_rev}} by {{req.reviewer.person.plain_name}}</a> {% if req.reviewed_rev != req.doc.rev %} (<a href="{{ rfcdiff_base_url }}?url1={{ req.doc.name }}-{{ req.reviewed_rev }}&amp;url2={{ req.doc.name }}-{{ req.doc.rev }}">diff</a>){% endif %}<br>
{% if req.reviewer == review_req.reviewer %}</strong>{% endif %}
@ -92,115 +101,99 @@
</tr>
{% endif %}
</tbody>
<tbody class="meta">
<tr>
<th>Review</th>
<th>State</th>
<td>{{ review_req.state.name }}
{% if snapshot %}
<span class="label label-warning">Snapshot</span>
{% endif %}
</td>
</tr>
<tr>
<th></th>
<th>Reviewer</th>
<td>
{% if review_req.reviewer %}
{{ review_req.reviewer.person }}
{% else %}
None assigned yet
{% endif %}
{% if can_assign_reviewer %}
<a class="btn btn-default btn-xs" href="{% url "ietf.doc.views_review.assign_reviewer" name=doc.name request_id=review_req.pk %}"><span class="fa fa-user"></span> {% if review_req.reviewer %}Reassign{% else %}Assign{% endif %} reviewer</a>
{% endif %}
{% if review_req.reviewer %}
{% if can_reject_reviewer_assignment or can_accept_reviewer_assignment %}
<div class="reviewer-assignment-not-accepted">
{% if review_req.state_id == "requested"%}
<em>Assignment not accepted yet:</em>
{% else %}
<em>Assignment accepted:</em>
{% endif %}
{% if can_reject_reviewer_assignment %}
<a class="btn btn-danger btn-xs" href="{% url "ietf.doc.views_review.reject_reviewer_assignment" name=doc.name request_id=review_req.pk %}"><span class="fa fa-ban"></span> Reject</a>
{% endif %}
{% if can_accept_reviewer_assignment %}
<form style="display:inline" method="post" action="{% url "ietf.doc.views_review.review_request" name=doc.name request_id=review_req.pk %}">{% csrf_token %}<button class="btn btn-success btn-xs" type="submit" name="action" value="accept"><span class="fa fa-check"></span> Accept</button></form>
{% endif %}
</div>
{% endif %}
{% endif %}
</td>
</tr>
<tr>
<th></th>
<th>Review</th>
<td>
{% if review_req.review %}
<a href="{{ review_req.review.get_absolute_url }}">{{ review_req.review.name }}</a>
{% elif review_req.state_id == "requested" or review_req.state_id == "accepted" %}
Not completed yet
{% else %}
Not available
{% endif %}
{% if can_complete_review %}
<a class="btn btn-primary btn-xs" href="{% url "ietf.doc.views_review.complete_review" name=doc.name request_id=review_req.pk %}"><span class="fa fa-pencil-square-o"></span> {% if review_req.state_id == "requested" or review_req.state_id == "accepted" %}Complete review{% else %}Correct review{% endif %}</a>
{% endif %}
</td>
</tr>
{% if review_req.review and review_req.review.external_url %}
<tr>
<th></th>
<th>Posted at</th>
<td>
<a href="{{ review_req.review.external_url }}">{{ review_req.review.external_url }}</a>
</td>
</tr>
{% endif %}
{% if review_req.reviewed_rev %}
<tr>
<th></th>
<th>Reviewed rev.</th>
<td><a href="{% url "ietf.doc.views_doc.document_main" name=review_req.doc.name rev=review_req.reviewed_rev %}">{{ review_req.reviewed_rev }}</a> {% if review_req.reviewed_rev != review_req.doc.rev %}(document currently at {{ review_req.doc.rev }}){% endif %}</td>
</tr>
{% endif %}
{% if review_req.result %}
<tr>
<th></th>
<th>Review result</th>
<td>{{ review_req.result.name }}</td>
</tr>
{% endif %}
{% if doc.time %}
<tr>
<th></th>
<th>Draft last updated</th>
<td>{{ doc.time|date:"Y-m-d" }}</td>
</tr>
{% endif %}
{% if review_req.state_id == "completed" or review_req.state_id == "part-completed" %}
<tr>
<th></th>
<th>Review completed:</th>
<td>
{{ review_req.review_done_time|date:"Y-m-d" }}
</td>
</tr>
{% endif %}
</tbody>
</table>
<p><strong>Assignments</strong></p>
{% if can_assign_reviewer %}
<p>
<a class="btn btn-default btn-xs" href="{% url "ietf.doc.views_review.assign_reviewer" name=doc.name request_id=review_req.pk %}"><span class="fa fa-user"></span> Assign reviewer</a>
</p>
{% endif %}
{% for assignment in assignments %}
<div class="panel panel-default">
<div class="panel-heading">
{{ assignment.reviewer.person }}
{% if assignment.can_reject_reviewer_assignment or assignment.can_accept_reviewer_assignment %}
<div class="reviewer-assignment-not-accepted">
{% if assignment.state_id == "requested"%}
<em>Assignment not accepted yet:</em>
{% else %}
<em>Assignment accepted:</em>
{% endif %}
{% if assignment.can_reject_reviewer_assignment %}
<a class="btn btn-danger btn-xs" href="{% url "ietf.doc.views_review.reject_reviewer_assignment" name=doc.name assignment_id=assignment.pk %}"><span class="fa fa-ban"></span> Reject</a>
{% endif %}
{% if assignment.can_accept_reviewer_assignment %}
<form style="display:inline" method="post" action="{% url "ietf.doc.views_review.review_request" name=doc.name request_id=review_req.pk %}">{% csrf_token %}<button class="btn btn-success btn-xs" type="submit" name="action" value="accept"><span class="fa fa-check"></span> Accept</button></form>
{% endif %}
</div>
{% endif %}
</div>
<div class="panel-body">
<table class="table table-condensed">
<tbody class="panel-meta">
<tr>
<th>State</th>
<td>{{ review_req.state.name }}
{% if snapshot %}
<span class="label label-warning">Snapshot</span>
{% endif %}
</td>
</tr>
<tr>
<th>Review</th>
<td>
{% if assignment.review %}
<a href="{{ assignment.review.get_absolute_url }}">{{ assignment.review.name }}</a>
{% elif assignment.state_id == "requested" or assignment.state_id == "accepted" %}
Not completed yet
{% else %}
Not available
{% endif %}
{% if assignment.can_complete_review %}
{# TODO: The url below needs to be to a new complete_assignment passed the assignment.pk #}
<a class="btn btn-primary btn-xs" href="{% url "ietf.doc.views_review.complete_review" name=doc.name assignment_id=assignment.pk %}"><span class="fa fa-pencil-square-o"></span> {% if assignment.state_id == "requested" or assignment.state_id == "accepted" %}Complete review{% else %}Correct review{% endif %}</a>
{% endif %}
</td>
</tr>
{% if assignment.review and assignment.review.external_url %}
<tr>
<th>Posted at</th>
<td>
<a href="{{ assignment.review.external_url }}">{{ assignment.review.external_url }}</a>
</td>
</tr>
{% endif %}
{% if assignment.reviewed_rev %}
<tr>
<th>Reviewed rev.</th>
<td><a href="{% url "ietf.doc.views_doc.document_main" name=review_req.doc.name rev=assignment.reviewed_rev %}">{{ assignment.reviewed_rev }}</a> {% if assignment.reviewed_rev != review_req.doc.rev %}(document currently at {{ review_req.doc.rev }}){% endif %}</td>
</tr>
{% endif %}
{% if assignment.result %}
<tr>
<th>Review result</th>
<td>{{ assignment.result.name }}</td>
</tr>
{% endif %}
{% if assignment.state_id == "completed" or assignment.state_id == "part-completed" %}
<tr>
<th>Review completed:</th>
<td>
{{ assignment.completed_on|date:"Y-m-d" }}
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
{% endfor %}

View file

@ -1,9 +1,8 @@
{% load ietf_filters %}{% autoescape off %}{% filter maybewordwrap:80 %}{% if review_req.state_id == "part-completed" %}
Review is partially done. Another review request has been registered for
completing it.
{% load ietf_filters %}{% autoescape off %}{% filter maybewordwrap:80 %}{% if assignment.state_id == "part-completed" %}
Review is partially done. Another assignment may be needed to complete it.
{% endif %}Reviewer: {{ review_req.reviewer.person }}
Review result: {{ review_req.result.name }}
{% endif %}Reviewer: {{ assignment.reviewer.person }}
Review result: {{ assignment.result.name }}
{{ content }}
{% endfilter %}{% endautoescape %}

View file

@ -1,13 +1,13 @@
{% 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 %}
Subject: "{{assignment.result}}" review submitted for {{assignment.review_request.doc}}{% if assignment.reviewed_rev %}-{{assignment.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 %}.
{{assignment.reviewer.person}} has submitted a "{{assignment.result}}" review result for {{assignment.review_request.doc}}{% if assignment.reviewed_rev %}-{{assignment.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 review is available at {{settings.IDTRACKER_BASE_URL}}{% url 'ietf.doc.views_doc.document_main' name=assignment.review.name %}
The document is available at {{settings.IDTRACKER_BASE_URL}}{% url 'ietf.doc.views_doc.document_main' name=review_req.doc.name %}
The document is available at {{settings.IDTRACKER_BASE_URL}}{% url 'ietf.doc.views_doc.document_main' name=assignment.review_request.doc.name %}
This message was sent because {% if explicit_request %}the reviewer indicated it should be on the review completion form{% else %}the review team settings indicated it should be{% endif %}.

View file

@ -1,10 +1,7 @@
{% autoescape off %}Review was partially completed by {{ by }}.
{% if new_review_req_url %}
A new review request has been added for completing the review:
{{ new_review_req_url }}
{% else %}
Found {{ existing_open_reqs|length }} open review request{{ existing_open_reqs|pluralize }} on the document so a new
review request has not been added.
A new reviewer may need to be assigned to get a complete review.
{% if existing_assignments %}
The following are already assigned to review this document:{% for assignment in existing_assignments %}
{{ assignment.reviewer.name }} : {{ assignment.state }}{% endfor %}
{% endif %}{% endautoescape %}