Track slide submissions even after acceptance or rejection. Fixes #2835. Commit ready for merge.

- Legacy-Id: 18371
This commit is contained in:
Mark J. Donnelly 2020-08-13 13:48:01 +00:00
parent b6c3a8f1f8
commit 21ba67fb79
9 changed files with 176 additions and 20 deletions

View file

@ -0,0 +1,28 @@
# Generated by Django 2.2.14 on 2020-08-03 11:53
from django.db import migrations
import django.db.models.deletion
import ietf.utils.models
class Migration(migrations.Migration):
dependencies = [
('name', '0016_slidesubmissionstatusname'),
('doc', '0035_populate_docextresources'),
('meeting', '0030_allow_empty_joint_with_sessions'),
('name', '0016_slidesubmissionstatusname'),
]
operations = [
migrations.AddField(
model_name='slidesubmission',
name='doc',
field=ietf.utils.models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='doc.Document'),
),
migrations.AddField(
model_name='slidesubmission',
name='status',
field=ietf.utils.models.ForeignKey(default='pending', on_delete=django.db.models.deletion.PROTECT, to='name.SlideSubmissionStatusName'),
),
]

View file

@ -28,7 +28,7 @@ from ietf.dbtemplate.models import DBTemplate
from ietf.doc.models import Document
from ietf.group.models import Group
from ietf.group.utils import can_manage_materials
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName, ImportantDateName, TimerangeName
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName, ImportantDateName, TimerangeName, SlideSubmissionStatusName
from ietf.person.models import Person
from ietf.utils.decorators import memoize
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
@ -1269,6 +1269,9 @@ class SlideSubmission(models.Model):
apply_to_all = models.BooleanField(default=False)
submitter = ForeignKey(Person)
status = ForeignKey(SlideSubmissionStatusName, default='pending', on_delete=models.PROTECT)
doc = ForeignKey(Document, null=True, on_delete=models.SET_NULL)
def staged_filepath(self):
return os.path.join(settings.SLIDE_STAGING_PATH , self.filename)

View file

@ -2760,7 +2760,11 @@ class MaterialsTests(TestCase):
self.assertEqual(r.status_code,200)
r = self.client.post(url,dict(title='some title',disapprove="disapprove"))
self.assertEqual(r.status_code,302)
self.assertEqual(SlideSubmission.objects.count(), 0)
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'rejected').count(), 1)
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'pending').count(), 0)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertContains(r, "These slides have already been rejected")
def test_approve_proposed_slides(self):
submission = SlideSubmissionFactory()
@ -2769,13 +2773,22 @@ class MaterialsTests(TestCase):
chair = RoleFactory(group=submission.session.group,name_id='chair').person
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
login_testing_unauthorized(self, chair.user.username, url)
self.assertEqual(submission.status_id, 'pending')
self.assertIsNone(submission.doc)
r = self.client.get(url)
self.assertEqual(r.status_code,200)
r = self.client.post(url,dict(title='different title',approve='approve'))
self.assertEqual(r.status_code,302)
self.assertEqual(SlideSubmission.objects.count(), 0)
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'pending').count(), 0)
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'approved').count(), 1)
submission = SlideSubmission.objects.get(id = submission.id)
self.assertEqual(submission.status_id, 'approved')
self.assertIsNotNone(submission.doc)
self.assertEqual(session.sessionpresentation_set.count(),1)
self.assertEqual(session.sessionpresentation_set.first().document.title,'different title')
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertContains(r, "These slides have already been approved")
def test_approve_proposed_slides_multisession_apply_one(self):
submission = SlideSubmissionFactory(session__meeting__type_id='ietf')
@ -2846,7 +2859,7 @@ class MaterialsTests(TestCase):
self.assertEqual(r.status_code, 302)
self.client.logout()
(first_submission, second_submission) = SlideSubmission.objects.filter(session=session).order_by('id')
(first_submission, second_submission) = SlideSubmission.objects.filter(session=session, status__slug = 'pending').order_by('id')
approve_url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':second_submission.pk,'num':second_submission.session.meeting.number})
login_testing_unauthorized(self, chair.user.username, approve_url)
@ -2858,7 +2871,8 @@ class MaterialsTests(TestCase):
self.assertEqual(r.status_code,302)
self.client.logout()
self.assertEqual(SlideSubmission.objects.count(),0)
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'pending').count(),0)
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'rejected').count(),1)
self.assertEqual(session.sessionpresentation_set.first().document.rev,'01')
path = os.path.join(submission.session.meeting.get_materials_path(),'slides')
filename = os.path.join(path,session.sessionpresentation_set.first().document.name+'-01.txt')

