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:
parent
7aff7d3e72
commit
5c8be91b08
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue