Skip to content

Commit 21ba67f

Browse files
committed
Track slide submissions even after acceptance or rejection. Fixes ietf-tools#2835. Commit ready for merge.
- Legacy-Id: 18371
1 parent b6c3a8f commit 21ba67f

9 files changed

Lines changed: 176 additions & 20 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 2.2.14 on 2020-08-03 11:53
2+
3+
from django.db import migrations
4+
import django.db.models.deletion
5+
import ietf.utils.models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('name', '0016_slidesubmissionstatusname'),
12+
('doc', '0035_populate_docextresources'),
13+
('meeting', '0030_allow_empty_joint_with_sessions'),
14+
('name', '0016_slidesubmissionstatusname'),
15+
]
16+
17+
operations = [
18+
migrations.AddField(
19+
model_name='slidesubmission',
20+
name='doc',
21+
field=ietf.utils.models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='doc.Document'),
22+
),
23+
migrations.AddField(
24+
model_name='slidesubmission',
25+
name='status',
26+
field=ietf.utils.models.ForeignKey(default='pending', on_delete=django.db.models.deletion.PROTECT, to='name.SlideSubmissionStatusName'),
27+
),
28+
]

ietf/meeting/models.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from ietf.doc.models import Document
2929
from ietf.group.models import Group
3030
from ietf.group.utils import can_manage_materials
31-
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName, ImportantDateName, TimerangeName
31+
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName, ImportantDateName, TimerangeName, SlideSubmissionStatusName
3232
from ietf.person.models import Person
3333
from ietf.utils.decorators import memoize
3434
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
@@ -1269,6 +1269,9 @@ class SlideSubmission(models.Model):
12691269
apply_to_all = models.BooleanField(default=False)
12701270
submitter = ForeignKey(Person)
12711271

1272+
status = ForeignKey(SlideSubmissionStatusName, default='pending', on_delete=models.PROTECT)
1273+
doc = ForeignKey(Document, null=True, on_delete=models.SET_NULL)
1274+
12721275
def staged_filepath(self):
12731276
return os.path.join(settings.SLIDE_STAGING_PATH , self.filename)
12741277

ietf/meeting/tests_views.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2760,7 +2760,11 @@ def test_disapprove_proposed_slides(self):
27602760
self.assertEqual(r.status_code,200)
27612761
r = self.client.post(url,dict(title='some title',disapprove="disapprove"))
27622762
self.assertEqual(r.status_code,302)
2763-
self.assertEqual(SlideSubmission.objects.count(), 0)
2763+
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'rejected').count(), 1)
2764+
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'pending').count(), 0)
2765+
r = self.client.get(url)
2766+
self.assertEqual(r.status_code, 200)
2767+
self.assertContains(r, "These slides have already been rejected")
27642768

27652769
def test_approve_proposed_slides(self):
27662770
submission = SlideSubmissionFactory()
@@ -2769,13 +2773,22 @@ def test_approve_proposed_slides(self):
27692773
chair = RoleFactory(group=submission.session.group,name_id='chair').person
27702774
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
27712775
login_testing_unauthorized(self, chair.user.username, url)
2776+
self.assertEqual(submission.status_id, 'pending')
2777+
self.assertIsNone(submission.doc)
27722778
r = self.client.get(url)
27732779
self.assertEqual(r.status_code,200)
27742780
r = self.client.post(url,dict(title='different title',approve='approve'))
27752781
self.assertEqual(r.status_code,302)
2776-
self.assertEqual(SlideSubmission.objects.count(), 0)
2782+
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'pending').count(), 0)
2783+
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'approved').count(), 1)
2784+
submission = SlideSubmission.objects.get(id = submission.id)
2785+
self.assertEqual(submission.status_id, 'approved')
2786+
self.assertIsNotNone(submission.doc)
27772787
self.assertEqual(session.sessionpresentation_set.count(),1)
27782788
self.assertEqual(session.sessionpresentation_set.first().document.title,'different title')
2789+
r = self.client.get(url)
2790+
self.assertEqual(r.status_code, 200)
2791+
self.assertContains(r, "These slides have already been approved")
27792792

