Skip to content

Commit 227fdd7

Browse files
committed
Add reviews to search results and IESG agenda. Support restricting
review types so that teams will only have those review types listed and suggested that they are configured to have. Fix a couple of things in the importer after having tested it on all the databases. Set unavailable end date >= 2020 to indefinite. Fix a couple of bugs. - Legacy-Id: 12095
1 parent 168fc04 commit 227fdd7

16 files changed

Lines changed: 142 additions & 60 deletions

ietf/doc/resources.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -516,13 +516,12 @@ class Meta:
516516

517517

518518
from ietf.person.resources import PersonResource
519-
from ietf.review.resources import ReviewRequestResource
520519
from ietf.name.resources import ReviewRequestStateNameResource
521520
class ReviewRequestDocEventResource(ModelResource):
522521
by = ToOneField(PersonResource, 'by')
523522
doc = ToOneField(DocumentResource, 'doc')
524523
docevent_ptr = ToOneField(DocEventResource, 'docevent_ptr')
525-
review_request = ToOneField(ReviewRequestResource, 'review_request')
524+
review_request = ToOneField('review.ReviewRequestResource', 'review_request')
526525
state = ToOneField(ReviewRequestStateNameResource, 'state', null=True)
527526
class Meta:
528527
queryset = ReviewRequestDocEvent.objects.all()

ietf/doc/tests_review.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import debug # pyflakes:ignore
1414

