Skip to content

Commit becad91

Browse files
committed
Allow logged in users to propose slides for meeting sessions. Fixes ietf-tools#2547 and ietf-tools#2403. Commit ready for merge.
- Legacy-Id: 16102
1 parent 7224e06 commit becad91

16 files changed

Lines changed: 11353 additions & 10877 deletions
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.20 on 2019-03-25 06:11
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations
6+
7+
def forward(apps, schema_editor):
8+
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
9+
Recipient = apps.get_model('mailtrigger', 'Recipient')
10+
11+
changed = MailTrigger.objects.create(
12+
slug = 'slides_proposed',
13+
desc = 'Recipients when slides are proposed for a given session',
14+
)
15+
changed.to.set(Recipient.objects.filter(slug__in=['group_chairs', 'group_responsible_directors', 'group_secretaries']))
16+
17+
def reverse(apps, schema_editor):
18+
MailTrigger = apps.get_model('mailtrigger','MailTrigger')
19+
MailTrigger.objects.filter(slug='slides_proposed').delete()
20+
21+
class Migration(migrations.Migration):
22+
23+
dependencies = [
24+
('mailtrigger', '0004_ballot_rfceditornote_changed_postapproval'),
25+
]
26+
27+
operations = [
28+
migrations.RunPython(forward,reverse)
29+
]

ietf/meeting/admin.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from ietf.meeting.models import (Meeting, Room, Session, TimeSlot, Constraint, Schedule,
44
SchedTimeSessAssignment, ResourceAssociation, FloorPlan, UrlResource,
5-
SessionPresentation, ImportantDate, )
5+
SessionPresentation, ImportantDate, SlideSubmission, )
66

77

88
class UrlResourceAdmin(admin.ModelAdmin):
@@ -128,6 +128,9 @@ class SessionPresentationAdmin(admin.ModelAdmin):
128128
class ImportantDateAdmin(admin.ModelAdmin):
129129
model = ImportantDate
130130
list_display = ['meeting', 'name', 'date']
131-
132-
133131
admin.site.register(ImportantDate,ImportantDateAdmin)
132+
133+
class SlideSubmissionAdmin(admin.ModelAdmin):
134+
model = SlideSubmission
135+
list_display = ['session', 'submitter', 'title']
136+
admin.site.register(SlideSubmission, SlideSubmissionAdmin)

ietf/meeting/factories.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from django.core.files.base import ContentFile
66

7-
from ietf.meeting.models import Meeting, Session, Schedule, TimeSlot, SessionPresentation, FloorPlan, Room
7+
from ietf.meeting.models import Meeting, Session, Schedule, TimeSlot, SessionPresentation, FloorPlan, Room, SlideSubmission
88
from ietf.group.factories import GroupFactory
99
from ietf.person.factories import PersonFactory
1010

@@ -157,3 +157,16 @@ class Meta:
157157
), 'floorplan.jpg'
158158
)
159159
)
160+
161+
class SlideSubmissionFactory(factory.DjangoModelFactory):
162+
class Meta:
163+
model = SlideSubmission
164+
165+
session = factory.SubFactory(SessionFactory)
166+
title = factory.Faker('sentence')
167+
filename = factory.Sequence(lambda n: 'test_slide_%d'%n)
168+
submitter = factory.SubFactory(PersonFactory)
169+
170+
make_file = factory.PostGeneration(
171+
lambda obj, create, extracted, **kwargs: open(obj.staged_filepath(),'a').close()
172+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.20 on 2019-03-23 07:41
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
import ietf.utils.models
8+
9+
10+
class Migration(migrations.Migration):
11+
12+
dependencies = [
13+
('person', '0009_auto_20190118_0725'),
14+
('meeting', '0011_auto_20190114_0550'),
15+
]
16+
17+
operations = [
18+
migrations.CreateModel(
19+
name='SlideSubmission',
20+
fields=[
21+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22+
('title', models.CharField(max_length=255)),
23+
('filename', models.CharField(max_length=255)),
24+
('apply_to_all', models.BooleanField(default=False)),
25+
('session', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='meeting.Session')),
26+
('submitter', ietf.utils.models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='person.Person')),
27+
],
28+
),
29+
]

ietf/meeting/models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,3 +1152,16 @@ class Meta:
11521152

11531153
def __unicode__(self):
11541154
return u'%s : %s : %s' % ( self.meeting, self.name, self.date )
1155+
1156+
class SlideSubmission(models.Model):
1157+
session = ForeignKey(Session)
1158+
title = models.CharField(max_length=255)
1159+
filename = models.CharField(max_length=255)
1160+
apply_to_all = models.BooleanField(default=False)
1161+
submitter = ForeignKey(Person)
1162+
1163+
def staged_filepath(self):
1164+
return os.path.join(settings.SLIDE_STAGING_PATH , self.filename)
1165+
1166+
def staged_url(self):
1167+
return "".join([settings.SLIDE_STAGING_URL, self.filename])

