Commit ready for merge. - Legacy-Id: 17069
This commit is contained in:
parent
6e55f26dbd
commit
65d84155b6
|
@ -5,6 +5,7 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import io
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import datetime
|
||||
|
@ -12,6 +13,7 @@ import requests
|
|||
import email.utils
|
||||
|
||||
from django.utils.http import is_safe_url
|
||||
from simple_history.utils import update_change_reason
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -135,12 +137,15 @@ def request_review(request, name):
|
|||
review_req.team = team
|
||||
review_req.save()
|
||||
|
||||
descr = "Requested {} review by {}".format(review_req.type.name,
|
||||
review_req.team.acronym.upper())
|
||||
update_change_reason(review_req, descr)
|
||||
ReviewRequestDocEvent.objects.create(
|
||||
type="requested_review",
|
||||
doc=doc,
|
||||
rev=doc.rev,
|
||||
by=request.user.person,
|
||||
desc="Requested {} review by {}".format(review_req.type.name, review_req.team.acronym.upper()),
|
||||
desc=descr,
|
||||
time=review_req.time,
|
||||
review_request=review_req,
|
||||
state=None,
|
||||
|
@ -226,12 +231,17 @@ def review_request(request, name, request_id):
|
|||
if assignment.can_accept_reviewer_assignment:
|
||||
assignment.state = ReviewAssignmentStateName.objects.get(slug="accepted")
|
||||
assignment.save()
|
||||
update_change_reason(assignment, 'Assignment for {} accepted'.format(assignment.reviewer.person))
|
||||
return redirect(review_request, name=review_req.doc.name, request_id=review_req.pk)
|
||||
|
||||
wg_chairs = None
|
||||
if review_req.doc.group:
|
||||
wg_chairs = [role.person for role in review_req.doc.group.role_set.filter(name__slug='chair')]
|
||||
|
||||
history = list(review_req.history.all())
|
||||
history += itertools.chain(*[list(r.history.all()) for r in review_req.reviewassignment_set.all()])
|
||||
history.sort(key=lambda h: h.history_date, reverse=True)
|
||||
|
||||
return render(request, 'doc/review/review_request.html', {
|
||||
'doc': doc,
|
||||
'review_req': review_req,
|
||||
|
@ -241,6 +251,7 @@ def review_request(request, name, request_id):
|
|||
'can_edit_deadline': can_edit_deadline,
|
||||
'assignments': assignments,
|
||||
'wg_chairs': wg_chairs,
|
||||
'history': history,
|
||||
})
|
||||
|
||||
|
||||
|
@ -348,16 +359,18 @@ def reject_reviewer_assignment(request, name, assignment_id):
|
|||
review_assignment.state = ReviewAssignmentStateName.objects.get(slug="rejected")
|
||||
review_assignment.save()
|
||||
|
||||
descr = "Assignment of request for {} review by {} to {} was rejected".format(
|
||||
review_assignment.review_request.type.name,
|
||||
review_assignment.review_request.team.acronym.upper(),
|
||||
review_assignment.reviewer.person
|
||||
)
|
||||
update_change_reason(review_assignment, descr)
|
||||
ReviewAssignmentDocEvent.objects.create(
|
||||
type="closed_review_assignment",
|
||||
doc=review_assignment.review_request.doc,
|
||||
rev=review_assignment.review_request.doc.rev,
|
||||
by=request.user.person,
|
||||
desc="Assignment of request for {} review by {} to {} was rejected".format(
|
||||
review_assignment.review_request.type.name,
|
||||
review_assignment.review_request.team.acronym.upper(),
|
||||
review_assignment.reviewer.person,
|
||||
),
|
||||
desc=descr,
|
||||
review_assignment=review_assignment,
|
||||
state=review_assignment.state,
|
||||
)
|
||||
|
@ -393,17 +406,17 @@ def withdraw_reviewer_assignment(request, name, assignment_id):
|
|||
if request.method == "POST" and request.POST.get("action") == "withdraw":
|
||||
review_assignment.state_id = 'withdrawn'
|
||||
review_assignment.save()
|
||||
|
||||
descr = "Assignment of request for {} review by {} to {} was withdrawn".format(
|
||||
review_assignment.review_request.type.name,
|
||||
review_assignment.review_request.team.acronym.upper(),
|
||||
review_assignment.reviewer.person, )
|
||||
update_change_reason(review_assignment, descr)
|
||||
ReviewAssignmentDocEvent.objects.create(
|
||||
type="closed_review_assignment",
|
||||
doc=review_assignment.review_request.doc,
|
||||
rev=review_assignment.review_request.doc.rev,
|
||||
by=request.user.person,
|
||||
desc="Assignment of request for {} review by {} to {} was withdrawn".format(
|
||||
review_assignment.review_request.type.name,
|
||||
review_assignment.review_request.team.acronym.upper(),
|
||||
review_assignment.reviewer.person,
|
||||
),
|
||||
desc=descr,
|
||||
review_assignment=review_assignment,
|
||||
state=review_assignment.state,
|
||||
)
|
||||
|
@ -431,16 +444,17 @@ def mark_reviewer_assignment_no_response(request, name, assignment_id):
|
|||
review_assignment.state_id = 'no-response'
|
||||
review_assignment.save()
|
||||
|
||||
descr = "Assignment of request for {} review by {} to {} was marked no-response".format(
|
||||
review_assignment.review_request.type.name,
|
||||
review_assignment.review_request.team.acronym.upper(),
|
||||
review_assignment.reviewer.person)
|
||||
update_change_reason(review_assignment, descr)
|
||||
ReviewAssignmentDocEvent.objects.create(
|
||||
type="closed_review_assignment",
|
||||
doc=review_assignment.review_request.doc,
|
||||
rev=review_assignment.review_request.doc.rev,
|
||||
by=request.user.person,
|
||||
desc="Assignment of request for {} review by {} to {} was marked no-response".format(
|
||||
review_assignment.review_request.type.name,
|
||||
review_assignment.review_request.team.acronym.upper(),
|
||||
review_assignment.reviewer.person,
|
||||
),
|
||||
desc=descr,
|
||||
review_assignment=review_assignment,
|
||||
state=review_assignment.state,
|
||||
)
|
||||
|
@ -737,7 +751,7 @@ def complete_review(request, name, assignment_id=None, acronym=None):
|
|||
assignment.review = review
|
||||
assignment.completed_on = completion_datetime
|
||||
assignment.save()
|
||||
|
||||
|
||||
need_to_email_review = review_submission != "link" and assignment.review_request.team.list_email and not revising_review
|
||||
|
||||
submitted_on_different_date = completion_datetime.date() != datetime.date.today()
|
||||
|
@ -748,6 +762,7 @@ def complete_review(request, name, assignment_id=None, acronym=None):
|
|||
assignment.result.name,
|
||||
assignment.reviewer.person,
|
||||
)
|
||||
update_change_reason(assignment, desc)
|
||||
if need_to_email_review:
|
||||
desc += " " + "Sent review to list."
|
||||
if revising_review:
|
||||
|
|
|
@ -91,7 +91,8 @@ from ietf.meeting.helpers import get_meeting
|
|||
from ietf.meeting.utils import group_sessions
|
||||
from ietf.name.models import GroupTypeName, StreamName
|
||||
from ietf.person.models import Email
|
||||
from ietf.review.models import ReviewRequest, ReviewAssignment, ReviewerSettings, ReviewSecretarySettings
|
||||
from ietf.review.models import ReviewRequest, ReviewAssignment, ReviewerSettings, \
|
||||
ReviewSecretarySettings, UnavailablePeriod
|
||||
from ietf.review.utils import (can_manage_review_requests_for_team,
|
||||
can_access_review_stats_for_team,
|
||||
|
||||
|
@ -1754,6 +1755,7 @@ def change_reviewer_settings(request, acronym, reviewer_email, group_type=None):
|
|||
period.team = group
|
||||
period.person = reviewer
|
||||
period.save()
|
||||
update_change_reason(period, "Added unavailability period: {}".format(period))
|
||||
|
||||
today = datetime.date.today()
|
||||
|
||||
|
@ -1794,6 +1796,7 @@ def change_reviewer_settings(request, acronym, reviewer_email, group_type=None):
|
|||
for period in unavailable_periods:
|
||||
if str(period.pk) == period_id:
|
||||
period.delete()
|
||||
update_change_reason(period, "Removed unavailability period: {}".format(period))
|
||||
|
||||
today = datetime.date.today()
|
||||
|
||||
|
@ -1821,6 +1824,7 @@ def change_reviewer_settings(request, acronym, reviewer_email, group_type=None):
|
|||
if not period.end_date and period.end_form.is_valid():
|
||||
period.end_date = period.end_form.cleaned_data["end_date"]
|
||||
period.save()
|
||||
update_change_reason(period, "Set end date of unavailability period: {}".format(period))
|
||||
|
||||
msg = "Set end date of unavailable period: {} - {} ({})".format(
|
||||
period.start_date.isoformat() if period.start_date else "indefinite",
|
||||
|
@ -1840,6 +1844,7 @@ def change_reviewer_settings(request, acronym, reviewer_email, group_type=None):
|
|||
'settings_form': settings_form,
|
||||
'period_form': period_form,
|
||||
'unavailable_periods': unavailable_periods,
|
||||
'unavailable_periods_history': UnavailablePeriod.history.filter(person=reviewer, team=group),
|
||||
'reviewersettings': settings,
|
||||
})
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class ReviewSecretarySettingsAdmin(admin.ModelAdmin):
|
|||
raw_id_fields = ['team', 'person']
|
||||
admin.site.register(ReviewSecretarySettings, ReviewSecretarySettingsAdmin)
|
||||
|
||||
class UnavailablePeriodAdmin(admin.ModelAdmin):
|
||||
class UnavailablePeriodAdmin(simple_history.admin.SimpleHistoryAdmin):
|
||||
list_display = ["person", "team", "start_date", "end_date", "availability", "reason"]
|
||||
list_display_links = ["person"]
|
||||
list_filter = ["team"]
|
||||
|
@ -56,7 +56,7 @@ class NextReviewerInTeamAdmin(admin.ModelAdmin):
|
|||
|
||||
admin.site.register(NextReviewerInTeam, NextReviewerInTeamAdmin)
|
||||
|
||||
class ReviewRequestAdmin(admin.ModelAdmin):
|
||||
class ReviewRequestAdmin(simple_history.admin.SimpleHistoryAdmin):
|
||||
list_display = ["doc", "time", "type", "team", "deadline"]
|
||||
list_display_links = ["doc"]
|
||||
list_filter = ["team", "type", "state"]
|
||||
|
@ -67,7 +67,7 @@ class ReviewRequestAdmin(admin.ModelAdmin):
|
|||
|
||||
admin.site.register(ReviewRequest, ReviewRequestAdmin)
|
||||
|
||||
class ReviewAssignmentAdmin(admin.ModelAdmin):
|
||||
class ReviewAssignmentAdmin(simple_history.admin.SimpleHistoryAdmin):
|
||||
list_display = ["review_request", "reviewer", "assigned_on", "result"]
|
||||
list_filter = ["result", "state"]
|
||||
ordering = ["-id"]
|
||||
|
|
224
ietf/review/migrations/0017_add_additional_history.py
Normal file
224
ietf/review/migrations/0017_add_additional_history.py
Normal file
|
@ -0,0 +1,224 @@
|
|||
# Copyright The IETF Trust 2016-2019, All Rights Reserved
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.23 on 2019-11-19 04:36
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import ietf.utils.models
|
||||
import ietf.utils.validators
|
||||
import simple_history.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('person', '0010_auto_20191119_0414'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('doc', '0024_auto_20191119_0430'),
|
||||
('name', '0007_auto_20191119_0430'),
|
||||
('group', '0020_auto_20191119_0414'),
|
||||
('review', '0016_add_review_team_remind_days_unconfirmed_assignments'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalReviewAssignment',
|
||||
fields=[
|
||||
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('history_change_reason', models.TextField(null=True)),
|
||||
('assigned_on', models.DateTimeField(blank=True, null=True)),
|
||||
('completed_on', models.DateTimeField(blank=True, null=True)),
|
||||
('reviewed_rev', models.CharField(blank=True, max_length=16, verbose_name='reviewed revision')),
|
||||
('mailarch_url', models.URLField(blank=True, null=True)),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField()),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('result', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='name.ReviewResultName')),
|
||||
('review', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='doc.Document')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical review assignment',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': 'history_date',
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalReviewRequest',
|
||||
fields=[
|
||||
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('history_change_reason', models.TextField(null=True)),
|
||||
('time', models.DateTimeField(default=datetime.datetime.now)),
|
||||
('deadline', models.DateField()),
|
||||
('requested_rev', models.CharField(blank=True, help_text='Fill in if a specific revision is to be reviewed, e.g. 02', max_length=16, verbose_name='requested revision')),
|
||||
('comment', models.TextField(blank=True, default='', help_text='Provide any additional information to show to the review team secretary and reviewer', max_length=2048, verbose_name="Requester's comments and instructions")),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField()),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('doc', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='doc.Document')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('requested_by', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='person.Person')),
|
||||
('state', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='name.ReviewRequestStateName')),
|
||||
('team', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='group.Group')),
|
||||
('type', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='name.ReviewTypeName')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical review request',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': 'history_date',
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalUnavailablePeriod',
|
||||
fields=[
|
||||
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('history_change_reason', models.TextField(null=True)),
|
||||
('start_date', models.DateField(default=datetime.date.today, 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. The default is today.", null=True)),
|
||||
('end_date', models.DateField(blank=True, help_text='Leaving the end date blank means that the period continues indefinitely. You can end it later.', null=True)),
|
||||
('availability', models.CharField(choices=[('canfinish', 'Can do follow-ups'), ('unavailable', 'Completely unavailable')], max_length=30)),
|
||||
('reason', models.TextField(blank=True, default='', help_text="Provide (for the secretary's benefit) the reason why the review is unavailable", max_length=2048, verbose_name='Reason why reviewer is unavailable (Optional)')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField()),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('person', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='person.Person')),
|
||||
('team', ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='group.Group')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical unavailable period',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': 'history_date',
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalreviewersettings',
|
||||
name='expertise',
|
||||
field=models.TextField(blank=True, default='', help_text="Describe the reviewer's expertise in this team's area", max_length=2048, verbose_name="Reviewer's expertise in this team's area"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalreviewersettings',
|
||||
name='filter_re',
|
||||
field=models.CharField(blank=True, help_text='Draft names matching this regular expression should not be assigned', max_length=255, validators=[ietf.utils.validators.RegexStringValidator()], verbose_name='Filter regexp'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalreviewersettings',
|
||||
name='min_interval',
|
||||
field=models.IntegerField(blank=True, choices=[(7, 'Once per week'), (14, 'Once per fortnight'), (30, 'Once per month'), (61, 'Once per two months'), (91, 'Once per quarter')], null=True, verbose_name='Can review at most'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalreviewersettings',
|
||||
name='remind_days_before_deadline',
|
||||
field=models.IntegerField(blank=True, help_text="To get an email reminder in case you forget 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 this reminder.", null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalreviewersettings',
|
||||
name='remind_days_open_reviews',
|
||||
field=models.PositiveIntegerField(blank=True, help_text="To get a periodic email reminder of all your open reviews, enter the number of days between these reminders. Clear the field if you don't want these reminders.", null=True, verbose_name='Periodic reminder of open reviews every X days'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='historicalreviewersettings',
|
||||
name='skip_next',
|
||||
field=models.IntegerField(default=0, verbose_name='Skip next assignments'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewassignment',
|
||||
name='reviewed_rev',
|
||||
field=models.CharField(blank=True, max_length=16, verbose_name='reviewed revision'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewersettings',
|
||||
name='expertise',
|
||||
field=models.TextField(blank=True, default='', help_text="Describe the reviewer's expertise in this team's area", max_length=2048, verbose_name="Reviewer's expertise in this team's area"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewersettings',
|
||||
name='filter_re',
|
||||
field=models.CharField(blank=True, help_text='Draft names matching this regular expression should not be assigned', max_length=255, validators=[ietf.utils.validators.RegexStringValidator()], verbose_name='Filter regexp'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewersettings',
|
||||
name='min_interval',
|
||||
field=models.IntegerField(blank=True, choices=[(7, 'Once per week'), (14, 'Once per fortnight'), (30, 'Once per month'), (61, 'Once per two months'), (91, 'Once per quarter')], null=True, verbose_name='Can review at most'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewersettings',
|
||||
name='remind_days_before_deadline',
|
||||
field=models.IntegerField(blank=True, help_text="To get an email reminder in case you forget 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 this reminder.", null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewersettings',
|
||||
name='skip_next',
|
||||
field=models.IntegerField(default=0, verbose_name='Skip next assignments'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewrequest',
|
||||
name='comment',
|
||||
field=models.TextField(blank=True, default='', help_text='Provide any additional information to show to the review team secretary and reviewer', max_length=2048, verbose_name="Requester's comments and instructions"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewrequest',
|
||||
name='requested_rev',
|
||||
field=models.CharField(blank=True, help_text='Fill in if a specific revision is to be reviewed, e.g. 02', max_length=16, verbose_name='requested revision'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewsecretarysettings',
|
||||
name='remind_days_before_deadline',
|
||||
field=models.IntegerField(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.", null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewteamsettings',
|
||||
name='autosuggest',
|
||||
field=models.BooleanField(default=True, verbose_name='Automatically suggest possible review requests'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewteamsettings',
|
||||
name='remind_days_unconfirmed_assignments',
|
||||
field=models.PositiveIntegerField(blank=True, help_text="To send a periodic email reminder to reviewers of review assignments they have neither accepted nor rejected, enter the number of days between these reminders. Clear the field if you don't want these reminders to be sent.", null=True, verbose_name='Periodic reminder of not yet accepted or rejected review assignments to reviewer every X days'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='reviewteamsettings',
|
||||
name='secr_mail_alias',
|
||||
field=models.CharField(blank=True, help_text='Email alias for all of the review team secretaries', max_length=255, verbose_name='Email alias for all of the review team secretaries'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='unavailableperiod',
|
||||
name='availability',
|
||||
field=models.CharField(choices=[('canfinish', 'Can do follow-ups'), ('unavailable', 'Completely unavailable')], max_length=30),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='unavailableperiod',
|
||||
name='end_date',
|
||||
field=models.DateField(blank=True, help_text='Leaving the end date blank means that the period continues indefinitely. You can end it later.', null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='unavailableperiod',
|
||||
name='reason',
|
||||
field=models.TextField(blank=True, default='', help_text="Provide (for the secretary's benefit) the reason why the review is unavailable", max_length=2048, verbose_name='Reason why reviewer is unavailable (Optional)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='unavailableperiod',
|
||||
name='start_date',
|
||||
field=models.DateField(default=datetime.date.today, 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. The default is today.", null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalreviewassignment',
|
||||
name='review_request',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='review.ReviewRequest'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalreviewassignment',
|
||||
name='reviewer',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='person.Email'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalreviewassignment',
|
||||
name='state',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='name.ReviewAssignmentStateName'),
|
||||
),
|
||||
]
|
|
@ -61,6 +61,7 @@ class ReviewSecretarySettings(models.Model):
|
|||
|
||||
@python_2_unicode_compatible
|
||||
class UnavailablePeriod(models.Model):
|
||||
history = HistoricalRecords(history_change_reason_field=models.TextField(null=True))
|
||||
team = ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))
|
||||
person = 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. The default is today.")
|
||||
|
@ -120,6 +121,7 @@ class NextReviewerInTeam(models.Model):
|
|||
@python_2_unicode_compatible
|
||||
class ReviewRequest(models.Model):
|
||||
"""Represents a request for a review and the process it goes through."""
|
||||
history = HistoricalRecords(history_change_reason_field=models.TextField(null=True))
|
||||
state = ForeignKey(ReviewRequestStateName)
|
||||
|
||||
# Fields filled in on the initial record creation - these
|
||||
|
@ -145,6 +147,7 @@ class ReviewRequest(models.Model):
|
|||
@python_2_unicode_compatible
|
||||
class ReviewAssignment(models.Model):
|
||||
""" One of possibly many reviews assigned in response to a ReviewRequest """
|
||||
history = HistoricalRecords(history_change_reason_field=models.TextField(null=True))
|
||||
review_request = ForeignKey(ReviewRequest)
|
||||
state = ForeignKey(ReviewAssignmentStateName)
|
||||
reviewer = ForeignKey(Email)
|
||||
|
|
|
@ -14,7 +14,8 @@ from ietf.api import ToOneField # pyflakes:ignore
|
|||
from ietf.review.models import (ReviewerSettings, ReviewRequest, ReviewAssignment,
|
||||
UnavailablePeriod, ReviewWish, NextReviewerInTeam,
|
||||
ReviewSecretarySettings, ReviewTeamSettings,
|
||||
HistoricalReviewerSettings )
|
||||
HistoricalReviewerSettings, HistoricalUnavailablePeriod,
|
||||
HistoricalReviewRequest, HistoricalReviewAssignment)
|
||||
|
||||
|
||||
from ietf.person.resources import PersonResource
|
||||
|
@ -70,6 +71,33 @@ class ReviewRequestResource(ModelResource):
|
|||
api.review.register(ReviewRequestResource())
|
||||
|
||||
|
||||
class HistoricalReviewRequestResource(ModelResource):
|
||||
state = ToOneField(ReviewRequestStateNameResource, 'state')
|
||||
type = ToOneField(ReviewTypeNameResource, 'type')
|
||||
doc = ToOneField(DocumentResource, 'doc')
|
||||
team = ToOneField(GroupResource, 'team')
|
||||
requested_by = ToOneField(PersonResource, 'requested_by')
|
||||
class Meta:
|
||||
queryset = HistoricalReviewRequest.objects.all()
|
||||
serializer = api.Serializer()
|
||||
cache = SimpleCache()
|
||||
#resource_name = 'reviewrequest'
|
||||
ordering = ['id', ]
|
||||
filtering = {
|
||||
"id": ALL,
|
||||
"time": ALL,
|
||||
"deadline": ALL,
|
||||
"requested_rev": ALL,
|
||||
"comment": ALL,
|
||||
"state": ALL_WITH_RELATIONS,
|
||||
"type": ALL_WITH_RELATIONS,
|
||||
"doc": ALL_WITH_RELATIONS,
|
||||
"team": ALL_WITH_RELATIONS,
|
||||
"requested_by": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.review.register(HistoricalReviewRequestResource())
|
||||
|
||||
|
||||
from ietf.person.resources import PersonResource
|
||||
from ietf.group.resources import GroupResource
|
||||
class UnavailablePeriodResource(ModelResource):
|
||||
|
@ -93,6 +121,26 @@ class UnavailablePeriodResource(ModelResource):
|
|||
api.review.register(UnavailablePeriodResource())
|
||||
|
||||
|
||||
class HistoricalUnavailablePeriodResource(ModelResource):
|
||||
team = ToOneField(GroupResource, 'team')
|
||||
person = ToOneField(PersonResource, 'person')
|
||||
class Meta:
|
||||
queryset = HistoricalUnavailablePeriod.objects.all()
|
||||
serializer = api.Serializer()
|
||||
cache = SimpleCache()
|
||||
ordering = ['id', ]
|
||||
filtering = {
|
||||
"id": ALL,
|
||||
"start_date": ALL,
|
||||
"end_date": ALL,
|
||||
"availability": ALL,
|
||||
"reason": ALL,
|
||||
"team": ALL_WITH_RELATIONS,
|
||||
"person": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.review.register(HistoricalUnavailablePeriodResource())
|
||||
|
||||
|
||||
from ietf.person.resources import PersonResource
|
||||
from ietf.group.resources import GroupResource
|
||||
from ietf.doc.resources import DocumentResource
|
||||
|
@ -236,3 +284,30 @@ class ReviewAssignmentResource(ModelResource):
|
|||
"result": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.review.register(ReviewAssignmentResource())
|
||||
|
||||
|
||||
class HistoricalReviewAssignmentResource(ModelResource):
|
||||
review_request = ToOneField(ReviewRequestResource, 'review_request')
|
||||
state = ToOneField(ReviewAssignmentStateNameResource, 'state')
|
||||
reviewer = ToOneField(EmailResource, 'reviewer')
|
||||
review = ToOneField(DocumentResource, 'review', null=True)
|
||||
result = ToOneField(ReviewResultNameResource, 'result', null=True)
|
||||
class Meta:
|
||||
queryset = HistoricalReviewAssignment.objects.all()
|
||||
serializer = api.Serializer()
|
||||
cache = SimpleCache()
|
||||
#resource_name = 'reviewassignment'
|
||||
ordering = ['id', ]
|
||||
filtering = {
|
||||
"id": ALL,
|
||||
"assigned_on": ALL,
|
||||
"completed_on": ALL,
|
||||
"reviewed_rev": ALL,
|
||||
"mailarch_url": ALL,
|
||||
"review_request": ALL_WITH_RELATIONS,
|
||||
"state": ALL_WITH_RELATIONS,
|
||||
"reviewer": ALL_WITH_RELATIONS,
|
||||
"review": ALL_WITH_RELATIONS,
|
||||
"result": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.review.register(HistoricalReviewAssignmentResource())
|
||||
|
|
|
@ -16,6 +16,7 @@ from django.template.defaultfilters import pluralize
|
|||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.contrib.sites.models import Site
|
||||
from simple_history.utils import update_change_reason
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
from ietf.dbtemplate.models import DBTemplate
|
||||
|
@ -425,16 +426,17 @@ def assign_review_request_to_reviewer(request, review_req, reviewer, add_skip=Fa
|
|||
if reviewer:
|
||||
possibly_advance_next_reviewer_for_team(review_req.team, reviewer.person_id, add_skip)
|
||||
|
||||
descr = "Request for {} review by {} is assigned to {}".format(
|
||||
review_req.type.name,
|
||||
review_req.team.acronym.upper(),
|
||||
reviewer.person if reviewer else "(None)")
|
||||
update_change_reason(assignment, descr)
|
||||
ReviewRequestDocEvent.objects.create(
|
||||
type="assigned_review_request",
|
||||
doc=review_req.doc,
|
||||
rev=review_req.doc.rev,
|
||||
by=request.user.person,
|
||||
desc="Request for {} review by {} is assigned to {}".format(
|
||||
review_req.type.name,
|
||||
review_req.team.acronym.upper(),
|
||||
reviewer.person if reviewer else "(None)",
|
||||
),
|
||||
desc=descr,
|
||||
review_request=review_req,
|
||||
state_id='assigned',
|
||||
)
|
||||
|
@ -526,12 +528,13 @@ def close_review_request(request, review_req, close_state, close_comment=''):
|
|||
# if close_state.slug == "no-review-version":
|
||||
# review_req.reviewed_rev = review_req.requested_rev or review_req.doc.rev # save rev for later reference
|
||||
review_req.save()
|
||||
|
||||
|
||||
if not suggested_req:
|
||||
descr = "Closed request for {} review by {} with state '{}'".format(
|
||||
review_req.type.name, review_req.team.acronym.upper(), close_state.name)
|
||||
if close_comment:
|
||||
descr += ': ' + close_comment
|
||||
update_change_reason(review_req, descr)
|
||||
ReviewRequestDocEvent.objects.create(
|
||||
type="closed_review_request",
|
||||
doc=review_req.doc,
|
||||
|
|
|
@ -16,5 +16,25 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<h3>History</h3>
|
||||
<div id="history">
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr>
|
||||
<th class="col-md-1">Date</th>
|
||||
<th class="col-md-1">By</th>
|
||||
<th class="col-md-10">Description</th>
|
||||
</tr>
|
||||
{% for h in history %}
|
||||
{% if h.history_change_reason %}
|
||||
<tr>
|
||||
<td>{{ h.history_date|date }}</td>
|
||||
<td>{{ h.history_user.person }}</td>
|
||||
<td>{{ h.history_change_reason }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<h3>History</h3>
|
||||
<h3>History of settings</h3>
|
||||
|
||||
<div id="history">
|
||||
<table class="table table-condensed table-striped">
|
||||
|
@ -101,6 +101,23 @@
|
|||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<h3>History of unavailable periods</h3>
|
||||
<div id="history">
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr>
|
||||
<th class="col-md-1">Date</th>
|
||||
<th class="col-md-1">By</th>
|
||||
<th class="col-md-10">Description</th>
|
||||
</tr>
|
||||
{% for h in unavailable_periods_history.all %}
|
||||
<tr>
|
||||
<td>{{h.history_date|date}}</td>
|
||||
<td>{{h.history_user.person}}</td>
|
||||
<td>{{h.history_change_reason}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<p style="padding-top: 2em;">
|
||||
<a href="{{ back_url }}" class="btn btn-default">Back</a>
|
||||
</p>
|
||||
|
|
Loading…
Reference in a new issue