15-
from ietf.review.models import (ReviewRequest, ReviewTeamResult, ReviewerSettings,
15+
from ietf.review.models import (ReviewRequest, ResultUsedInReviewTeam, ReviewerSettings,
1616
ReviewWish, UnavailablePeriod, NextReviewerInTeam)
1717
from ietf.review.utils import reviewer_rotation_list, possibly_advance_next_reviewer_for_team
1818
import ietf.review.mailarch
@@ -470,7 +470,7 @@ def setup_complete_review_test(self):
470470
review_req.state = ReviewRequestStateName.objects.get(slug="accepted")
471471
review_req.save()
472472
for r in ReviewResultName.objects.filter(slug__in=("issues", "ready")):
473-
ReviewTeamResult.objects.get_or_create(team=review_req.team, result=r)
473+
ResultUsedInReviewTeam.objects.get_or_create(team=review_req.team, result=r)
474474
review_req.team.save()
475475

476476
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": doc.name, "request_id": review_req.pk })
@@ -508,7 +508,7 @@ def test_complete_review_upload_content(self):
508508
test_file.name = "unnamed"
509509

510510
r = self.client.post(url, data={
511-
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
511+
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
512512
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
513513
"reviewed_rev": review_req.doc.rev,
514514
"review_submission": "upload",
@@ -552,7 +552,7 @@ def test_complete_review_enter_content(self):
552552
empty_outbox()
553553

554554
r = self.client.post(url, data={
555-
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
555+
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
556556
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
557557
"reviewed_rev": review_req.doc.rev,
558558
"review_submission": "enter",
@@ -583,7 +583,7 @@ def test_complete_review_link_to_mailing_list(self):
583583
empty_outbox()
584584

585585
r = self.client.post(url, data={
586-
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
586+
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
587587
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
588588
"reviewed_rev": review_req.doc.rev,
589589
"review_submission": "link",
@@ -611,7 +611,7 @@ def test_partially_complete_review(self):
611611
empty_outbox()
612612

613613
r = self.client.post(url, data={
614-
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
614+
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
615615
"state": ReviewRequestStateName.objects.get(slug="part-completed").pk,
616616
"reviewed_rev": review_req.doc.rev,
617617
"review_submission": "enter",
@@ -645,7 +645,7 @@ def test_partially_complete_review(self):
645645
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": review_req.doc.name, "request_id": review_req.pk })
646646

647647
r = self.client.post(url, data={
648-
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
648+
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
649649
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
650650
"reviewed_rev": review_req.doc.rev,
651651
"review_submission": "enter",

ietf/doc/utils_search.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ def fill_in_document_table_attributes(docs):
6060
d.expirable = expirable_draft(d)
6161

6262
if d.get_state_slug() != "rfc":
63-
d.milestones = d.groupmilestone_set.filter(state="active").order_by("time").select_related("group")
63+
d.milestones = sorted((m for m in d.groupmilestone_set.all() if m.state_id == "active"), key=lambda m: m.time)
6464

65+
d.reviewed_by_teams = sorted(set(r.team for r in d.reviewrequest_set.all()), key=lambda g: g.acronym)
6566

6667

6768
# RFCs
@@ -101,7 +102,7 @@ def prepare_document_table(request, docs, query=None, max_results=500):
101102
# evaluate and fill in attribute results immediately to decrease
102103
# the number of queries
103104
docs = docs.select_related("ad", "ad__person", "std_level", "intended_std_level", "group", "stream")
104-
docs = docs.prefetch_related("states__type", "tags")
105+
docs = docs.prefetch_related("states__type", "tags", "groupmilestone_set__group", "reviewrequest_set__team")
105106

106107
docs = list(docs[:max_results])
107108

ietf/doc/views_doc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
from ietf.meeting.models import Session
6565
from ietf.meeting.utils import group_sessions, get_upcoming_manageable_sessions, sort_sessions
6666
from ietf.review.models import ReviewRequest
67-
from ietf.review.utils import can_request_review_of_doc, review_requests_to_list_for_doc
67+
from ietf.review.utils import can_request_review_of_doc, review_requests_to_list_for_docs
6868
from ietf.review.utils import no_review_from_teams_on_doc
6969

7070
def render_document_top(request, doc, tab, name):
@@ -360,7 +360,7 @@ def document_main(request, name, rev=None):
360360
published = doc.latest_event(type="published_rfc")
361361
started_iesg_process = doc.latest_event(type="started_iesg_process")
362362

363-
review_requests = review_requests_to_list_for_doc(doc)
363+
review_requests = review_requests_to_list_for_docs([doc]).get(doc.pk, [])
364364
no_review_from_teams = no_review_from_teams_on_doc(doc, rev or doc.rev)
365365

366366
return render_to_response("doc/document_draft.html",
@@ -586,7 +586,7 @@ def document_main(request, name, rev=None):
586586

587587
other_reviews = []
588588
if review_req:
589-
other_reviews = [r for r in review_requests_to_list_for_doc(review_req.doc) if r != review_req]
589+
other_reviews = [r for r in review_requests_to_list_for_docs([review_req.doc]).get(doc.pk, []) if r != review_req]
590590

591591
return render(request, "doc/document_review.html",
592592
dict(doc=doc,

ietf/doc/views_review.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ietf.doc.models import (Document, NewRevisionDocEvent, State, DocAlias,
1313
LastCallDocEvent, ReviewRequestDocEvent)
1414
from ietf.name.models import ReviewRequestStateName, ReviewResultName, DocTypeName
15-
from ietf.review.models import ReviewRequest
15+
from ietf.review.models import ReviewRequest, TypeUsedInReviewTeam
1616
from ietf.group.models import Group
1717
from ietf.person.fields import PersonEmailChoiceField, SearchablePersonField
1818
from ietf.ietfauth.utils import is_authorized_in_doc_stream, user_is_person, has_role
@@ -49,16 +49,13 @@ def __init__(self, user, doc, *args, **kwargs):
4949

5050
self.doc = doc
5151

52-
self.fields['type'].queryset = self.fields['type'].queryset.filter(used=True)
53-
self.fields['type'].widget = forms.RadioSelect(choices=[t for t in self.fields['type'].choices if t[0]])
54-
5552
f = self.fields["team"]
5653
f.queryset = active_review_teams()
57-
if not is_authorized_in_doc_stream(user, doc): # user is a reviewer
58-
f.queryset = f.queryset.filter(role__name="reviewer", role__person__user=user)
59-
6054
f.initial = [group.pk for group in f.queryset if can_manage_review_requests_for_team(user, group, allow_non_team_personnel=False)]
6155

56+
self.fields['type'].queryset = self.fields['type'].queryset.filter(used=True, typeusedinreviewteam__team__in=self.fields["team"].queryset).distinct()
57+
self.fields['type'].widget = forms.RadioSelect(choices=[t for t in self.fields['type'].choices if t[0]])
58+
6259
self.fields["requested_rev"].label = "Document revision"
6360

6461
if has_role(user, "Secretariat"):
@@ -76,6 +73,17 @@ def clean_deadline(self):
7673
def clean_requested_rev(self):
7774
return clean_doc_revision(self.doc, self.cleaned_data.get("requested_rev"))
7875

76+
def clean(self):
77+
chosen_type = self.cleaned_data.get("type")
78+
chosen_teams = self.cleaned_data.get("team")
79+
80+
if chosen_type and chosen_teams:
81+
for t in chosen_teams:
82+
if not TypeUsedInReviewTeam.objects.filter(type=chosen_type, team=t).exists():
83+
self.add_error("type", "{} does not use the review type {}.".format(t.name, chosen_type.name))
84+
85+
return self.cleaned_data
86+
7987
@login_required
8088
def request_review(request, name):
8189
doc = get_object_or_404(Document, name=name)
@@ -343,7 +351,7 @@ def __init__(self, review_req, *args, **kwargs):
343351
" ".join("<a class=\"rev label label-default\">{}</a>".format(r)
344352
for r in known_revisions))
345353

346-
self.fields["result"].queryset = self.fields["result"].queryset.filter(reviewteamresult__team=review_req.team)
354+
self.fields["result"].queryset = self.fields["result"].queryset.filter(resultusedinreviewteam__team=review_req.team)
347355
self.fields["review_submission"].choices = [
348356
(k, label.format(mailing_list=review_req.team.list_email or "[error: team has no mailing list set]"))
349357
for k, label in self.fields["review_submission"].choices

ietf/group/views_review.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from ietf.review.models import ReviewRequest, ReviewerSettings, UnavailablePeriod
1212
from ietf.review.utils import (can_manage_review_requests_for_team, close_review_request_states,
13-
extract_revision_ordered_review_requests_for_documents,
13+
extract_revision_ordered_review_requests_for_documents_and_replaced,
1414
assign_review_request_to_reviewer,
1515
close_review_request,
1616
setup_reviewer_field,
@@ -201,7 +201,7 @@ def manage_review_requests(request, acronym, group_type=None):
201201

202202
review_requests = suggested_review_requests_for_team(group) + open_review_requests
203203

204-
document_requests = extract_revision_ordered_review_requests_for_documents(
204+
document_requests = extract_revision_ordered_review_requests_for_documents_and_replaced(
205205
ReviewRequest.objects.filter(state__in=("part-completed", "completed"), team=group).prefetch_related("result"),
206206
set(r.doc_id for r in review_requests),
207207
)

ietf/iesg/agenda.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent
1111
from ietf.iesg.models import TelechatDate, TelechatAgendaItem
12-
12+
from ietf.review.utils import review_requests_to_list_for_docs
1313

1414
def get_agenda_date(date=None):
1515
if not date:
@@ -152,6 +152,8 @@ def fill_in_agenda_docs(date, sections, matches=None):
152152
matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date)
153153
matches = matches.select_related("stream", "group").distinct()
154154

155+
review_requests_for_docs = review_requests_to_list_for_docs(matches)
156+
155157
for doc in matches:
156158
if doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date:
157159
continue
@@ -174,6 +176,8 @@ def fill_in_agenda_docs(date, sections, matches=None):
174176
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
175177
if e and (e.consensus != None):
176178
doc.consensus = "Yes" if e.consensus else "No"
179+
180+
doc.review_requests = review_requests_for_docs.get(doc.pk, [])
177181
elif doc.type_id == "conflrev":
178182
doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
179183
elif doc.type_id == "charter":

ietf/review/import_from_review_tool.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
import datetime, re, itertools
1717
from collections import namedtuple
1818
from django.db import connections
19-
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName,
20-
ReviewRequestStateName, ReviewTypeName, ReviewTeamResult,
19+
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName, ResultUsedInReviewTeam,
20+
ReviewRequestStateName, ReviewTypeName, TypeUsedInReviewTeam,
2121
UnavailablePeriod, NextReviewerInTeam)
2222
from ietf.group.models import Group, Role, RoleName
2323
from ietf.person.models import Person, Email, Alias
@@ -129,6 +129,10 @@ def parse_timestamp(t):
129129
today = datetime.date.today()
130130
end_date = unavailable_until.date()
131131
if end_date >= today:
132+
if end_date >= datetime.date(2020, 1, 1):
133+
# convert hacked end dates to indefinite
134+
end_date = None
135+
132136
UnavailablePeriod.objects.filter(person=email.person, team=team).delete()
133137

134138
UnavailablePeriod.objects.create(
@@ -162,7 +166,10 @@ def parse_timestamp(t):
162166
summaries = [v.strip().lower() for v in row.value.split(";") if v.strip()]
163167

164168
for s in summaries:
165-
ReviewTeamResult.objects.get_or_create(team=team, result=results[s])
169+
ResultUsedInReviewTeam.objects.get_or_create(team=team, result=results[s])
170+
171+
for t in ReviewTypeName.objects.filter(slug__in=["early", "lc", "telechat"]):
172+
TypeUsedInReviewTeam.objects.get_or_create(team=team, type=t)
166173

167174
# review requests
168175

@@ -186,8 +193,8 @@ def parse_timestamp(t):
186193

187194
requested_re = re.compile("(?:ADDED docname =>|Created: remote=|AUTO UPDATED status TO new|CHANGED status FROM [^ ]+ => new|CHANGE status done => working)")
188195

189-
add_docstatus_re = re.compile('([a-zA-Z-_]+) ADD docstatus => (\w+)')
190-
update_docstatus_re = re.compile('([a-zA-Z-_]+) (?:UPDATE|CHANGE) docstatus \w+ => (\w+)')
196+
add_docstatus_re = re.compile('([a-zA-Z-_@.]+) ADD docstatus => (\w+)')
197+
update_docstatus_re = re.compile('([a-zA-Z-_@.]+) (?:UPDATE|CHANGE) docstatus \w+ => (\w+)')
191198
iesgstatus_re = re.compile('(?:ADD|ADDED|CHANGED) iesgstatus (?:FROM )?(?:[^ ]+ )?=> ([a-zA-Z-_]+)?')
192199

193200
deadline_re = re.compile('(?:ADD|ADDED|CHANGED) deadline (?:FROM )?(?:[^ ]+ )?=> ([1-9][0-9]+)')
@@ -196,6 +203,8 @@ def parse_timestamp(t):
196203

197204
close_states = ["done", "rejected", "withdrawn", "noresponse"]
198205

206+
document_blacklist = set([(u"tsvdir", u"draft-arkko-ipv6-transition-guidelines-09 ")])
207+
199208
with db_con.cursor() as c:
200209
c.execute("""select docname, time, who, text from doclog where
201210
text like 'Created: remote=%'
@@ -214,6 +223,9 @@ def parse_timestamp(t):
214223
or text like '%CHANGED iesgstatus % => %'
215224
order by docname, time asc;""")
216225
for docname, rows in itertools.groupby(namedtuplefetchall(c), lambda row: row.docname):
226+
if (team.acronym, docname) in document_blacklist:
227+
continue # ignore
228+
217229
branches = {}
218230

219231
latest_requested = None
@@ -233,12 +245,12 @@ def parse_timestamp(t):
233245
else:
234246
if "ADD docstatus" in row.text:
235247
m = add_docstatus_re.match(row.text)
236-
assert m, 'row.text "{}" does not match add regexp'.format(row.text)
248+
assert m, 'row.text "{}" does not match add regexp {}'.format(row.text, docname)
237249
membername, state = m.groups()
238250
used = True
239251
elif "UPDATE docstatus" in row.text or "CHANGE docstatus" in row.text:
240252
m = update_docstatus_re.match(row.text)
241-
assert m, 'row.text "{}" does not match update regexp'.format(row.text)
253+
assert m, 'row.text "{}" does not match update regexp {}'.format(row.text, docname)
242254
membername, state = m.groups()
243255
used = True
244256

@@ -332,6 +344,9 @@ def parse_timestamp(t):
332344
c.execute("select * from reviews order by reviewid;")
333345

334346
for row in namedtuplefetchall(c):
347+
if (team.acronym, row.docname) in document_blacklist:
348+
continue # ignore
349+
335350
meta = doc_metadata.get((row.docname, row.version))
336351
if not meta:
337352
meta = doc_metadata.get(row.docname)
@@ -345,7 +360,7 @@ def parse_timestamp(t):
345360
if row.summary == "noresponse":
346361
reviewed_rev = ""
347362

348-
event_collection = None
363+
event_collection = {}
349364
branches = document_history.get(row.docname)
350365
if not branches:
351366
print "WARNING: no history for", row.docname

ietf/review/migrations/0001_initial.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class Migration(migrations.Migration):
6363
bases=(models.Model,),
6464
),
6565
migrations.CreateModel(
66-
name='ReviewTeamResult',
66+
name='ResultUsedInReviewTeam',
6767
fields=[
6868
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
6969
('result', models.ForeignKey(to='name.ReviewResultName')),
@@ -73,6 +73,17 @@ class Migration(migrations.Migration):
7373
},
7474
bases=(models.Model,),
7575
),
76+
migrations.CreateModel(
77+
name='TypeUsedInReviewTeam',
78+
fields=[
79+
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
80+
('team', models.ForeignKey(to='group.Group')),
81+
('type', models.ForeignKey(to='name.ReviewTypeName')),
82+
],
83+
options={
84+
},
85+
bases=(models.Model,),
86+
),
7687
migrations.CreateModel(
7788
name='ReviewWish',
7889
fields=[

ietf/review/models.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class ReviewWish(models.Model):
6666
def __unicode__(self):
6767
return u"{} wishes to review {} in {}".format(self.person, self.doc.name, self.team.acronym)
6868

69-
class ReviewTeamResult(models.Model):
69+
class ResultUsedInReviewTeam(models.Model):
7070
"""Captures that a result name is valid for a given team for new
7171
reviews. This also implicitly defines which teams are review
7272
teams - if there are no possible review results valid for a given
@@ -75,7 +75,16 @@ class ReviewTeamResult(models.Model):
7575
result = models.ForeignKey(ReviewResultName)
7676

7777
def __unicode__(self):
78-
return u"{} in {}".format(self.result.name, self.group.acronym)
78+
return u"{} in {}".format(self.result.name, self.team.acronym)
79+
80+
class TypeUsedInReviewTeam(models.Model):
81+
"""Captures that a type name is valid for a given team for new
82+
reviews. """
83+
team = models.ForeignKey(Group)
84+
type = models.ForeignKey(ReviewTypeName)
85+
86+
def __unicode__(self):
87+
return u"{} in {}".format(self.type.name, self.team.acronym)
7988

8089
class NextReviewerInTeam(models.Model):
8190
team = models.ForeignKey(Group)
@@ -97,7 +106,7 @@ class ReviewRequest(models.Model):
97106
time = models.DateTimeField(default=datetime.datetime.now)
98107
type = models.ForeignKey(ReviewTypeName)
99108
doc = models.ForeignKey(Document, related_name='reviewrequest_set')
100-
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamresult=None))
109+
team = models.ForeignKey(Group, limit_choices_to=~models.Q(resultusedinreviewteam=None))
101110
deadline = models.DateField()
102111
requested_by = models.ForeignKey(Person)
103112
requested_rev = models.CharField(verbose_name="requested revision", max_length=16, blank=True, help_text="Fill in if a specific revision is to be reviewed, e.g. 02")

0 commit comments

Comments
 (0)