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
This commit is contained in:
Ole Laursen 2016-10-06 14:47:41 +00:00
parent 168fc0425b
commit 227fdd7953
16 changed files with 142 additions and 60 deletions

View file

@ -516,13 +516,12 @@ api.doc.register(BallotPositionDocEventResource())
from ietf.person.resources import PersonResource
from ietf.review.resources import ReviewRequestResource
from ietf.name.resources import ReviewRequestStateNameResource
class ReviewRequestDocEventResource(ModelResource):
by = ToOneField(PersonResource, 'by')
doc = ToOneField(DocumentResource, 'doc')
docevent_ptr = ToOneField(DocEventResource, 'docevent_ptr')
review_request = ToOneField(ReviewRequestResource, 'review_request')
review_request = ToOneField('review.ReviewRequestResource', 'review_request')
state = ToOneField(ReviewRequestStateNameResource, 'state', null=True)
class Meta:
queryset = ReviewRequestDocEvent.objects.all()

View file

@ -12,7 +12,7 @@ from pyquery import PyQuery
import debug # pyflakes:ignore
from ietf.review.models import (ReviewRequest, ReviewTeamResult, ReviewerSettings,
from ietf.review.models import (ReviewRequest, ResultUsedInReviewTeam, ReviewerSettings,
ReviewWish, UnavailablePeriod, NextReviewerInTeam)
from ietf.review.utils import reviewer_rotation_list, possibly_advance_next_reviewer_for_team
import ietf.review.mailarch
@ -470,7 +470,7 @@ class ReviewTests(TestCase):
review_req.state = ReviewRequestStateName.objects.get(slug="accepted")
review_req.save()
for r in ReviewResultName.objects.filter(slug__in=("issues", "ready")):
ReviewTeamResult.objects.get_or_create(team=review_req.team, result=r)
ResultUsedInReviewTeam.objects.get_or_create(team=review_req.team, result=r)
review_req.team.save()
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": doc.name, "request_id": review_req.pk })
@ -508,7 +508,7 @@ class ReviewTests(TestCase):
test_file.name = "unnamed"
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "upload",
@ -552,7 +552,7 @@ class ReviewTests(TestCase):
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
@ -583,7 +583,7 @@ class ReviewTests(TestCase):
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "link",
@ -611,7 +611,7 @@ class ReviewTests(TestCase):
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="part-completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
@ -645,7 +645,7 @@ class ReviewTests(TestCase):
url = urlreverse('ietf.doc.views_review.complete_review', kwargs={ "name": review_req.doc.name, "request_id": review_req.pk })
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(reviewteamresult__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",

View file

@ -60,8 +60,9 @@ def fill_in_document_table_attributes(docs):
d.expirable = expirable_draft(d)
if d.get_state_slug() != "rfc":
d.milestones = d.groupmilestone_set.filter(state="active").order_by("time").select_related("group")
d.milestones = sorted((m for m in d.groupmilestone_set.all() if m.state_id == "active"), key=lambda m: m.time)
d.reviewed_by_teams = sorted(set(r.team for r in d.reviewrequest_set.all()), key=lambda g: g.acronym)
# RFCs
@ -101,7 +102,7 @@ def prepare_document_table(request, docs, query=None, max_results=500):
# evaluate and fill in attribute results immediately to decrease
# the number of queries
docs = docs.select_related("ad", "ad__person", "std_level", "intended_std_level", "group", "stream")
docs = docs.prefetch_related("states__type", "tags")
docs = docs.prefetch_related("states__type", "tags", "groupmilestone_set__group", "reviewrequest_set__team")
docs = list(docs[:max_results])

View file

@ -64,7 +64,7 @@ from ietf.mailtrigger.utils import gather_relevant_expansions
from ietf.meeting.models import Session
from ietf.meeting.utils import group_sessions, get_upcoming_manageable_sessions, sort_sessions
from ietf.review.models import ReviewRequest
from ietf.review.utils import can_request_review_of_doc, review_requests_to_list_for_doc
from ietf.review.utils import can_request_review_of_doc, review_requests_to_list_for_docs
from ietf.review.utils import no_review_from_teams_on_doc
def render_document_top(request, doc, tab, name):
@ -360,7 +360,7 @@ def document_main(request, name, rev=None):
published = doc.latest_event(type="published_rfc")
started_iesg_process = doc.latest_event(type="started_iesg_process")
review_requests = review_requests_to_list_for_doc(doc)
review_requests = review_requests_to_list_for_docs([doc]).get(doc.pk, [])
no_review_from_teams = no_review_from_teams_on_doc(doc, rev or doc.rev)
return render_to_response("doc/document_draft.html",
@ -586,7 +586,7 @@ def document_main(request, name, rev=None):
other_reviews = []
if review_req:
other_reviews = [r for r in review_requests_to_list_for_doc(review_req.doc) if r != review_req]
other_reviews = [r for r in review_requests_to_list_for_docs([review_req.doc]).get(doc.pk, []) if r != review_req]
return render(request, "doc/document_review.html",
dict(doc=doc,

View file

@ -12,7 +12,7 @@ from django.core.urlresolvers import reverse as urlreverse
from ietf.doc.models import (Document, NewRevisionDocEvent, State, DocAlias,
LastCallDocEvent, ReviewRequestDocEvent)
from ietf.name.models import ReviewRequestStateName, ReviewResultName, DocTypeName
from ietf.review.models import ReviewRequest
from ietf.review.models import ReviewRequest, TypeUsedInReviewTeam
from ietf.group.models import Group
from ietf.person.fields import PersonEmailChoiceField, SearchablePersonField
from ietf.ietfauth.utils import is_authorized_in_doc_stream, user_is_person, has_role
@ -49,16 +49,13 @@ class RequestReviewForm(forms.ModelForm):
self.doc = doc
self.fields['type'].queryset = self.fields['type'].queryset.filter(used=True)
self.fields['type'].widget = forms.RadioSelect(choices=[t for t in self.fields['type'].choices if t[0]])
f = self.fields["team"]
f.queryset = active_review_teams()
if not is_authorized_in_doc_stream(user, doc): # user is a reviewer
f.queryset = f.queryset.filter(role__name="reviewer", role__person__user=user)
f.initial = [group.pk for group in f.queryset if can_manage_review_requests_for_team(user, group, allow_non_team_personnel=False)]
self.fields['type'].queryset = self.fields['type'].queryset.filter(used=True, typeusedinreviewteam__team__in=self.fields["team"].queryset).distinct()
self.fields['type'].widget = forms.RadioSelect(choices=[t for t in self.fields['type'].choices if t[0]])
self.fields["requested_rev"].label = "Document revision"
if has_role(user, "Secretariat"):
@ -76,6 +73,17 @@ class RequestReviewForm(forms.ModelForm):
def clean_requested_rev(self):
return clean_doc_revision(self.doc, self.cleaned_data.get("requested_rev"))
def clean(self):
chosen_type = self.cleaned_data.get("type")
chosen_teams = self.cleaned_data.get("team")
if chosen_type and chosen_teams:
for t in chosen_teams:
if not TypeUsedInReviewTeam.objects.filter(type=chosen_type, team=t).exists():
self.add_error("type", "{} does not use the review type {}.".format(t.name, chosen_type.name))
return self.cleaned_data
@login_required
def request_review(request, name):
doc = get_object_or_404(Document, name=name)
@ -343,7 +351,7 @@ class CompleteReviewForm(forms.Form):
" ".join("<a class=\"rev label label-default\">{}</a>".format(r)
for r in known_revisions))
self.fields["result"].queryset = self.fields["result"].queryset.filter(reviewteamresult__team=review_req.team)
self.fields["result"].queryset = self.fields["result"].queryset.filter(resultusedinreviewteam__team=review_req.team)
self.fields["review_submission"].choices = [
(k, label.format(mailing_list=review_req.team.list_email or "[error: team has no mailing list set]"))
for k, label in self.fields["review_submission"].choices

View file

@ -10,7 +10,7 @@ from django.template.loader import render_to_string
from ietf.review.models import ReviewRequest, ReviewerSettings, UnavailablePeriod
from ietf.review.utils import (can_manage_review_requests_for_team, close_review_request_states,
extract_revision_ordered_review_requests_for_documents,
extract_revision_ordered_review_requests_for_documents_and_replaced,
assign_review_request_to_reviewer,
close_review_request,
setup_reviewer_field,
@ -201,7 +201,7 @@ def manage_review_requests(request, acronym, group_type=None):
review_requests = suggested_review_requests_for_team(group) + open_review_requests
document_requests = extract_revision_ordered_review_requests_for_documents(
document_requests = extract_revision_ordered_review_requests_for_documents_and_replaced(
ReviewRequest.objects.filter(state__in=("part-completed", "completed"), team=group).prefetch_related("result"),
set(r.doc_id for r in review_requests),
)

View file

@ -9,7 +9,7 @@ from django.http import Http404
from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent
from ietf.iesg.models import TelechatDate, TelechatAgendaItem
from ietf.review.utils import review_requests_to_list_for_docs
def get_agenda_date(date=None):
if not date:
@ -152,6 +152,8 @@ def fill_in_agenda_docs(date, sections, matches=None):
matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date)
matches = matches.select_related("stream", "group").distinct()
review_requests_for_docs = review_requests_to_list_for_docs(matches)
for doc in matches:
if doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date:
continue
@ -174,6 +176,8 @@ def fill_in_agenda_docs(date, sections, matches=None):
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
if e and (e.consensus != None):
doc.consensus = "Yes" if e.consensus else "No"
doc.review_requests = review_requests_for_docs.get(doc.pk, [])
elif doc.type_id == "conflrev":
doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
elif doc.type_id == "charter":

View file

@ -16,8 +16,8 @@ django.setup()
import datetime, re, itertools
from collections import namedtuple
from django.db import connections
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName,
ReviewRequestStateName, ReviewTypeName, ReviewTeamResult,
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName, ResultUsedInReviewTeam,
ReviewRequestStateName, ReviewTypeName, TypeUsedInReviewTeam,
UnavailablePeriod, NextReviewerInTeam)
from ietf.group.models import Group, Role, RoleName
from ietf.person.models import Person, Email, Alias
@ -129,6 +129,10 @@ with db_con.cursor() as c:
today = datetime.date.today()
end_date = unavailable_until.date()
if end_date >= today:
if end_date >= datetime.date(2020, 1, 1):
# convert hacked end dates to indefinite
end_date = None
UnavailablePeriod.objects.filter(person=email.person, team=team).delete()
UnavailablePeriod.objects.create(
@ -162,7 +166,10 @@ with db_con.cursor() as c:
summaries = [v.strip().lower() for v in row.value.split(";") if v.strip()]
for s in summaries:
ReviewTeamResult.objects.get_or_create(team=team, result=results[s])
ResultUsedInReviewTeam.objects.get_or_create(team=team, result=results[s])
for t in ReviewTypeName.objects.filter(slug__in=["early", "lc", "telechat"]):
TypeUsedInReviewTeam.objects.get_or_create(team=team, type=t)
# review requests
@ -186,8 +193,8 @@ document_history = {}
requested_re = re.compile("(?:ADDED docname =>|Created: remote=|AUTO UPDATED status TO new|CHANGED status FROM [^ ]+ => new|CHANGE status done => working)")
add_docstatus_re = re.compile('([a-zA-Z-_]+) ADD docstatus => (\w+)')
update_docstatus_re = re.compile('([a-zA-Z-_]+) (?:UPDATE|CHANGE) docstatus \w+ => (\w+)')
add_docstatus_re = re.compile('([a-zA-Z-_@.]+) ADD docstatus => (\w+)')
update_docstatus_re = re.compile('([a-zA-Z-_@.]+) (?:UPDATE|CHANGE) docstatus \w+ => (\w+)')
iesgstatus_re = re.compile('(?:ADD|ADDED|CHANGED) iesgstatus (?:FROM )?(?:[^ ]+ )?=> ([a-zA-Z-_]+)?')
deadline_re = re.compile('(?:ADD|ADDED|CHANGED) deadline (?:FROM )?(?:[^ ]+ )?=> ([1-9][0-9]+)')
@ -196,6 +203,8 @@ lcend_re = re.compile('(?:ADD|ADDED|CHANGED) lcend (?:FROM )?(?:[^ ]+ )?=> ([1-9
close_states = ["done", "rejected", "withdrawn", "noresponse"]
document_blacklist = set([(u"tsvdir", u"draft-arkko-ipv6-transition-guidelines-09 ")])
with db_con.cursor() as c:
c.execute("""select docname, time, who, text from doclog where
text like 'Created: remote=%'
@ -214,6 +223,9 @@ with db_con.cursor() as c:
or text like '%CHANGED iesgstatus % => %'
order by docname, time asc;""")
for docname, rows in itertools.groupby(namedtuplefetchall(c), lambda row: row.docname):
if (team.acronym, docname) in document_blacklist:
continue # ignore
branches = {}
latest_requested = None
@ -233,12 +245,12 @@ with db_con.cursor() as c:
else:
if "ADD docstatus" in row.text:
m = add_docstatus_re.match(row.text)
assert m, 'row.text "{}" does not match add regexp'.format(row.text)
assert m, 'row.text "{}" does not match add regexp {}'.format(row.text, docname)
membername, state = m.groups()
used = True
elif "UPDATE docstatus" in row.text or "CHANGE docstatus" in row.text:
m = update_docstatus_re.match(row.text)
assert m, 'row.text "{}" does not match update regexp'.format(row.text)
assert m, 'row.text "{}" does not match update regexp {}'.format(row.text, docname)
membername, state = m.groups()
used = True
@ -332,6 +344,9 @@ with db_con.cursor() as c:
c.execute("select * from reviews order by reviewid;")
for row in namedtuplefetchall(c):
if (team.acronym, row.docname) in document_blacklist:
continue # ignore
meta = doc_metadata.get((row.docname, row.version))
if not meta:
meta = doc_metadata.get(row.docname)
@ -345,7 +360,7 @@ with db_con.cursor() as c:
if row.summary == "noresponse":
reviewed_rev = ""
event_collection = None
event_collection = {}
branches = document_history.get(row.docname)
if not branches:
print "WARNING: no history for", row.docname

View file

@ -63,7 +63,7 @@ class Migration(migrations.Migration):
bases=(models.Model,),
),
migrations.CreateModel(
name='ReviewTeamResult',
name='ResultUsedInReviewTeam',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('result', models.ForeignKey(to='name.ReviewResultName')),
@ -73,6 +73,17 @@ class Migration(migrations.Migration):
},
bases=(models.Model,),
),
migrations.CreateModel(
name='TypeUsedInReviewTeam',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('team', models.ForeignKey(to='group.Group')),
('type', models.ForeignKey(to='name.ReviewTypeName')),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='ReviewWish',
fields=[

View file

@ -66,7 +66,7 @@ class ReviewWish(models.Model):
def __unicode__(self):
return u"{} wishes to review {} in {}".format(self.person, self.doc.name, self.team.acronym)
class ReviewTeamResult(models.Model):
class ResultUsedInReviewTeam(models.Model):
"""Captures that a result name is valid for a given team for new
reviews. This also implicitly defines which teams are review
teams - if there are no possible review results valid for a given
@ -75,7 +75,16 @@ class ReviewTeamResult(models.Model):
result = models.ForeignKey(ReviewResultName)
def __unicode__(self):
return u"{} in {}".format(self.result.name, self.group.acronym)
return u"{} in {}".format(self.result.name, self.team.acronym)
class TypeUsedInReviewTeam(models.Model):
"""Captures that a type name is valid for a given team for new
reviews. """
team = models.ForeignKey(Group)
type = models.ForeignKey(ReviewTypeName)
def __unicode__(self):
return u"{} in {}".format(self.type.name, self.team.acronym)
class NextReviewerInTeam(models.Model):
team = models.ForeignKey(Group)
@ -97,7 +106,7 @@ class ReviewRequest(models.Model):
time = models.DateTimeField(default=datetime.datetime.now)
type = models.ForeignKey(ReviewTypeName)
doc = models.ForeignKey(Document, related_name='reviewrequest_set')
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamresult=None))
team = models.ForeignKey(Group, limit_choices_to=~models.Q(resultusedinreviewteam=None))
deadline = models.DateField()
requested_by = models.ForeignKey(Person)
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")

View file

@ -7,7 +7,8 @@ from tastypie.cache import SimpleCache
from ietf import api
from ietf.api import ToOneField # pyflakes:ignore
from ietf.review.models import (ReviewerSettings, ReviewRequest, ReviewTeamResult,
from ietf.review.models import (ReviewerSettings, ReviewRequest,
ResultUsedInReviewTeam, TypeUsedInReviewTeam,
UnavailablePeriod, ReviewWish, NextReviewerInTeam)
@ -67,20 +68,20 @@ api.review.register(ReviewRequestResource())
from ietf.group.resources import GroupResource
from ietf.name.resources import ReviewResultNameResource
class ReviewTeamResultResource(ModelResource):
class ResultUsedInReviewTeamResource(ModelResource):
team = ToOneField(GroupResource, 'team')
result = ToOneField(ReviewResultNameResource, 'result')
class Meta:
queryset = ReviewTeamResult.objects.all()
queryset = ResultUsedInReviewTeam.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'reviewteamresult'
#resource_name = 'resultusedinreviewteam'
filtering = {
"id": ALL,
"team": ALL_WITH_RELATIONS,
"result": ALL_WITH_RELATIONS,
}
api.review.register(ReviewTeamResultResource())
api.review.register(ResultUsedInReviewTeamResource())
@ -144,3 +145,22 @@ class NextReviewerInTeamResource(ModelResource):
}
api.review.register(NextReviewerInTeamResource())
from ietf.group.resources import GroupResource
from ietf.name.resources import ReviewTypeNameResource
class TypeUsedInReviewTeamResource(ModelResource):
team = ToOneField(GroupResource, 'team')
type = ToOneField(ReviewTypeNameResource, 'type')
class Meta:
queryset = TypeUsedInReviewTeam.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'typeusedinreviewteam'
filtering = {
"id": ALL,
"team": ALL_WITH_RELATIONS,
"type": ALL_WITH_RELATIONS,
}
api.review.register(TypeUsedInReviewTeamResource())

View file

@ -10,14 +10,14 @@ from ietf.doc.models import (Document, ReviewRequestDocEvent, State,
from ietf.iesg.models import TelechatDate
from ietf.person.models import Person, Email
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream
from ietf.review.models import (ReviewRequest, ReviewRequestStateName, ReviewTypeName,
from ietf.review.models import (ReviewRequest, ReviewRequestStateName, ReviewTypeName, TypeUsedInReviewTeam,
ReviewerSettings, UnavailablePeriod, ReviewWish, NextReviewerInTeam)
from ietf.utils.mail import send_mail
from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs
def active_review_teams():
# if there's a ReviewTeamResult defined, it's a review team
return Group.objects.filter(state="active").exclude(reviewteamresult=None)
# if there's a ResultUsedInReviewTeam defined, it's a review team
return Group.objects.filter(state="active").exclude(resultusedinreviewteam=None)
def close_review_request_states():
return ReviewRequestStateName.objects.filter(used=True).exclude(slug__in=["requested", "accepted", "rejected", "part-completed", "completed"])
@ -35,14 +35,14 @@ def can_manage_review_requests_for_team(user, team, allow_non_team_personnel=Tru
return (Role.objects.filter(name__in=["secr", "delegate"], person__user=user, group=team).exists()
or (allow_non_team_personnel and has_role(user, "Secretariat")))
def review_requests_to_list_for_doc(doc):
def review_requests_to_list_for_docs(docs):
request_qs = ReviewRequest.objects.filter(
state__in=["requested", "accepted", "part-completed", "completed"],
).prefetch_related("result")
doc_names = [doc.name]
doc_names = [d.name for d in docs]
return extract_revision_ordered_review_requests_for_documents(request_qs, doc_names).get(doc.pk, [])
return extract_revision_ordered_review_requests_for_documents_and_replaced(request_qs, doc_names)
def no_review_from_teams_on_doc(doc, rev):
return Group.objects.filter(
@ -417,9 +417,9 @@ def suggested_review_requests_for_team(team):
requested_state = ReviewRequestStateName.objects.get(slug="requested", used=True)
if True: # FIXME
last_call_type = ReviewTypeName.objects.get(slug="lc")
if TypeUsedInReviewTeam.objects.filter(team=team, type=last_call_type).exists():
# in Last Call
last_call_type = ReviewTypeName.objects.get(slug="lc")
last_call_docs = Document.objects.filter(states=State.objects.get(type="draft-iesg", slug="lc", used=True))
last_call_expiry_events = { e.doc_id: e for e in LastCallDocEvent.objects.order_by("time", "id") }
for doc in last_call_docs:
@ -443,11 +443,11 @@ def suggested_review_requests_for_team(team):
seen_deadlines[doc.pk] = deadline
if True: # FIXME
telechat_type = ReviewTypeName.objects.get(slug="telechat")
if TypeUsedInReviewTeam.objects.filter(team=team, type=telechat_type).exists():
# on Telechat Agenda
telechat_dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4])
telechat_type = ReviewTypeName.objects.get(slug="telechat")
telechat_deadline_delta = datetime.timedelta(days=2)
telechat_docs = Document.objects.filter(
@ -499,8 +499,8 @@ def suggested_review_requests_for_team(team):
res.sort(key=lambda r: (r.deadline, r.doc_id), reverse=True)
return res
def extract_revision_ordered_review_requests_for_documents(review_request_queryset, names):
"""Extracts all review requests for document names (including replaced ancestors)."""
def extract_revision_ordered_review_requests_for_documents_and_replaced(review_request_queryset, names):
"""Extracts all review requests for document names (including replaced ancestors), return them neatly sorted."""
names = set(names)

View file

@ -3,13 +3,9 @@
<a href="{% if review_request.review %}{% url "doc_view" review_request.review.name %}{% else %}{% url "ietf.doc.views_review.review_request" review_request.doc_id review_request.pk %}{% endif %}">
{{ review_request.team.acronym|upper }} {{ review_request.type.name }} Review{% if review_request.reviewed_rev and review_request.reviewed_rev != current_rev or review_request.doc_id != current_doc_name %} (of {% if review_request.doc_id != current_doc_name %}{{ review_request.doc_id }}{% endif %}-{{ review_request.reviewed_rev }}){% endif %}{% if review_request.result %}:
{{ review_request.result.name }}{% endif %} {% if review_request.state_id == "part-completed" %}(partially completed){% endif %}
- reviewer: {{ review_request.reviewer.person }}</a>
{% else %}
<i>
<a href="{% url "ietf.doc.views_review.review_request" review_request.doc_id review_request.pk %}">{{ review_request.team.acronym|upper }} {{ review_request.type.name }} Review
{% if review_request.reviewer %}
- reviewer: {{ review_request.reviewer.person }}
{% endif %}
- due: {{ review_request.deadline|date:"Y-m-d" }}</a></i>
{% endif %}
</div>

View file

@ -42,6 +42,13 @@
{{ doc.intended_std_level }}
{% endif %}
{% if doc.reviewed_by_teams %}
<br>Reviewed by:
{% for g in doc.reviewed_by_teams %}
{{ g.acronym }}{% if not forloop.last %}, {% endif %}
{% endfor %}
{% endif %}
{% for m in doc.milestones %}
{% if forloop.first %}<br>{% endif %}
<span title="Part of {{ m.group.acronym }} milestone: {{ m.desc }}" class="milestone">{{ m.due|date:"M Y" }}</span>{% if not forloop.last %}, {% endif %}

View file

@ -47,6 +47,15 @@
<dt>Consensus</dt><dd>{{ doc.consensus }}</dd>
{% endif %}
{% if doc.review_requests %}
<dt>Reviews</dt>
<dd>
{% for review_request in doc.review_requests %}
{% include "doc/review_request_summary.html" with current_doc_name=doc.name current_rev=doc.rev %}
{% endfor %}
</dd>
{% endif %}
{% if doc.lastcall_expires %}
<dt>Last call expires</dt><dd>{{ doc.lastcall_expires|date:"Y-m-d" }}</dd>
{% endif %}

View file

@ -14,7 +14,8 @@ from ietf.meeting.models import Meeting
from ietf.name.models import StreamName, DocRelationshipName
from ietf.person.models import Person, Email
from ietf.group.utils import setup_default_community_list_for_group
from ietf.review.models import ReviewRequest, ReviewerSettings, ReviewResultName, ReviewTeamResult
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName, ResultUsedInReviewTeam,
ReviewTypeName, TypeUsedInReviewTeam)
def create_person(group, role_name, name=None, username=None, email_address=None, password=None):
"""Add person/user/email and role."""
@ -396,7 +397,9 @@ def make_test_data():
def make_review_data(doc):
team = create_group(acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
for r in ReviewResultName.objects.filter(slug__in=["issues", "ready-issues", "ready", "not-ready"]):
ReviewTeamResult.objects.create(team=team, result=r)
ResultUsedInReviewTeam.objects.create(team=team, result=r)
for t in ReviewTypeName.objects.filter(slug__in=["early", "lc", "telechat"]):
TypeUsedInReviewTeam.objects.create(team=team, type=t)
p = Person.objects.get(user__username="plain")
email = p.email_set.first()