27802793
def test_approve_proposed_slides_multisession_apply_one(self):
27812794
submission = SlideSubmissionFactory(session__meeting__type_id='ietf')
@@ -2846,7 +2859,7 @@ def test_submit_and_approve_multiple_versions(self):
28462859
self.assertEqual(r.status_code, 302)
28472860
self.client.logout()
28482861

2849-
(first_submission, second_submission) = SlideSubmission.objects.filter(session=session).order_by('id')
2862+
(first_submission, second_submission) = SlideSubmission.objects.filter(session=session, status__slug = 'pending').order_by('id')
28502863

28512864
approve_url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':second_submission.pk,'num':second_submission.session.meeting.number})
28522865
login_testing_unauthorized(self, chair.user.username, approve_url)
@@ -2858,7 +2871,8 @@ def test_submit_and_approve_multiple_versions(self):
28582871
self.assertEqual(r.status_code,302)
28592872
self.client.logout()
28602873

2861-
self.assertEqual(SlideSubmission.objects.count(),0)
2874+
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'pending').count(),0)
2875+
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'rejected').count(),1)
28622876
self.assertEqual(session.sessionpresentation_set.first().document.rev,'01')
28632877
path = os.path.join(submission.session.meeting.get_materials_path(),'slides')
28642878
filename = os.path.join(path,session.sessionpresentation_set.first().document.name+'-01.txt')

ietf/meeting/views.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
from django import forms
2929
from django.shortcuts import render, redirect, get_object_or_404
30-
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
30+
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, HttpResponseNotFound, Http404
3131
from django.conf import settings
3232
from django.contrib import messages
3333
from django.contrib.auth.decorators import login_required
@@ -79,6 +79,7 @@
7979
from ietf.meeting.utils import current_session_status
8080
from ietf.meeting.utils import data_for_meetings_overview
8181
from ietf.message.utils import infer_message
82+
from ietf.name.models import SlideSubmissionStatusName
8283
from ietf.secr.proceedings.utils import handle_upload_file
8384
from ietf.secr.proceedings.proc_utils import (get_progress_stats, post_process, import_audio_files,
8485
create_recording)
@@ -1615,9 +1616,9 @@ def session_details(request, num, acronym):
16151616
pending_suggestions = None
16161617
if request.user.is_authenticated:
16171618
if can_manage:
1618-
pending_suggestions = session.slidesubmission_set.all()
1619+
pending_suggestions = session.slidesubmission_set.filter(status__slug='pending')
16191620
else:
1620-
pending_suggestions = session.slidesubmission_set.filter(submitter=request.user.person)
1621+
pending_suggestions = session.slidesubmission_set.filter(status__slug='pending', submitter=request.user.person)
16211622

