Skip to content

Commit 61668db

Browse files
committed
changes to support input of session recordings
- Legacy-Id: 8216
1 parent e9a7133 commit 61668db

16 files changed

Lines changed: 441 additions & 60 deletions

File tree

ietf/doc/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ def get_file_path(self):
9090
return settings.DOCUMENT_PATH_PATTERN.format(doc=self)
9191

9292
def href(self):
93+
if self.external_url.startswith == 'http://':
94+
return self.external_url
95+
9396
meeting_related = self.meeting_related()
9497

9598
settings_var = settings.DOC_HREFS

ietf/secr/proceedings/forms.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
from django.template.defaultfilters import filesizeformat
66

77
from ietf.doc.models import Document
8+
from ietf.group.models import Group
89
from ietf.name.models import DocTypeName
9-
from ietf.meeting.models import Meeting
10-
10+
from ietf.meeting.models import Meeting, Session
1111

1212

1313
# ---------------------------------------------
@@ -22,6 +22,14 @@
2222
# Forms
2323
#----------------------------------------------------------
2424

25+
class AjaxChoiceField(forms.ChoiceField):
26+
'''
27+
Special ChoiceField to use when populating options with Ajax. The submitted value
28+
is not in the initial choices list so we need to override valid_value().
29+
'''
30+
def valid_value(self, value):
31+
return True
32+
2533
class EditSlideForm(forms.ModelForm):
2634
class Meta:
2735
model = Document
@@ -42,6 +50,46 @@ def clean(self):
4250
raise forms.ValidationError('A meeting already exists for this date.')
4351
return cleaned_data
4452

53+
class RecordingForm(forms.Form):
54+
group = forms.CharField(max_length=40)
55+
external_url = forms.CharField(max_length=255,label='Url')
56+
session = AjaxChoiceField(choices=(('','----'),))
57+
58+
def clean_session(self):
59+
'''
60+
Emulate ModelChoiceField functionality
61+
'''
62+
id = self.cleaned_data.get('session')
63+
try:
64+
return Session.objects.get(id=id)
65+
except Session.DoesNotExist:
66+
raise forms.ValidationError('Invalid Session')
67+
68+
def clean_group(self):
69+
acronym = self.cleaned_data.get('group')
70+
try:
71+
return Group.objects.get(acronym=acronym)
72+
except Group.DoesNotExist:
73+
raise forms.ValidationError('Invalid group name')
74+
75+
class RecordingEditForm(forms.ModelForm):
76+
class Meta:
77+
model = Document
78+
fields = ['external_url']
79+
80+
def __init__(self, *args, **kwargs):
81+
super(RecordingEditForm, self).__init__(*args, **kwargs)
82+
self.fields['external_url'].label='Url'
83+
"""
84+
class RecordingEditForm(forms.Form):
85+
name = forms.CharField(max_length=255,widget=forms.HiddenInput)
86+
url = forms.CharField(max_length=255)
87+
88+
def save(self):
89+
recording = get_object_or_404(Document,name=name)
90+
recording.external_url = self.cleaned_data['url']
91+
"""
92+
4593
class ReplaceSlideForm(forms.ModelForm):
4694
file = forms.FileField(label='Select File')
4795

ietf/secr/proceedings/proc_utils.py

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,68 @@
1212
from django.conf import settings
1313
from django.shortcuts import render_to_response
1414

15-
from ietf.doc.models import Document, RelatedDocument, DocEvent
15+
from ietf.doc.models import Document, RelatedDocument, DocEvent, NewRevisionDocEvent, State
1616
from ietf.group.models import Group, Role
1717
from ietf.group.utils import get_charter_text
1818
from ietf.meeting.helpers import get_schedule
1919
from ietf.meeting.models import Session, Meeting, ScheduledSession
20+
from ietf.person.models import Person
2021
from ietf.secr.proceedings.models import InterimMeeting # proxy model
2122
from ietf.secr.proceedings.models import Registration
2223
from ietf.secr.utils.document import get_rfc_num
2324
from ietf.secr.utils.group import groups_by_session
24-
from ietf.secr.utils.meeting import get_upload_root, get_proceedings_path, get_material, get_session
25-
25+
from ietf.secr.utils.meeting import get_upload_root, get_proceedings_path, get_materials, get_session
2626

2727