ietf/meeting/resources.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from ietf.meeting.models import ( Meeting, ResourceAssociation, Constraint, Room, Schedule, Session,
1111
TimeSlot, SchedTimeSessAssignment, SessionPresentation, FloorPlan,
12-
UrlResource, ImportantDate )
12+
UrlResource, ImportantDate, SlideSubmission )
1313

1414
from ietf.name.resources import MeetingTypeNameResource
1515
class MeetingResource(ModelResource):
@@ -301,3 +301,23 @@ class Meta:
301301
"name": ALL_WITH_RELATIONS,
302302
}
303303
api.meeting.register(ImportantDateResource())
304+
305+
306+
from ietf.person.resources import PersonResource
307+
class SlideSubmissionResource(ModelResource):
308+
session = ToOneField(SessionResource, 'session')
309+
submitter = ToOneField(PersonResource, 'submitter')
310+
class Meta:
311+
queryset = SlideSubmission.objects.all()
312+
serializer = api.Serializer()
313+
cache = SimpleCache()
314+
#resource_name = 'slidesubmission'
315+
filtering = {
316+
"id": ALL,
317+
"title": ALL,
318+
"filename": ALL,
319+
"apply_to_all": ALL,
320+
"session": ALL_WITH_RELATIONS,
321+
"submitter": ALL_WITH_RELATIONS,
322+
}
323+
api.meeting.register(SlideSubmissionResource())

ietf/meeting/tests_views.py

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from ietf.meeting.helpers import send_interim_approval_request
2626
from ietf.meeting.helpers import send_interim_cancellation_notice
2727
from ietf.meeting.helpers import send_interim_minutes_reminder, populate_important_dates, update_important_dates
28-
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation
28+
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation, SlideSubmission
2929
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting
3030
from ietf.meeting.utils import finalize
3131
from ietf.name.models import SessionStatusName, ImportantDateName
@@ -34,9 +34,9 @@
3434
from ietf.utils.text import xslugify
3535

3636
from ietf.person.factories import PersonFactory
37-
from ietf.group.factories import GroupFactory, GroupEventFactory
37+
from ietf.group.factories import GroupFactory, GroupEventFactory, RoleFactory
3838
from ietf.meeting.factories import ( SessionFactory, SessionPresentationFactory, ScheduleFactory,
39-
MeetingFactory, FloorPlanFactory, TimeSlotFactory )
39+
MeetingFactory, FloorPlanFactory, TimeSlotFactory, SlideSubmissionFactory )
4040
from ietf.doc.factories import DocumentFactory
4141
from ietf.submit.tests import submission_file
4242

@@ -2007,6 +2007,116 @@ def test_remove_sessionpresentation(self):
20072007
self.assertEqual(0,session.sessionpresentation_set.count())
20082008
self.assertEqual(2,doc.docevent_set.count())
20092009

