Improve review assignment slightly by sorting reviewers by latest

review - still missing a bunch of factors, and unassignment is now
temporarily gone
 - Legacy-Id: 11531
This commit is contained in:
Ole Laursen 2016-07-07 12:17:55 +00:00
parent 7aff7d3e72
commit 5c8be91b08
4 changed files with 74 additions and 5 deletions

View file

@ -17,7 +17,8 @@ from ietf.person.fields import PersonEmailChoiceField, SearchablePersonField
from ietf.review.utils import (active_review_teams, assign_review_request_to_reviewer,
can_request_review_of_doc, can_manage_review_requests_for_team,
email_review_request_change, make_new_review_request_from_existing,
close_review_request_states, close_review_request)
close_review_request_states, close_review_request,
construct_review_request_assignment_choices)
from ietf.review import mailarch
from ietf.utils.fields import DatepickerDateField
from ietf.utils.text import skip_prefix
@ -212,6 +213,8 @@ class AssignReviewerForm(forms.Form):
f.queryset = f.queryset.filter(role__name="reviewer", role__group=review_req.team)
if review_req.reviewer:
f.initial = review_req.reviewer_id
f.choices = construct_review_request_assignment_choices(f.queryset, review_req.team, review_req)
@login_required
def assign_reviewer(request, name, request_id):

View file

@ -8,7 +8,8 @@ from ietf.review.utils import (can_manage_review_requests_for_team, close_review
extract_revision_ordered_review_requests_for_documents,
assign_review_request_to_reviewer,
close_review_request,
# email_review_request_change, make_new_review_request_from_existing,
construct_review_request_assignment_choices,
# make_new_review_request_from_existing,
suggested_review_requests_for_team)
from ietf.group.utils import get_group_or_404
from ietf.person.fields import PersonEmailChoiceField
@ -58,7 +59,7 @@ class ManageReviewRequestForm(forms.Form):
role__name="reviewer",
role__group=review_req.team,
)
self.fields["reviewer"].choices = construct_review_request_assignment_choices(self.fields["reviewer"].queryset, review_req.team, review_req)
self.fields["reviewer"].widget.attrs["class"] = "form-control input-sm"
if self.is_bound:

View file

@ -11,7 +11,14 @@ class Reviewer(models.Model):
reviewer and team."""
team = models.ForeignKey(Group)
person = models.ForeignKey(Person)
frequency = models.IntegerField(default=30, help_text="Can review every N days")
FREQUENCIES = [
(7, "Once per week"),
(14, "Once per fortnight"),
(30, "Once per month"),
(60, "Once per two months"),
(90, "Once per quarter"),
]
frequency = models.IntegerField(default=30, help_text="Can review every N days", choices=FREQUENCIES)
unavailable_until = models.DateTimeField(blank=True, null=True, help_text="When will this reviewer be available again")
filter_re = models.CharField(max_length=255, blank=True)
skip_next = models.IntegerField(default=0, help_text="Skip the next N review assignments")

View file

@ -2,13 +2,14 @@ import datetime
from collections import defaultdict
from django.contrib.sites.models import Site
from django.db import models
from ietf.group.models import Group, Role
from ietf.doc.models import Document, DocEvent, State, LastCallDocEvent
from ietf.iesg.models import TelechatDate
from ietf.person.models import Person
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, Reviewer
from ietf.utils.mail import send_mail
from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs
@ -273,3 +274,60 @@ def extract_revision_ordered_review_requests_for_documents(queryset, names):
front = next_front
return res
def construct_review_request_assignment_choices(possible_emails, team, review_req=None):
possible_emails = list(possible_emails)
reviewers = { r.person_id: r for r in Reviewer.objects.filter(team=team, person__in=[e.person_id for e in possible_emails]) }
latest_assignment_for_reviewer = dict(ReviewRequest.objects.filter(
reviewer__in=possible_emails,
).values_list("reviewer").annotate(models.Max("time")))
now = datetime.datetime.now()
rankings = []
for e in possible_emails:
reviewer = reviewers.get(e.person_id)
if not reviewer:
reviewer = Reviewer()
days_past = None
latest = latest_assignment_for_reviewer.get(e.pk)
if latest is not None:
days_past = (now - latest).days - reviewer.frequency
# FIXME:
# positive: (Perhaps do these separately? As initial values?)
# has done review of previous rev
# would like to review
# blocks:
# connections to doc + filter_re
# has rejected same request/completed partial review
# is unavailable_until
if days_past is None:
ready_for = "first time"
else:
d = int(round(days_past))
if d > 0:
ready_for = "ready for {} {}".format(d, "day" if d == 1 else "days")
else:
d = -d
ready_for = "frequency exceeded - ready in {} {}".format(d, "day" if d == 1 else "days")
label = "{}: {}".format(e.person, ready_for)
rank = (-100000 if days_past is None else -days_past,)
rankings.append({
"email": e,
"rank": rank,
"label": label,
})
rankings.sort(key=lambda r: r["rank"])
# FIXME: empty choices
return [(r["email"].pk, r["label"]) for r in rankings]