Skip to content

Commit

Permalink
Slight refactor of the review models to capture review team settings.…
Browse files Browse the repository at this point in the history
… Allows configuring review teams to get automatic suggestions for reviews or not. Provides a better admin for creating/managing review teams. Fixes #2048 and #2072. Commit ready for merge.

 - Legacy-Id: 12520
  • Loading branch information
rjsparks committed Dec 14, 2016
1 parent 57fa52e commit b24bdb5
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 37 deletions.
19 changes: 9 additions & 10 deletions ietf/doc/tests_review.py
Expand Up @@ -12,7 +12,7 @@

import debug # pyflakes:ignore

from ietf.review.models import (ReviewRequest, ResultUsedInReviewTeam, ReviewerSettings,
from ietf.review.models import (ReviewRequest, ReviewerSettings,
ReviewWish, UnavailablePeriod, NextReviewerInTeam)
from ietf.review.utils import reviewer_rotation_list, possibly_advance_next_reviewer_for_team
import ietf.review.mailarch
Expand Down Expand Up @@ -479,8 +479,7 @@ def setup_complete_review_test(self):
review_req.state = ReviewRequestStateName.objects.get(slug="accepted")
review_req.save()
for r in ReviewResultName.objects.filter(slug__in=("issues", "ready")):
ResultUsedInReviewTeam.objects.get_or_create(team=review_req.team, result=r)
review_req.team.save()
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 })

Expand Down Expand Up @@ -517,7 +516,7 @@ def test_complete_review_upload_content(self):
test_file.name = "unnamed"