View file

@ -27,7 +27,7 @@ import debug # pyflakes:ignore
from django import forms
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, HttpResponseNotFound, Http404
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
@ -79,6 +79,7 @@ from ietf.meeting.utils import session_requested_by
from ietf.meeting.utils import current_session_status
from ietf.meeting.utils import data_for_meetings_overview
from ietf.message.utils import infer_message
from ietf.name.models import SlideSubmissionStatusName
from ietf.secr.proceedings.utils import handle_upload_file
from ietf.secr.proceedings.proc_utils import (get_progress_stats, post_process, import_audio_files,
create_recording)
@ -1615,9 +1616,9 @@ def session_details(request, num, acronym):
pending_suggestions = None
if request.user.is_authenticated:
if can_manage:
pending_suggestions = session.slidesubmission_set.all()
pending_suggestions = session.slidesubmission_set.filter(status__slug='pending')
else:
pending_suggestions = session.slidesubmission_set.filter(submitter=request.user.person)
pending_suggestions = session.slidesubmission_set.filter(status__slug='pending', submitter=request.user.person)
return render(request, "meeting/session_details.html",
{ 'scheduled_sessions':scheduled_sessions ,
@ -3213,13 +3214,16 @@ def approve_proposed_slides(request, slidesubmission_id, num):
name, _ = os.path.splitext(submission.filename)
name = name[:name.rfind('-ss')]
existing_doc = Document.objects.filter(name=name).first()
if request.method == 'POST':
if request.method == 'POST' and submission.status.slug == 'pending':
form = ApproveSlidesForm(show_apply_to_all_checkbox, request.POST)
if form.is_valid():
apply_to_all = submission.session.type_id == 'regular'
if show_apply_to_all_checkbox:
apply_to_all = form.cleaned_data['apply_to_all']
if request.POST.get('approve'):
# Ensure that we have a file to approve. The system gets cranky otherwise.
if submission.filename is None or submission.filename == '' or not os.path.isfile(submission.staged_filepath()):
return HttpResponseNotFound("The slides you attempted to approve could not be found. Please disapprove and delete them instead.")
title = form.cleaned_data['title']
if existing_doc:
doc = Document.objects.get(name=name)
@ -3259,15 +3263,29 @@ def approve_proposed_slides(request, slidesubmission_id, num):
os.rename(submission.staged_filepath(), os.path.join(path, target_filename))
post_process(doc)
acronym = submission.session.group.acronym
submission.delete()
submission.status = SlideSubmissionStatusName.objects.get(slug='approved')
submission.doc = doc
submission.save()
return redirect('ietf.meeting.views.session_details',num=num,acronym=acronym)
elif request.POST.get('disapprove'):
os.unlink(submission.staged_filepath())
# Errors in processing a submit request sometimes result
# in a SlideSubmission object without a file. Handle
# this case and keep processing the 'disapprove' even if
# the filename doesn't exist.
try:
if submission.filename != None and submission.filename != '':
os.unlink(submission.staged_filepath())
except (FileNotFoundError, IsADirectoryError):
pass
acronym = submission.session.group.acronym
submission.delete()
submission.status = SlideSubmissionStatusName.objects.get(slug='rejected')
submission.save()
return redirect('ietf.meeting.views.session_details',num=num,acronym=acronym)
else:
pass
elif not submission.status.slug == 'pending':
return render(request, "meeting/previously_approved_slides.html",
{'submission': submission })
else:
initial = {
'title': submission.title,

View file

@ -11,7 +11,7 @@ from ietf.name.models import (
ReviewRequestStateName, ReviewResultName, ReviewTypeName, RoleName, RoomResourceName,
SessionStatusName, StdLevelName, StreamName, TimeSlotTypeName, TopicAudienceName,
DocUrlTagName, ReviewAssignmentStateName, ReviewerQueuePolicyName, TimerangeName,
ExtResourceName, ExtResourceTypeName, )
ExtResourceName, ExtResourceTypeName, SlideSubmissionStatusName)
from ietf.stats.models import CountryAlias
@ -89,3 +89,4 @@ admin.site.register(TimerangeName, NameAdmin)
admin.site.register(TopicAudienceName, NameAdmin)
admin.site.register(DocUrlTagName, NameAdmin)
admin.site.register(ExtResourceTypeName, NameAdmin)
admin.site.register(SlideSubmissionStatusName, NameAdmin)

View file

@ -12015,6 +12015,36 @@
"model": "name.sessionstatusname",
"pk": "schedw"
},
{
"fields": {
"desc": "Approved",
"name": "approved",
"order": 1,
"used": true
},
"model": "name.slidesubmissionstatusname",
"pk": "approved"
},
{
"fields": {
"desc": "Pending",
"name": "pending",
"order": 0,
"used": true
},
"model": "name.slidesubmissionstatusname",
"pk": "pending"
},
{
"fields": {
"desc": "Rejected",
"name": "rejected",
"order": 2,
"used": true
},
"model": "name.slidesubmissionstatusname",
"pk": "rejected"
},
{
"fields": {
"desc": "",
@ -14843,7 +14873,7 @@
"fields": {
"command": "xym",
"switch": "--version",
"time": "2020-07-09T00:12:56.528",
"time": "2020-07-23T00:12:27.508",
"used": true,
"version": "xym 0.4.8"
},
@ -14854,9 +14884,9 @@
"fields": {
"command": "pyang",
"switch": "--version",
"time": "2020-07-09T00:12:58.135",
"time": "2020-07-23T00:12:28.886",
"used": true,
"version": "pyang 2.2.1"
"version": "pyang 2.3.2"
},
"model": "utils.versioninfo",
"pk": 2
@ -14865,7 +14895,7 @@
"fields": {
"command": "yanglint",
"switch": "--version",
"time": "2020-07-09T00:12:58.398",
"time": "2020-07-23T00:12:29.140",
"used": true,
"version": "yanglint SO 1.6.7"
},
@ -14876,9 +14906,9 @@
"fields": {
"command": "xml2rfc",
"switch": "--version",
"time": "2020-07-09T00:13:00.193",
"time": "2020-07-23T00:12:30.892",
"used": true,
"version": "xml2rfc 2.46.0"
"version": "xml2rfc 2.47.0"
},
"model": "utils.versioninfo",
"pk": 4

View file

@ -0,0 +1,42 @@
# Generated by Django 2.2.14 on 2020-08-03 11:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('name', '0015_populate_extres'),
]
def forward(apps, schema_editor):
SlideSubmissionStatusName = apps.get_model('name', 'SlideSubmissionStatusName')
slide_submission_status_names = [
('pending', 'Pending'),
('approved', 'Approved'),
('rejected', 'Rejected'),
]
for order, (slug, desc) in enumerate(slide_submission_status_names):
SlideSubmissionStatusName.objects.create(slug=slug, name=slug, desc=desc, used=True, order=order)
def reverse(apps, schema_editor):
pass
operations = [
migrations.CreateModel(
name='SlideSubmissionStatusName',
fields=[
('slug', models.CharField(max_length=32, primary_key=True, serialize=False)),
('name', models.CharField(max_length=255)),
('desc', models.TextField(blank=True)),
('used', models.BooleanField(default=True)),
('order', models.IntegerField(default=0)),
],
options={
'ordering': ['order', 'name'],
'abstract': False,
},
),
migrations.RunPython(forward, reverse),
]

View file

@ -130,3 +130,5 @@ class ExtResourceTypeName(NameModel):
class ExtResourceName(NameModel):
"""GitHub Repository URL, GitHub Username, ..."""
type = ForeignKey(ExtResourceTypeName)
class SlideSubmissionStatusName(NameModel):
"Pending, Accepted, Rejected"

View file

@ -17,7 +17,8 @@ from ietf.name.models import ( AgendaTypeName, BallotPositionName, ConstraintNam
LiaisonStatementState, LiaisonStatementTagName, MeetingTypeName, NomineePositionStateName,
ReviewAssignmentStateName, ReviewRequestStateName, ReviewResultName, ReviewTypeName,
RoleName, RoomResourceName, SessionStatusName, StdLevelName, StreamName, TimeSlotTypeName,
TopicAudienceName, ReviewerQueuePolicyName, TimerangeName, ExtResourceTypeName, ExtResourceName)
TopicAudienceName, ReviewerQueuePolicyName, TimerangeName, ExtResourceTypeName, ExtResourceName,
SlideSubmissionStatusName)
class TimeSlotTypeNameResource(ModelResource):
class Meta:
@ -650,3 +651,20 @@ class ExtResourceNameResource(ModelResource):
"type": ALL_WITH_RELATIONS,
}
api.name.register(ExtResourceNameResource())
class SlideSubmissionStatusNameResource(ModelResource):
class Meta:
queryset = SlideSubmissionStatusName.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
resource_name = 'slidesubmissionstatusname'
ordering = ['slug', ]
filtering = {
"slug": ALL,
"name": ALL,
"desc": ALL,
"used": ALL,
"order": ALL,
}
api.name.register(SlideSubmissionStatusNameResource())