2828
# -------------------------------------------------
2929
# Helper Functions
3030
# -------------------------------------------------
31+
def check_audio_files(group,meeting):
32+
'''
33+
Checks for audio files and creates corresponding materials (docs) for the Session
34+
Expects audio files in the format ietf[meeting num]-[room]-YYYMMDD-HHMM-*,
35+
Example: ietf90-salonb-20140721-1710-pm3.mp3
36+
'''
37+
for session in Session.objects.filter(group=group,meeting=meeting,status__in=('sched','schedw')):
38+
timeslot = session.official_scheduledsession().timeslot
39+
room = timeslot.location.name.lower()
40+
room = room.replace(' ','')
41+
room = room.replace('/','')
42+
time = timeslot.time.strftime("%Y%m%d-%H%M")
43+
filename = 'ietf{}-{}-{}-*'.format(meeting.number,room,time)
44+
path = os.path.join(settings.MEETING_RECORDINGS_DIR,'ietf{}'.format(meeting.number),filename)
45+
for file in glob.glob(path):
46+
url = 'http://www.ietf.org/audio/ietf{}/{}'.format(meeting.number,os.path.basename(file))
47+
doc = Document.objects.filter(external_url=url).first()
48+
if not doc:
49+
create_recording(session,meeting,group,url)
50+
51+
def create_recording(session,meeting,group,url):
52+
sequence = get_next_sequence(group,meeting,'record')
53+
name = 'record-{}-{}-{}'.format(meeting.number,group.acronym,sequence)
54+
time = session.official_scheduledsession().timeslot.time.strftime('%Y-%m-%d %H:%M')
55+
if url.endswith('mp3'):
56+
title = 'Audio recording for {}'.format(time)
57+
else:
58+
title = 'Video recording for {}'.format(time)
59+
60+
doc = Document.objects.create(name=name,
61+
title=title,
62+
external_url=url,
63+
group=group,
64+
rev='00',
65+
type_id='record')
66+
doc.set_state(State.objects.get(type='record', slug='active'))
67+
68+
# create DocEvent
69+
NewRevisionDocEvent.objects.create(type='new_revision',
70+
by=Person.objects.get(name='(system)'),
71+
doc=doc,
72+
rev=doc.rev,
73+
desc='New revision available',
74+
time=doc.time)
75+
session.materials.add(doc)
76+
3177
def mycomp(timeslot):
3278
'''
3379
This takes a timeslot object and returns a key to sort by the area acronym or None
@@ -141,6 +187,13 @@ def get_progress_stats(sdate,edate):
141187

142188
return data
143189

190+
def get_next_sequence(group,meeting,type):
191+
'''
192+
Returns the next sequence number to use for a document of type = type.
193+
Takes a group=Group object, meeting=Meeting object, type = string
194+
'''
195+
return Document.objects.filter(name__startswith='{}-{}-{}-'.format(type,meeting.number,group.acronym)).count() + 1
196+
144197
def write_html(path,content):
145198
f = open(path,'w')
146199
f.write(content)
@@ -188,14 +241,8 @@ def create_proceedings(meeting, group, is_final=False):
188241
if meeting.type_id == 'ietf' and int(meeting.number) < 79:
189242
return
190243

191-
sessions = Session.objects.filter(meeting=meeting,group=group)
192-
if sessions:
193-
session = sessions[0]
194-
agenda,minutes,slides = get_material(session)
195-
else:
196-
agenda = None
197-
minutes = None
198-
slides = None
244+
check_audio_files(group,meeting)
245+
materials = get_materials(group,meeting)
199246

200247
chairs = group.role_set.filter(name='chair')
201248
secretaries = group.role_set.filter(name='secr')
@@ -215,7 +262,7 @@ def create_proceedings(meeting, group, is_final=False):
215262
settings.MEDIA_URL,
216263
meeting.date.strftime('%Y/%m/%d'),
217264
group.acronym)
218-
265+
219266
# Only do these tasks if we are running official proceedings generation,
220267
# otherwise skip them for expediency. This procedure is called any time meeting
221268
# materials are uploaded/deleted, and we don't want to do all this work each time.
@@ -313,9 +360,7 @@ def create_proceedings(meeting, group, is_final=False):
313360
'tas': tas,
314361
'meeting': meeting,
315362
'rfcs': rfcs,
316-
'slides': slides,
317-
'minutes': minutes,
318-
'agenda': agenda}
363+
'materials': materials}
319364
)
320365

321366
# save proceedings

ietf/secr/proceedings/tests.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
from django.core.urlresolvers import reverse
44

5-
from ietf.utils.test_utils import TestCase
6-
from ietf.meeting.models import Meeting
5+
from ietf.group.models import Group
6+
from ietf.meeting.models import Meeting, Session
7+
from ietf.meeting.test_data import make_meeting_test_data
8+
from ietf.person.models import Person
79
from ietf.utils.test_data import make_test_data
8-
10+
from ietf.utils.test_utils import TestCase
911

1012
SECR_USER='secretary'
1113

@@ -18,11 +20,30 @@ def test_main(self):
1820
response = self.client.get(url)
1921
self.assertEqual(response.status_code, 200)
2022

21-
def test_view(self):
22-
"View Test"
23+
class RecordingsTestCase(TestCase):
24+
def test_page(self):
2325
make_test_data()
24-
meeting = Meeting.objects.all()[0]
25-
url = reverse('meetings_view', kwargs={'meeting_id':meeting.number})
26+
meeting = Meeting.objects.first()
27+
url = reverse('proceedings_recording', kwargs={'meeting_num':meeting.number})
2628
self.client.login(username="secretary", password="secretary+password")
2729
response = self.client.get(url)
2830
self.assertEqual(response.status_code, 200)
31+
def test_post(self):
32+
make_meeting_test_data()
33+
meeting = Meeting.objects.first()
34+
group = Group.objects.get(acronym='mars')
35+
#session = Session.objects.create(group=group,
36+
# requested_by=Person.objects.get(name="(System)"),
37+
# meeting=meeting,
38+
# status_id='sched')
39+
# need ss and timeslot
40+
session = Session.objects.filter(meeting=meeting,group=group,status__in=('sched','schedw')).first()
41+
url = reverse('proceedings_recording', kwargs={'meeting_num':meeting.number})
42+
data = dict(group=group.acronym,external_url='http://youtube.com/xyz',session=session.pk)
43+
self.client.login(username="secretary", password="secretary+password")
44+
response = self.client.post(url,data,follow=True)
45+
self.assertEqual(response.status_code, 200)
46+
print response.content
47+
self.failUnless(group.acronym in response.content)
48+
49+
#def test_edit(self):

ietf/secr/proceedings/urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
urlpatterns = patterns('ietf.secr.proceedings.views',
44
url(r'^$', 'main', name='proceedings'),
55
url(r'^ajax/generate-proceedings/(?P<meeting_num>\d{1,3})/$', 'ajax_generate_proceedings', name='proceedings_ajax_generate_proceedings'),
6+
url(r'^ajax/get-sessions/(?P<meeting_num>\d{1,3})/(?P<acronym>[A-Za-z0-9_\-\+]+)/', 'ajax_get_sessions', name='proceedings_ajax_get_sessions'),
67
url(r'^ajax/order-slide/$', 'ajax_order_slide', name='proceedings_ajax_order_slide'),
78
# special offline URL for testing proceedings build
89
url(r'^build/(?P<meeting_num>\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/(?P<acronym>[A-Za-z0-9_\-\+]+)/$',
@@ -15,6 +16,8 @@
1516
url(r'^progress-report/(?P<meeting_num>\d{1,3})/$', 'progress_report', name='proceedings_progress_report'),
1617
url(r'^replace-slide/(?P<slide_id>[A-Za-z0-9._\-\+]+)/$', 'replace_slide', name='proceedings_replace_slide'),
1718
url(r'^(?P<meeting_num>\d{1,3})/$', 'select', name='proceedings_select'),
19+
url(r'^(?P<meeting_num>\d{1,3})/recording/$', 'recording', name='proceedings_recording'),
20+
url(r'^(?P<meeting_num>\d{1,3})/recording/edit/(?P<name>[A-Za-z0-9_\-\+]+)$', 'recording_edit', name='proceedings_recording_edit'),
1821
# NOTE: we have two entries here which both map to upload_unified, passing session_id or acronym
1922
url(r'^(?P<meeting_num>\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/(?P<session_id>\d{1,6})/$',
2023
'upload_unified', name='proceedings_upload_unified'),

0 commit comments

Comments
 (0)