r = self.client.post(url, data={
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "upload",
Expand Down Expand Up @@ -560,7 +559,7 @@ def test_complete_review_enter_content(self):
empty_outbox()

r = self.client.post(url, data={
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
Expand Down Expand Up @@ -590,7 +589,7 @@ def test_complete_review_link_to_mailing_list(self):
empty_outbox()

r = self.client.post(url, data={
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "link",
Expand Down Expand Up @@ -618,7 +617,7 @@ def test_partially_complete_review(self):
empty_outbox()

r = self.client.post(url, data={
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="part-completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
Expand Down Expand Up @@ -652,7 +651,7 @@ def test_partially_complete_review(self):
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(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
Expand All @@ -677,7 +676,7 @@ def test_revise_review_enter_content(self):
empty_outbox()

r = self.client.post(url, data={
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
Expand All @@ -702,7 +701,7 @@ def test_revise_review_enter_content(self):
# revise again
empty_outbox()
r = self.client.post(url, data={
"result": ReviewResultName.objects.get(resultusedinreviewteam__team=review_req.team, slug="ready").pk,
"result": ReviewResultName.objects.get(reviewteamsettings__group=review_req.team, slug="ready").pk,
"state": ReviewRequestStateName.objects.get(slug="part-completed").pk,
"reviewed_rev": review_req.doc.rev,
"review_submission": "enter",
Expand Down
8 changes: 4 additions & 4 deletions ietf/doc/views_review.py
Expand Up @@ -14,7 +14,7 @@
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, TypeUsedInReviewTeam
from ietf.review.models import ReviewRequest
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
Expand Down Expand Up @@ -57,7 +57,7 @@ def __init__(self, user, doc, *args, **kwargs):
f.queryset = active_review_teams()
f.initial = [group.pk for group in f.queryset if can_manage_review_requests_for_team(user, group, allow_personnel_outside_team=False)]

self.fields['type'].queryset = self.fields['type'].queryset.filter(used=True, typeusedinreviewteam__team__in=self.fields["team"].queryset).distinct()
self.fields['type'].queryset = self.fields['type'].queryset.filter(used=True, reviewteamsettings__group__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"
Expand All @@ -83,7 +83,7 @@ def clean(self):

if chosen_type and chosen_teams:
for t in chosen_teams:
if not TypeUsedInReviewTeam.objects.filter(type=chosen_type, team=t).exists():
if chosen_type not in t.reviewteamsettings.review_types.all():
self.add_error("type", "{} does not use the review type {}.".format(t.name, chosen_type.name))

return self.cleaned_data
Expand Down Expand Up @@ -361,7 +361,7 @@ def __init__(self, review_req, *args, **kwargs):
" ".join("<a class=\"rev label label-default\">{}</a>".format(r)
for r in known_revisions))

self.fields["result"].queryset = self.fields["result"].queryset.filter(resultusedinreviewteam__team=review_req.team)
self.fields["result"].queryset = self.fields["result"].queryset.filter(reviewteamsettings__group=review_req.team)

def format_submission_choice(label):
if revising_review:
Expand Down
10 changes: 9 additions & 1 deletion ietf/review/admin.py
Expand Up @@ -2,7 +2,7 @@

from ietf.review.models import (ReviewerSettings, UnavailablePeriod, ReviewWish,
ResultUsedInReviewTeam, TypeUsedInReviewTeam, NextReviewerInTeam,
ReviewRequest)
ReviewRequest, ReviewTeamSettings )

class ReviewerSettingsAdmin(admin.ModelAdmin):
def acronym(self, obj):
Expand Down Expand Up @@ -72,3 +72,11 @@ class ReviewRequestAdmin(admin.ModelAdmin):
search_fields = ["doc__name", "reviewer__person__name"]

admin.site.register(ReviewRequest, ReviewRequestAdmin)

class ReviewTeamSettingsAdmin(admin.ModelAdmin):
list_display = ["group", ]
search_fields = ["group__acronym", ]
raw_id_fields = ["group", ]
filter_horizontal = ["review_types", "review_results", ]

admin.site.register(ReviewTeamSettings, ReviewTeamSettingsAdmin)
8 changes: 4 additions & 4 deletions ietf/review/import_from_review_tool.py
Expand Up @@ -16,8 +16,8 @@
import datetime, re, itertools
from collections import namedtuple
from django.db import connections
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName, ResultUsedInReviewTeam,
ReviewRequestStateName, ReviewTypeName, TypeUsedInReviewTeam,
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName,
ReviewRequestStateName, ReviewTypeName,
UnavailablePeriod, NextReviewerInTeam)
from ietf.group.models import Group, Role, RoleName
from ietf.person.models import Person, Email, Alias
Expand Down Expand Up @@ -190,10 +190,10 @@ def parse_timestamp(t):
summaries = [v.strip().lower() for v in row.value.split(";") if v.strip()]

for s in summaries:
ResultUsedInReviewTeam.objects.get_or_create(team=team, result=results[s])
team.reviewteamsettings.review_results.add(results[s])

for t in ReviewTypeName.objects.filter(slug__in=["early", "lc", "telechat"]):
TypeUsedInReviewTeam.objects.get_or_create(team=team, type=t)
team.reviewteamsettings.review_types.add(t)

# review requests

Expand Down
31 changes: 31 additions & 0 deletions ietf/review/migrations/0007_reviewteamsettings.py
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models
import ietf.review.models


class Migration(migrations.Migration):

dependencies = [
('name', '0016_auto_20161013_1010'),
('group', '0009_auto_20150930_0758'),
('review', '0006_auto_20161209_0436'),
]

operations = [
migrations.CreateModel(
name='ReviewTeamSettings',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('autosuggest', models.BooleanField(default=True, verbose_name=b'Automatically suggest possible review requests')),
('group', models.OneToOneField(to='group.Group')),
('review_results', models.ManyToManyField(default=ietf.review.models.get_default_review_results, to='name.ReviewResultName')),
('review_types', models.ManyToManyField(default=ietf.review.models.get_default_review_types, to='name.ReviewTypeName')),
],
options={
'verbose_name': 'Review team settings',
'verbose_name_plural': 'Review team settings',
},
),
]
29 changes: 29 additions & 0 deletions ietf/review/migrations/0008_populate_reviewteamsettings.py
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations

def forward(apps, schema_editor):
ReviewTeamSettings = apps.get_model('review','ReviewTeamSettings')
ResultUsedInReviewTeam = apps.get_model('review','ResultUsedInReviewTeam')
TypeUsedInReviewTeam = apps.get_model('review','TypeUsedInReviewTeam')

for group_id in ResultUsedInReviewTeam.objects.values_list('team',flat=True).distinct():
rts = ReviewTeamSettings.objects.create(group_id=group_id)
rts.review_types = TypeUsedInReviewTeam.objects.filter(team_id=group_id).values_list('type',flat=True).distinct()
rts.review_results = ResultUsedInReviewTeam.objects.filter(team_id=group_id).values_list('result',flat=True).distinct()


def reverse(apps, schema_editor):
ReviewTeamSettings = apps.get_model('review','ReviewTeamSettings')
ReviewTeamSettings.objects.all().delete()

class Migration(migrations.Migration):

dependencies = [
('review', '0007_reviewteamsettings'),
]

operations = [
migrations.RunPython(forward, reverse)
]
34 changes: 27 additions & 7 deletions ietf/review/models.py
Expand Up @@ -10,7 +10,7 @@

class ReviewerSettings(models.Model):
"""Keeps track of admin data associated with a reviewer in a team."""
team = models.ForeignKey(Group, limit_choices_to=~models.Q(resultusedinreviewteam=None))
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))
person = models.ForeignKey(Person)
INTERVALS = [
(7, "Once per week"),
Expand All @@ -34,7 +34,7 @@ class Meta:

class ReviewSecretarySettings(models.Model):
"""Keeps track of admin data associated with a secretary in a team."""
team = models.ForeignKey(Group, limit_choices_to=~models.Q(resultusedinreviewteam=None))
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))
person = models.ForeignKey(Person)
remind_days_before_deadline = models.IntegerField(null=True, blank=True, help_text="To get an email reminder in case a reviewer forgets to do an assigned review, enter the number of days before review deadline you want to receive it. Clear the field if you don't want a reminder.")

Expand All @@ -45,7 +45,7 @@ class Meta:
verbose_name_plural = "review secretary settings"

class UnavailablePeriod(models.Model):
team = models.ForeignKey(Group, limit_choices_to=~models.Q(resultusedinreviewteam=None))
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))
person = models.ForeignKey(Person)
start_date = models.DateField(default=datetime.date.today, null=True, help_text="Choose the start date so that you can still do a review if it's assigned just before the start date - this usually means you should mark yourself unavailable for assignment some time before you are actually away.")
end_date = models.DateField(blank=True, null=True, help_text="Leaving the end date blank means that the period continues indefinitely. You can end it later.")
Expand Down Expand Up @@ -76,7 +76,7 @@ def __unicode__(self):
class ReviewWish(models.Model):
"""Reviewer wishes to review a document when it becomes available for review."""
time = models.DateTimeField(default=datetime.datetime.now)
team = models.ForeignKey(Group, limit_choices_to=~models.Q(resultusedinreviewteam=None))
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))
person = models.ForeignKey(Person)
doc = models.ForeignKey(Document)

Expand Down Expand Up @@ -104,7 +104,7 @@ class Meta:
class TypeUsedInReviewTeam(models.Model):
"""Captures that a type name is valid for a given team for new
reviews. """
team = models.ForeignKey(Group, limit_choices_to=~models.Q(resultusedinreviewteam=None))
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))
type = models.ForeignKey(ReviewTypeName)

def __unicode__(self):
Expand All @@ -115,7 +115,7 @@ class Meta:
verbose_name_plural = "review type used in team settings"

class NextReviewerInTeam(models.Model):
team = models.ForeignKey(Group, limit_choices_to=~models.Q(resultusedinreviewteam=None))
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))
next_reviewer = models.ForeignKey(Person)

