Skip to content

Commit d565375

Browse files
committed
Merged in [16102] from rjsparks@nostrum.com:
Allow logged in users to propose slides for meeting sessions. Fixes ietf-tools#2547 and ietf-tools#2403. - Legacy-Id: 16118 Note: SVN reference [16102] has been migrated to Git commit becad91
2 parents 23d28bc + becad91 commit d565375

16 files changed

Lines changed: 11357 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):
@@ -129,6 +129,9 @@ class SessionPresentationAdmin(admin.ModelAdmin):
129129
class ImportantDateAdmin(admin.ModelAdmin):
130130
model = ImportantDate
131131
list_display = ['meeting', 'name', 'date']
132-
133-
134132
admin.site.register(ImportantDate,ImportantDateAdmin)
133+
134+
class SlideSubmissionAdmin(admin.ModelAdmin):
135+
model = SlideSubmission
136+
list_display = ['session', 'submitter', 'title']
137+
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: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from ietf.meeting.helpers import send_interim_approval_request
2727
from ietf.meeting.helpers import send_interim_cancellation_notice
2828
from ietf.meeting.helpers import send_interim_minutes_reminder, populate_important_dates, update_important_dates
29-
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation
29+
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation, SlideSubmission
3030
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting
3131
from ietf.meeting.utils import finalize
3232
from ietf.name.models import SessionStatusName, ImportantDateName
@@ -35,9 +35,9 @@
3535
from ietf.utils.text import xslugify
3636

3737
from ietf.person.factories import PersonFactory
38-
from ietf.group.factories import GroupFactory, GroupEventFactory
38+
from ietf.group.factories import GroupFactory, GroupEventFactory, RoleFactory
3939
from ietf.meeting.factories import ( SessionFactory, SessionPresentationFactory, ScheduleFactory,
40-
MeetingFactory, FloorPlanFactory, TimeSlotFactory )
40+
MeetingFactory, FloorPlanFactory, TimeSlotFactory, SlideSubmissionFactory )
4141
from ietf.doc.factories import DocumentFactory
4242
from ietf.submit.tests import submission_file
4343

@@ -1721,13 +1721,17 @@ class MaterialsTests(TestCase):
17211721

17221722
def setUp(self):
17231723
self.materials_dir = self.tempdir('materials')
1724+
self.staging_dir = self.tempdir('staging')
17241725
if not os.path.exists(self.materials_dir):
17251726
os.mkdir(self.materials_dir)
17261727
self.saved_agenda_path = settings.AGENDA_PATH
17271728
settings.AGENDA_PATH = self.materials_dir
1729+
self.saved_staging_path = settings.SLIDE_STAGING_PATH
1730+
settings.SLIDE_STAGING_PATH = self.staging_dir
17281731

17291732
def tearDown(self):
17301733
settings.AGENDA_PATH = self.saved_agenda_path
1734+
settings.SLIDE_STAGING_PATH = self.saved_staging_path
17311735
shutil.rmtree(self.materials_dir)
17321736

17331737
def crawl_materials(self, url, top):
@@ -2019,6 +2023,116 @@ def test_remove_sessionpresentation(self):
20192023
self.assertEqual(0,session.sessionpresentation_set.count())
20202024
self.assertEqual(2,doc.docevent_set.count())
20212025

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

20242138
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)