From 426542771aca202a7e7877aa333ec65c3e8ab9f1 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 17 Aug 2016 15:10:39 +0000 Subject: [PATCH] Turn ReviewRequest.deadline into a date field with no time - Legacy-Id: 11801 --- ietf/doc/tests_review.py | 19 +++++++++--------- ietf/doc/views_review.py | 20 +++---------------- ietf/group/tests_review.py | 4 ++-- ietf/group/views.py | 6 +++--- ietf/review/import_from_review_tool.py | 8 ++++---- ietf/review/migrations/0001_initial.py | 13 ++++++------ ietf/review/models.py | 4 ++-- ietf/review/resources.py | 8 ++++---- ietf/review/utils.py | 19 +++++++----------- .../doc/mail/review_request_changed.txt | 2 +- ietf/templates/doc/review/request_review.html | 3 +-- ietf/templates/doc/review/review_request.html | 8 +------- .../group/manage_review_requests.html | 8 ++------ ietf/templates/group/review_requests.html | 19 +++++------------- ietf/utils/test_data.py | 4 ++-- 15 files changed, 53 insertions(+), 92 deletions(-) diff --git a/ietf/doc/tests_review.py b/ietf/doc/tests_review.py index f9d97907d..d43eb02e2 100644 --- a/ietf/doc/tests_review.py +++ b/ietf/doc/tests_review.py @@ -12,7 +12,7 @@ from pyquery import PyQuery import debug # pyflakes:ignore -from ietf.review.models import ReviewRequest, ReviewTeamResult, Reviewer +from ietf.review.models import ReviewRequest, ReviewTeamResult, ReviewerSettings import ietf.review.mailarch from ietf.person.models import Email, Person from ietf.name.models import ReviewResultName, ReviewRequestStateName, ReviewTypeName @@ -51,21 +51,20 @@ class ReviewTests(TestCase): r = self.client.get(url) self.assertEqual(r.status_code, 200) - deadline_date = datetime.date.today() + datetime.timedelta(days=10) + deadline = datetime.date.today() + datetime.timedelta(days=10) # post request r = self.client.post(url, { "type": "early", "team": review_team.pk, - "deadline_date": deadline_date.isoformat(), + "deadline": deadline.isoformat(), "requested_rev": "01", "requested_by": Person.objects.get(user__username="plain").pk, }) self.assertEqual(r.status_code, 302) req = ReviewRequest.objects.get(doc=doc, state="requested") - self.assertEqual(req.deadline.date(), deadline_date) - self.assertEqual(req.deadline.time(), datetime.time(23, 59, 59)) + self.assertEqual(req.deadline, deadline) self.assertEqual(req.team, review_team) self.assertEqual(req.requested_rev, "01") self.assertEqual(doc.latest_event().type, "requested_review") @@ -146,14 +145,14 @@ class ReviewTests(TestCase): team=review_req.team, state=ReviewRequestStateName.objects.get(slug="completed"), reviewed_rev="01", - deadline=datetime.datetime.now() - datetime.timedelta(days=80), + deadline=datetime.date.today() - datetime.timedelta(days=80), reviewer=plain_email, ) - reviewer_obj = Reviewer.objects.get(person__email=plain_email) - reviewer_obj.filter_re = doc.name - reviewer_obj.unavailable_until = datetime.datetime.now() + datetime.timedelta(days=10) - reviewer_obj.save() + reviewer_settings = ReviewerSettings.objects.get(person__email=plain_email) + reviewer_settings.filter_re = doc.name + reviewer_settings.unavailable_until = datetime.datetime.now() + datetime.timedelta(days=10) + reviewer_settings.save() assign_url = urlreverse('ietf.doc.views_review.assign_reviewer', kwargs={ "name": doc.name, "request_id": review_req.pk }) diff --git a/ietf/doc/views_review.py b/ietf/doc/views_review.py index 8116b820b..c43258177 100644 --- a/ietf/doc/views_review.py +++ b/ietf/doc/views_review.py @@ -37,8 +37,7 @@ def clean_doc_revision(doc, rev): class RequestReviewForm(forms.ModelForm): team = forms.ModelMultipleChoiceField(queryset=Group.objects.all(), widget=forms.CheckboxSelectMultiple) - deadline_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={ "autoclose": "1", "start-date": "+0d" }) - deadline_time = forms.TimeField(widget=forms.TextInput(attrs={ 'placeholder': "HH:MM" }), help_text="If time is not specified, end of day is assumed", required=False) + deadline = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={ "autoclose": "1", "start-date": "+0d" }) class Meta: model = ReviewRequest @@ -59,7 +58,6 @@ class RequestReviewForm(forms.ModelForm): 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["deadline"].required = False self.fields["requested_rev"].label = "Document revision" if has_role(user, "Secretariat"): @@ -68,8 +66,8 @@ class RequestReviewForm(forms.ModelForm): self.fields["requested_by"].widget = forms.HiddenInput() self.fields["requested_by"].initial = user.person.pk - def clean_deadline_date(self): - v = self.cleaned_data.get('deadline_date') + def clean_deadline(self): + v = self.cleaned_data.get('deadline') if v < datetime.date.today(): raise forms.ValidationError("Select today or a date in the future.") return v @@ -77,18 +75,6 @@ class RequestReviewForm(forms.ModelForm): def clean_requested_rev(self): return clean_doc_revision(self.doc, self.cleaned_data.get("requested_rev")) - def clean(self): - deadline_date = self.cleaned_data.get('deadline_date') - deadline_time = self.cleaned_data.get('deadline_time', None) - - if deadline_date: - if deadline_time is None: - deadline_time = datetime.time(23, 59, 59) - - self.cleaned_data["deadline"] = datetime.datetime.combine(deadline_date, deadline_time) - - return self.cleaned_data - @login_required def request_review(request, name): doc = get_object_or_404(Document, name=name) diff --git a/ietf/group/tests_review.py b/ietf/group/tests_review.py index 90b2fe2e2..7cda17314 100644 --- a/ietf/group/tests_review.py +++ b/ietf/group/tests_review.py @@ -87,7 +87,7 @@ class ReviewTests(TestCase): doc=review_req1.doc, team=review_req1.team, type_id="early", - deadline=datetime.datetime.combine(datetime.date.today() + datetime.timedelta(days=30), datetime.time(23, 59, 59)), + deadline=datetime.date.today() + datetime.timedelta(days=30), state_id="accepted", reviewer=review_req1.reviewer, requested_by=Person.objects.get(user__username="plain"), @@ -97,7 +97,7 @@ class ReviewTests(TestCase): doc=review_req1.doc, team=review_req1.team, type_id="early", - deadline=datetime.datetime.combine(datetime.date.today() + datetime.timedelta(days=30), datetime.time(23, 59, 59)), + deadline=datetime.date.today() + datetime.timedelta(days=30), state_id="requested", requested_by=Person.objects.get(user__username="plain"), ) diff --git a/ietf/group/views.py b/ietf/group/views.py index 292f32ff1..066e0ae83 100644 --- a/ietf/group/views.py +++ b/ietf/group/views.py @@ -653,10 +653,10 @@ def review_requests(request, acronym, group_type=None): open_review_requests += suggested_review_requests_for_team(group) - now = datetime.datetime.now() + today = datetime.date.today() for r in open_review_requests: - delta = now - r.deadline - r.due = max(0, int(math.ceil(delta.total_seconds() / 3600.0))) + delta = today - r.deadline + r.due = max(0, delta.days()) closed_review_requests = ReviewRequest.objects.filter( team=group, diff --git a/ietf/review/import_from_review_tool.py b/ietf/review/import_from_review_tool.py index 244a5f092..05d940041 100755 --- a/ietf/review/import_from_review_tool.py +++ b/ietf/review/import_from_review_tool.py @@ -16,7 +16,7 @@ django.setup() import datetime from collections import namedtuple from django.db import connections -from ietf.review.models import ReviewRequest, Reviewer, ReviewResultName +from ietf.review.models import ReviewRequest, ReviewerSettings, ReviewResultName from ietf.review.models import ReviewRequestStateName, ReviewTypeName, ReviewTeamResult from ietf.group.models import Group, Role, RoleName from ietf.person.models import Person, Email, Alias @@ -105,7 +105,7 @@ with db_con.cursor() as c: if created: print "created role", unicode(role).encode("utf-8") - reviewer, created = Reviewer.objects.get_or_create( + reviewer, created = ReviewerSettings.objects.get_or_create( team=team, person=email.person, ) @@ -217,7 +217,7 @@ with db_con.cursor() as c: defaults={ "state": states["requested"], "type": type_name, - "deadline": deadline, + "deadline": deadline.date(), "requested_by": system_person, } ) @@ -228,7 +228,7 @@ with db_con.cursor() as c: req.type = type_name req.time = time req.reviewed_rev = reviewed_rev - req.deadline = deadline + req.deadline = deadline.date() req.save() # FIXME: add log entries diff --git a/ietf/review/migrations/0001_initial.py b/ietf/review/migrations/0001_initial.py index a78220cc0..13b5421f1 100644 --- a/ietf/review/migrations/0001_initial.py +++ b/ietf/review/migrations/0001_initial.py @@ -2,12 +2,13 @@ from __future__ import unicode_literals from django.db import models, migrations +import datetime class Migration(migrations.Migration): dependencies = [ - ('name', '0012_insert_review_name_data'), + ('name', '0013_auto_20160623_0621'), ('group', '0008_auto_20160505_0523'), ('person', '0014_auto_20160613_0751'), ('doc', '0012_auto_20160207_0537'), @@ -15,10 +16,10 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Reviewer', + name='ReviewerSettings', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('frequency', models.IntegerField(default=30, help_text=b'Can review every N days')), + ('frequency', models.IntegerField(default=30, help_text=b'Can review every N days', choices=[(7, b'Once per week'), (14, b'Once per fortnight'), (30, b'Once per month'), (61, b'Once per two months'), (91, b'Once per quarter')])), ('unavailable_until', models.DateTimeField(help_text=b'When will this reviewer be available again', null=True, blank=True)), ('filter_re', models.CharField(max_length=255, blank=True)), ('skip_next', models.IntegerField(default=0, help_text=b'Skip the next N review assignments')), @@ -34,15 +35,15 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('old_id', models.IntegerField(help_text=b'ID in previous review system', null=True, blank=True)), - ('time', models.DateTimeField(auto_now_add=True)), - ('deadline', models.DateTimeField()), + ('time', models.DateTimeField(default=datetime.datetime.now)), + ('deadline', models.DateField()), ('requested_rev', models.CharField(help_text=b'Fill in if a specific revision is to be reviewed, e.g. 02', max_length=16, verbose_name=b'requested revision', blank=True)), ('reviewed_rev', models.CharField(max_length=16, verbose_name=b'reviewed revision', blank=True)), ('doc', models.ForeignKey(related_name='review_request_set', to='doc.Document')), + ('requested_by', models.ForeignKey(to='person.Person')), ('result', models.ForeignKey(blank=True, to='name.ReviewResultName', null=True)), ('review', models.OneToOneField(null=True, blank=True, to='doc.Document')), ('reviewer', models.ForeignKey(blank=True, to='person.Email', null=True)), - ('requested_by', models.ForeignKey(to='person.Person')), ('state', models.ForeignKey(to='name.ReviewRequestStateName')), ('team', models.ForeignKey(to='group.Group')), ('type', models.ForeignKey(to='name.ReviewTypeName')), diff --git a/ietf/review/models.py b/ietf/review/models.py index 839edcdb7..7a21e6bbc 100644 --- a/ietf/review/models.py +++ b/ietf/review/models.py @@ -7,7 +7,7 @@ from ietf.group.models import Group from ietf.person.models import Person, Email from ietf.name.models import ReviewTypeName, ReviewRequestStateName, ReviewResultName -class Reviewer(models.Model): +class ReviewerSettings(models.Model): """Keeps track of admin data associated with the reviewer in the particular team. There will be one record for each combination of reviewer and team.""" @@ -50,7 +50,7 @@ class ReviewRequest(models.Model): type = models.ForeignKey(ReviewTypeName) doc = models.ForeignKey(Document, related_name='review_request_set') team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamresult=None)) - deadline = models.DateTimeField() + 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") diff --git a/ietf/review/resources.py b/ietf/review/resources.py index 02aa29d7b..f86145fb4 100644 --- a/ietf/review/resources.py +++ b/ietf/review/resources.py @@ -7,16 +7,16 @@ from tastypie.cache import SimpleCache from ietf import api from ietf.api import ToOneField # pyflakes:ignore -from ietf.review.models import Reviewer, ReviewRequest, ReviewTeamResult +from ietf.review.models import ReviewerSettings, ReviewRequest, ReviewTeamResult from ietf.person.resources import PersonResource from ietf.group.resources import GroupResource -class ReviewerResource(ModelResource): +class ReviewerSettingsResource(ModelResource): team = ToOneField(GroupResource, 'team') person = ToOneField(PersonResource, 'person') class Meta: - queryset = Reviewer.objects.all() + queryset = ReviewerSettings.objects.all() serializer = api.Serializer() cache = SimpleCache() #resource_name = 'reviewer' @@ -29,7 +29,7 @@ class ReviewerResource(ModelResource): "team": ALL_WITH_RELATIONS, "person": ALL_WITH_RELATIONS, } -api.review.register(ReviewerResource()) +api.review.register(ReviewerSettingsResource()) from ietf.doc.resources import DocumentResource from ietf.group.resources import RoleResource, GroupResource diff --git a/ietf/review/utils.py b/ietf/review/utils.py index 2321f3f04..cfdc43fa7 100644 --- a/ietf/review/utils.py +++ b/ietf/review/utils.py @@ -9,7 +9,7 @@ from ietf.doc.models import Document, DocEvent, State, LastCallDocEvent, Documen 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, Reviewer +from ietf.review.models import ReviewRequest, ReviewRequestStateName, ReviewTypeName, ReviewerSettings from ietf.utils.mail import send_mail from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs @@ -151,11 +151,6 @@ def close_review_request(request, review_req, close_state): by=request.user.person, notify_secretary=False, notify_reviewer=True, notify_requested_by=True) def suggested_review_requests_for_team(team): - def fixup_deadline(d): - if d.time() == datetime.time(0): - d = d - datetime.timedelta(seconds=1) # 23:59:59 is treated specially in the view code - return d - system_person = Person.objects.get(name="(System)") seen_deadlines = {} @@ -170,9 +165,9 @@ def suggested_review_requests_for_team(team): last_call_docs = Document.objects.filter(states=State.objects.get(type="draft-iesg", slug="lc", used=True)) last_call_expires = { e.doc_id: e.expires for e in LastCallDocEvent.objects.order_by("time", "id") } for doc in last_call_docs: - deadline = fixup_deadline(last_call_expires.get(doc.pk)) if doc.pk in last_call_expires else datetime.datetime.now() + deadline = last_call_expires[doc.pk].date() if doc.pk in last_call_expires else datetime.date.today() - if deadline > seen_deadlines.get(doc.pk, datetime.datetime.max): + if deadline > seen_deadlines.get(doc.pk, datetime.date.max): continue requests[doc.pk] = ReviewRequest( @@ -200,9 +195,9 @@ def suggested_review_requests_for_team(team): if d not in telechat_dates: continue - deadline = datetime.datetime.combine(d - telechat_deadline_delta, datetime.time(23, 59, 59)) + deadline = d - telechat_deadline_delta - if deadline > seen_deadlines.get(doc.pk, datetime.datetime.max): + if deadline > seen_deadlines.get(doc.pk, datetime.date.max): continue requests[doc.pk] = ReviewRequest( @@ -302,7 +297,7 @@ def make_assignment_choices(email_queryset, review_req): aliases = DocAlias.objects.filter(document=doc).values_list("name", flat=True) - reviewers = { r.person_id: r for r in Reviewer.objects.filter(team=team, person__in=[e.person_id for e in possible_emails]) } + reviewers = { r.person_id: r for r in ReviewerSettings.objects.filter(team=team, person__in=[e.person_id for e in possible_emails]) } # time since past assignment latest_assignment_for_reviewer = dict(ReviewRequest.objects.filter( @@ -347,7 +342,7 @@ def make_assignment_choices(email_queryset, review_req): for e in possible_emails: reviewer = reviewers.get(e.person_id) if not reviewer: - reviewer = Reviewer() + reviewer = ReviewerSettings() explanations = [] scores = [] # build up score in separate independent components diff --git a/ietf/templates/doc/mail/review_request_changed.txt b/ietf/templates/doc/mail/review_request_changed.txt index 88e1d151f..190911b8f 100644 --- a/ietf/templates/doc/mail/review_request_changed.txt +++ b/ietf/templates/doc/mail/review_request_changed.txt @@ -1,6 +1,6 @@ {% autoescape off %} {{ review_req.type.name }} review of: {{ review_req.doc.name }} ({% if review_req.requested_rev %}rev. {{ review_req.requested_rev }}{% else %}no specific version{% endif %}) - Deadline: {% if review_req.deadline|date:"H:i" != "23:59" %}{{ review_req.deadline|date:"Y-m-d H:i" }}{% else %}{{ review_req.deadline|date:"Y-m-d" }}{% endif %} + Deadline: {{ review_req.deadline|date:"Y-m-d" }} {{ review_req_url }} diff --git a/ietf/templates/doc/review/request_review.html b/ietf/templates/doc/review/request_review.html index 75c50fe2b..39625ca64 100644 --- a/ietf/templates/doc/review/request_review.html +++ b/ietf/templates/doc/review/request_review.html @@ -33,8 +33,7 @@ {% bootstrap_field form.requested_by layout="horizontal" %} {% bootstrap_field form.type layout="horizontal" %} {% bootstrap_field form.team layout="horizontal" %} - {% bootstrap_field form.deadline_date layout="horizontal" %} - {% bootstrap_field form.deadline_time layout="horizontal" %} + {% bootstrap_field form.deadline layout="horizontal" %} {% bootstrap_field form.requested_rev layout="horizontal" %} {% buttons %} diff --git a/ietf/templates/doc/review/review_request.html b/ietf/templates/doc/review/review_request.html index 1e182c83a..0b53d1130 100644 --- a/ietf/templates/doc/review/review_request.html +++ b/ietf/templates/doc/review/review_request.html @@ -50,13 +50,7 @@ Deadline - - {% if review_req.deadline|date:"H:i" != "23:59" %} - {{ review_req.deadline|date:"Y-m-d H:i" }} - {% else %} - {{ review_req.deadline|date:"Y-m-d" }} - {% endif %} - + {{ review_req.deadline|date:"Y-m-d" }} diff --git a/ietf/templates/group/manage_review_requests.html b/ietf/templates/group/manage_review_requests.html index 5b9af4042..66ad84e29 100644 --- a/ietf/templates/group/manage_review_requests.html +++ b/ietf/templates/group/manage_review_requests.html @@ -47,12 +47,8 @@ {{ r.type.name }} {% if r.time %}{{ r.time|date:"Y-m-d" }}{% else %}auto-suggested{% endif %} - {% if r.deadline|date:"H:i" != "23:59" %} - {{ r.deadline|date:"Y-m-d H:i" }} - {% else %} - {{ r.deadline|date:"Y-m-d" }} - {% endif %} - {% if r.due %}{{ r.due }} hour{{ r.due|pluralize }}{% endif %} + {{ r.deadline|date:"Y-m-d" }} + {% if r.due %}{{ r.due }} day{{ r.due|pluralize }}{% endif %} {% if r.reviewer %} diff --git a/ietf/templates/group/review_requests.html b/ietf/templates/group/review_requests.html index 38a75e20d..eccb631eb 100644 --- a/ietf/templates/group/review_requests.html +++ b/ietf/templates/group/review_requests.html @@ -33,12 +33,8 @@ {{ r.type.name }} {% if r.time %}{{ r.time|date:"Y-m-d" }}{% else %}auto-suggested{% endif %} - {% if r.deadline|date:"H:i" != "23:59" %} - {{ r.deadline|date:"Y-m-d H:i" }} - {% else %} - {{ r.deadline|date:"Y-m-d" }} - {% endif %} - {% if r.due %}{{ r.due }} hour{{ r.due|pluralize }}{% endif %} + {{ r.deadline|date:"Y-m-d" }} + {% if r.due %}{{ r.due }} day{{ r.due|pluralize }}{% endif %} {% if r.reviewer %} @@ -86,13 +82,7 @@ {{ r.doc.name }}{% if r.requested_rev %}-{{ r.requested_rev }}{% endif %} {{ r.type }} {{ r.time|date:"Y-m-d" }} - - {% if r.deadline|date:"H:i" != "23:59" %} - {{ r.deadline|date:"Y-m-d H:i" }} - {% else %} - {{ r.deadline|date:"Y-m-d" }} - {% endif %} - + {{ r.deadline|date:"Y-m-d" }} {% if r.reviewer %} {{ r.reviewer.person }} @@ -104,7 +94,8 @@ {% if r.result %} {{ r.result.name }} - {% endif %} + {% endif %} + {% endfor %} diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index d2ed4e20a..876d9cbb5 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -13,7 +13,7 @@ from ietf.ipr.models import HolderIprDisclosure, IprDocRel, IprDisclosureStateNa from ietf.meeting.models import Meeting from ietf.name.models import StreamName, DocRelationshipName from ietf.person.models import Person, Email -from ietf.review.models import ReviewRequest, Reviewer, ReviewResultName, ReviewTeamResult +from ietf.review.models import ReviewRequest, ReviewerSettings, ReviewResultName, ReviewTeamResult def create_person(group, role_name, name=None, username=None, email_address=None, password=None): """Add person/user/email and role.""" @@ -367,7 +367,7 @@ def make_review_data(doc): p = Person.objects.get(user__username="plain") email = p.email_set.first() Role.objects.create(name_id="reviewer", person=p, email=email, group=team) - Reviewer.objects.create(team=team, person=p, frequency=14, skip_next=0) + ReviewerSettings.objects.create(team=team, person=p, frequency=14, skip_next=0) review_req = ReviewRequest.objects.create( doc=doc,