16221623
return render(request, "meeting/session_details.html",
16231624
{ 'scheduled_sessions':scheduled_sessions ,
@@ -3213,13 +3214,16 @@ def approve_proposed_slides(request, slidesubmission_id, num):
32133214
name, _ = os.path.splitext(submission.filename)
32143215
name = name[:name.rfind('-ss')]
32153216
existing_doc = Document.objects.filter(name=name).first()
3216-
if request.method == 'POST':
3217+
if request.method == 'POST' and submission.status.slug == 'pending':
32173218
form = ApproveSlidesForm(show_apply_to_all_checkbox, request.POST)
32183219
if form.is_valid():
32193220
apply_to_all = submission.session.type_id == 'regular'
32203221
if show_apply_to_all_checkbox:
32213222
apply_to_all = form.cleaned_data['apply_to_all']
32223223
if request.POST.get('approve'):
3224+
# Ensure that we have a file to approve. The system gets cranky otherwise.
3225+
if submission.filename is None or submission.filename == '' or not os.path.isfile(submission.staged_filepath()):
3226+
return HttpResponseNotFound("The slides you attempted to approve could not be found. Please disapprove and delete them instead.")
32233227
title = form.cleaned_data['title']
32243228
if existing_doc:
32253229
doc = Document.objects.get(name=name)
@@ -3259,15 +3263,29 @@ def approve_proposed_slides(request, slidesubmission_id, num):
32593263
os.rename(submission.staged_filepath(), os.path.join(path, target_filename))
32603264
post_process(doc)
32613265
acronym = submission.session.group.acronym
3262-
submission.delete()
3266+
submission.status = SlideSubmissionStatusName.objects.get(slug='approved')
3267+
submission.doc = doc
3268+
submission.save()
32633269
return redirect('ietf.meeting.views.session_details',num=num,acronym=acronym)
32643270
elif request.POST.get('disapprove'):
3265-
os.unlink(submission.staged_filepath())
3271+
# Errors in processing a submit request sometimes result
3272+
# in a SlideSubmission object without a file. Handle
3273+
# this case and keep processing the 'disapprove' even if
3274+
# the filename doesn't exist.
3275+
try:
3276+
if submission.filename != None and submission.filename != '':
3277+
os.unlink(submission.staged_filepath())
3278+
except (FileNotFoundError, IsADirectoryError):
3279+
pass
32663280
acronym = submission.session.group.acronym
3267-
submission.delete()
3281+
submission.status = SlideSubmissionStatusName.objects.get(slug='rejected')
3282+
submission.save()
32683283
return redirect('ietf.meeting.views.session_details',num=num,acronym=acronym)
32693284
else:
32703285
pass
3286+
elif not submission.status.slug == 'pending':
3287+
return render(request, "meeting/previously_approved_slides.html",
3288+
{'submission': submission })
32713289
else:
32723290
initial = {
32733291
'title': submission.title,

ietf/name/admin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
ReviewRequestStateName, ReviewResultName, ReviewTypeName, RoleName, RoomResourceName,
1212
SessionStatusName, StdLevelName, StreamName, TimeSlotTypeName, TopicAudienceName,
1313
DocUrlTagName, ReviewAssignmentStateName, ReviewerQueuePolicyName, TimerangeName,
14-
ExtResourceName, ExtResourceTypeName, )
14+
ExtResourceName, ExtResourceTypeName, SlideSubmissionStatusName)
1515

1616

1717
from ietf.stats.models import CountryAlias
@@ -89,3 +89,4 @@ class ExtResourceNameAdmin(NameAdmin):
8989
admin.site.register(TopicAudienceName, NameAdmin)
9090
admin.site.register(DocUrlTagName, NameAdmin)
9191
admin.site.register(ExtResourceTypeName, NameAdmin)
92+
admin.site.register(SlideSubmissionStatusName, NameAdmin)

ietf/name/fixtures/names.json

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12015,6 +12015,36 @@
1201512015
"model": "name.sessionstatusname",
1201612016
"pk": "schedw"
1201712017
},
12018+
{
12019+
"fields": {
12020+
"desc": "Approved",
12021+
"name": "approved",
12022+
"order": 1,
12023+
"used": true
12024+
},
12025+
"model": "name.slidesubmissionstatusname",
12026+
"pk": "approved"
12027+
},
12028+
{
12029+
"fields": {
12030+
"desc": "Pending",
12031+
"name": "pending",
12032+
"order": 0,
12033+
"used": true
12034+
},
12035+
"model": "name.slidesubmissionstatusname",
12036+
"pk": "pending"
12037+
},
12038+
{
12039+
"fields": {
12040+
"desc": "Rejected",
12041+
"name": "rejected",
12042+
"order": 2,
12043+
"used": true
12044+
},
12045+
"model": "name.slidesubmissionstatusname",
12046+
"pk": "rejected"
12047+
},
1201812048
{
1201912049
"fields": {
1202012050
"desc": "",
@@ -14843,7 +14873,7 @@
1484314873
"fields": {
1484414874
"command": "xym",
1484514875
"switch": "--version",
14846-
"time": "2020-07-09T00:12:56.528",
14876+
"time": "2020-07-23T00:12:27.508",
1484714877
"used": true,
1484814878
"version": "xym 0.4.8"
1484914879
},
@@ -14854,9 +14884,9 @@
1485414884
"fields": {
1485514885
"command": "pyang",
1485614886
"switch": "--version",
14857-
"time": "2020-07-09T00:12:58.135",
14887+
"time": "2020-07-23T00:12:28.886",
1485814888
"used": true,
14859-
"version": "pyang 2.2.1"
14889+
"version": "pyang 2.3.2"
1486014890
},
1486114891
"model": "utils.versioninfo",
1486214892
"pk": 2
@@ -14865,7 +14895,7 @@
1486514895
"fields": {
1486614896
"command": "yanglint",
1486714897
"switch": "--version",
14868-
"time": "2020-07-09T00:12:58.398",
14898+
"time": "2020-07-23T00:12:29.140",
1486914899
"used": true,
1487014900
"version": "yanglint SO 1.6.7"
1487114901
},
@@ -14876,9 +14906,9 @@
1487614906
"fields": {
1487714907
"command": "xml2rfc",
1487814908
"switch": "--version",
14879-
"time": "2020-07-09T00:13:00.193",
14909+
"time": "2020-07-23T00:12:30.892",
1488014910
"used": true,
14881-
"version": "xml2rfc 2.46.0"
14911+
"version": "xml2rfc 2.47.0"
1488214912
},
1488314913
"model": "utils.versioninfo",
1488414914
"pk": 4
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Generated by Django 2.2.14 on 2020-08-03 11:53
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('name', '0015_populate_extres'),
10+
]
11+
12+
def forward(apps, schema_editor):
13+
SlideSubmissionStatusName = apps.get_model('name', 'SlideSubmissionStatusName')
14+
slide_submission_status_names = [
15+
('pending', 'Pending'),
16+
('approved', 'Approved'),
17+
('rejected', 'Rejected'),
18+
]
19+
for order, (slug, desc) in enumerate(slide_submission_status_names):
20+
SlideSubmissionStatusName.objects.create(slug=slug, name=slug, desc=desc, used=True, order=order)
21+
22+
23+
def reverse(apps, schema_editor):
24+
pass
25+
26+
operations = [
27+
migrations.CreateModel(
28+
name='SlideSubmissionStatusName',
29+
fields=[
30+
('slug', models.CharField(max_length=32, primary_key=True, serialize=False)),
31+
('name', models.CharField(max_length=255)),
32+
('desc', models.TextField(blank=True)),
33+
('used', models.BooleanField(default=True)),
34+
('order', models.IntegerField(default=0)),
35+
],
36+
options={
37+
'ordering': ['order', 'name'],
38+
'abstract': False,
39+
},
40+
),
41+
migrations.RunPython(forward, reverse),
42+
]

ietf/name/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,5 @@ class ExtResourceTypeName(NameModel):
130130
class ExtResourceName(NameModel):
131131
"""GitHub Repository URL, GitHub Username, ..."""
132132
type = ForeignKey(ExtResourceTypeName)
133+
class SlideSubmissionStatusName(NameModel):
134+
"Pending, Accepted, Rejected"

ietf/name/resources.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
LiaisonStatementState, LiaisonStatementTagName, MeetingTypeName, NomineePositionStateName,
1818
ReviewAssignmentStateName, ReviewRequestStateName, ReviewResultName, ReviewTypeName,
1919
RoleName, RoomResourceName, SessionStatusName, StdLevelName, StreamName, TimeSlotTypeName,
20-
TopicAudienceName, ReviewerQueuePolicyName, TimerangeName, ExtResourceTypeName, ExtResourceName)
20+
TopicAudienceName, ReviewerQueuePolicyName, TimerangeName, ExtResourceTypeName, ExtResourceName,
21+
SlideSubmissionStatusName)
2122

2223
class TimeSlotTypeNameResource(ModelResource):
2324
class Meta:
@@ -650,3 +651,20 @@ class Meta:
650651
"type": ALL_WITH_RELATIONS,
651652
}
652653
api.name.register(ExtResourceNameResource())
654+
655+
class SlideSubmissionStatusNameResource(ModelResource):
656+
class Meta:
657+
queryset = SlideSubmissionStatusName.objects.all()
658+
serializer = api.Serializer()
659+
cache = SimpleCache()
660+
resource_name = 'slidesubmissionstatusname'
661+
ordering = ['slug', ]
662+
filtering = {
663+
"slug": ALL,
664+
"name": ALL,
665+
"desc": ALL,
666+
"used": ALL,
667+
"order": ALL,
668+
}
669+
api.name.register(SlideSubmissionStatusNameResource())
670+

0 commit comments

Comments
 (0)