2010+
def test_propose_session_slides(self):
2011+
for type_id in ['ietf','interim']:
2012+
session = SessionFactory(meeting__type_id=type_id)
2013+
chair = RoleFactory(group=session.group,name_id='chair').person
2014+
session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
2015+
newperson = PersonFactory()
2016+
2017+
session_overview_url = urlreverse('ietf.meeting.views.session_details',kwargs={'num':session.meeting.number,'acronym':session.group.acronym})
2018+
propose_url = urlreverse('ietf.meeting.views.propose_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
2019+
2020+
r = self.client.get(session_overview_url)
2021+
self.assertEqual(r.status_code,200)
2022+
q = PyQuery(r.content)
2023+
self.assertFalse(q('#uploadslides'))
2024+
self.assertFalse(q('#proposeslides'))
2025+
2026+
self.client.login(username=newperson.user.username,password=newperson.user.username+"+password")
2027+
r = self.client.get(session_overview_url)
2028+
self.assertEqual(r.status_code,200)
2029+
q = PyQuery(r.content)
2030+
self.assertTrue(q('#proposeslides'))
2031+
self.client.logout()
2032+
2033+
login_testing_unauthorized(self,newperson.user.username,propose_url)
2034+
r = self.client.get(propose_url)
2035+
self.assertEqual(r.status_code,200)
2036+
test_file = StringIO('this is not really a slide')
2037+
test_file.name = 'not_really.txt'
2038+
empty_outbox()
2039+
r = self.client.post(propose_url,dict(file=test_file,title='a test slide file',apply_to_all=True))
2040+
self.assertEqual(r.status_code, 302)
2041+
session = Session.objects.get(pk=session.pk)
2042+
self.assertEqual(session.slidesubmission_set.count(),1)
2043+
self.assertEqual(len(outbox),1)
2044+
2045+
r = self.client.get(session_overview_url)
2046+
self.assertEqual(r.status_code, 200)
2047+
q = PyQuery(r.content)
2048+
self.assertEqual(len(q('#proposedslidelist p')), 1)
2049+
2050+
SlideSubmissionFactory(session = session)
2051+
2052+
self.client.logout()
2053+
self.client.login(username=chair.user.username, password=chair.user.username+"+password")
2054+
r = self.client.get(session_overview_url)
2055+
self.assertEqual(r.status_code, 200)
2056+
q = PyQuery(r.content)
2057+
self.assertEqual(len(q('#proposedslidelist p')), 2)
2058+
self.client.logout()
2059+
2060+
def test_disapprove_proposed_slides(self):
2061+
submission = SlideSubmissionFactory()
2062+
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
2063+
chair = RoleFactory(group=submission.session.group,name_id='chair').person
2064+
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
2065+
login_testing_unauthorized(self, chair.user.username, url)
2066+
r = self.client.get(url)
2067+
self.assertEqual(r.status_code,200)
2068+
r = self.client.post(url,dict(title='some title',disapprove="disapprove"))
2069+
self.assertEqual(r.status_code,302)
2070+
self.assertEqual(SlideSubmission.objects.count(), 0)
2071+
2072+
def test_approve_proposed_slides(self):
2073+
submission = SlideSubmissionFactory()
2074+
session = submission.session
2075+
session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
2076+
chair = RoleFactory(group=submission.session.group,name_id='chair').person
2077+
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
2078+
login_testing_unauthorized(self, chair.user.username, url)
2079+
r = self.client.get(url)
2080+
self.assertEqual(r.status_code,200)
2081+
r = self.client.post(url,dict(title='different title',approve='approve'))
2082+
self.assertEqual(r.status_code,302)
2083+
self.assertEqual(SlideSubmission.objects.count(), 0)
2084+
self.assertEqual(session.sessionpresentation_set.count(),1)
2085+
self.assertEqual(session.sessionpresentation_set.first().document.title,'different title')
2086+
2087+
def test_approve_proposed_slides_multisession_apply_one(self):
2088+
submission = SlideSubmissionFactory(session__meeting__type_id='ietf')
2089+
session1 = submission.session
2090+
session2 = SessionFactory(group=submission.session.group, meeting=submission.session.meeting)
2091+
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
2092+
chair = RoleFactory(group=submission.session.group,name_id='chair').person
2093+
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
2094+
login_testing_unauthorized(self, chair.user.username, url)
2095+
r = self.client.get(url)
2096+
self.assertEqual(r.status_code,200)
2097+
q = PyQuery(r.content)
2098+
self.assertTrue(q('#id_apply_to_all'))
2099+
r = self.client.post(url,dict(title='yet another title',approve='approve'))
2100+
self.assertEqual(r.status_code,302)
2101+
self.assertEqual(session1.sessionpresentation_set.count(),1)
2102+
self.assertEqual(session2.sessionpresentation_set.count(),0)
2103+
2104+
def test_approve_proposed_slides_multisession_apply_all(self):
2105+
submission = SlideSubmissionFactory(session__meeting__type_id='ietf')
2106+
session1 = submission.session
2107+
session2 = SessionFactory(group=submission.session.group, meeting=submission.session.meeting)
2108+
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
2109+
chair = RoleFactory(group=submission.session.group,name_id='chair').person
2110+
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
2111+
login_testing_unauthorized(self, chair.user.username, url)
2112+
r = self.client.get(url)
2113+
self.assertEqual(r.status_code,200)
2114+
r = self.client.post(url,dict(title='yet another title',apply_to_all=1,approve='approve'))
2115+
self.assertEqual(r.status_code,302)
2116+
self.assertEqual(session1.sessionpresentation_set.count(),1)
2117+
self.assertEqual(session2.sessionpresentation_set.count(),1)
2118+
2119+
20102120
class SessionTests(TestCase):
20112121

20122122
def test_meeting_requests(self):

ietf/meeting/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
url(r'^session/(?P<session_id>\d+)/bluesheets$', views.upload_session_bluesheets),
1414
url(r'^session/(?P<session_id>\d+)/minutes$', views.upload_session_minutes),
1515
url(r'^session/(?P<session_id>\d+)/agenda$', views.upload_session_agenda),
16+
url(r'^session/(?P<session_id>\d+)/propose_slides$', views.propose_session_slides),
1617
url(r'^session/(?P<session_id>\d+)/slides(?:/%(name)s)?$' % settings.URL_REGEXPS, views.upload_session_slides),
1718
url(r'^session/(?P<session_id>\d+)/slides/%(name)s/order$' % settings.URL_REGEXPS, views.set_slide_order),
1819
url(r'^session/(?P<session_id>\d+)/doc/%(name)s/remove$' % settings.URL_REGEXPS, views.remove_sessionpresentation),
1920
url(r'^session/(?P<session_id>\d+)\.ics$', views.ical_agenda),
2021
url(r'^sessions/(?P<acronym>[-a-z0-9]+)\.ics$', views.ical_agenda),
22+
url(r'^slidesubmission/(?P<slidesubmission_id>\d+)$', views.approve_proposed_slides)
2123
]
2224

2325

0 commit comments

Comments
 (0)