def __unicode__(self):
Expand All @@ -138,7 +138,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(resultusedinreviewteam=None))
team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=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")
Expand All @@ -156,3 +156,23 @@ class ReviewRequest(models.Model):

def __unicode__(self):
return u"%s review on %s by %s %s" % (self.type, self.doc, self.team, self.state)

def get_default_review_types():
return ReviewTypeName.objects.filter(slug__in=['early','lc','telechat'])

def get_default_review_results():
return ReviewResultName.objects.filter(slug__in=['not-ready', 'right-track', 'almost-ready', 'ready-issues', 'ready-nits', 'ready'])

class ReviewTeamSettings(models.Model):
"""Holds configuration specific to groups that are review teams"""
group = models.OneToOneField(Group)
autosuggest = models.BooleanField(default=True, verbose_name="Automatically suggest possible review requests")
review_types = models.ManyToManyField(ReviewTypeName, default=get_default_review_types)
review_results = models.ManyToManyField(ReviewResultName, default=get_default_review_results)

def __unicode__(self):
return u"%s" % (self.group.acronym,)

class Meta:
verbose_name = "Review team settings"
verbose_name_plural = "Review team settings"
17 changes: 16 additions & 1 deletion ietf/review/resources.py
Expand Up @@ -10,7 +10,7 @@
from ietf.review.models import (ReviewerSettings, ReviewRequest,
ResultUsedInReviewTeam, TypeUsedInReviewTeam,
UnavailablePeriod, ReviewWish, NextReviewerInTeam,
ReviewSecretarySettings)
ReviewSecretarySettings, ReviewTeamSettings )


from ietf.person.resources import PersonResource
Expand Down Expand Up @@ -185,3 +185,18 @@ class Meta:
}
api.review.register(ReviewSecretarySettingsResource())

class ReviewTeamSettingsResource(ModelResource):
group = ToOneField(GroupResource, 'group')
review_types = ToManyField(ReviewTypeNameResource, 'review_types')
review_results = ToManyField(ReviewResultNameResource, 'review_results')
class Meta:
queryset = ReviewTeamSettings.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
filtering = {
"id": ALL,
"autosuggest": ALL,
"review_types": ALL_WITH_RELATIONS,
"review_results": ALL_WITH_RELATIONS,
}
api.review.register(ReviewTeamSettingsResource())

0 comments on commit b24bdb5

Please sign in to comment.