From 61668dba809f9c8094ba30ca43dde57e562f999e Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Wed, 30 Jul 2014 21:55:42 +0000 Subject: [PATCH] changes to support input of session recordings - Legacy-Id: 8216 --- ietf/doc/models.py | 3 + ietf/secr/proceedings/forms.py | 52 ++++++++- ietf/secr/proceedings/proc_utils.py | 75 +++++++++--- ietf/secr/proceedings/tests.py | 35 ++++-- ietf/secr/proceedings/urls.py | 3 + ietf/secr/proceedings/views.py | 108 ++++++++++++++++-- .../includes/proceedings_functions.html | 5 + ietf/secr/templates/includes/slides.html | 2 +- .../templates/proceedings/proceedings.html | 27 +++-- .../secr/templates/proceedings/recording.html | 80 +++++++++++++ .../templates/proceedings/recording_edit.html | 40 +++++++ .../templates/proceedings/upload_unified.html | 16 +-- ietf/secr/utils/meeting.py | 27 +++-- ietf/settings.py | 2 + static/secretariat/css/custom.css | 4 + .../secretariat/js/proceedings-recording.js | 24 ++++ 16 files changed, 442 insertions(+), 61 deletions(-) create mode 100755 ietf/secr/templates/proceedings/recording.html create mode 100755 ietf/secr/templates/proceedings/recording_edit.html create mode 100644 static/secretariat/js/proceedings-recording.js diff --git a/ietf/doc/models.py b/ietf/doc/models.py index e5e304245..2fbf51e80 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -90,6 +90,9 @@ class DocumentInfo(models.Model): return settings.DOCUMENT_PATH_PATTERN.format(doc=self) def href(self): + if self.external_url.startswith == 'http://': + return self.external_url + meeting_related = self.meeting_related() settings_var = settings.DOC_HREFS diff --git a/ietf/secr/proceedings/forms.py b/ietf/secr/proceedings/forms.py index 428575884..d546af49a 100644 --- a/ietf/secr/proceedings/forms.py +++ b/ietf/secr/proceedings/forms.py @@ -5,9 +5,9 @@ from django.conf import settings from django.template.defaultfilters import filesizeformat from ietf.doc.models import Document +from ietf.group.models import Group from ietf.name.models import DocTypeName -from ietf.meeting.models import Meeting - +from ietf.meeting.models import Meeting, Session # --------------------------------------------- @@ -22,6 +22,14 @@ VALID_AGENDA_EXTENSIONS = ('.txt','.html','.htm') # Forms #---------------------------------------------------------- +class AjaxChoiceField(forms.ChoiceField): + ''' + Special ChoiceField to use when populating options with Ajax. The submitted value + is not in the initial choices list so we need to override valid_value(). + ''' + def valid_value(self, value): + return True + class EditSlideForm(forms.ModelForm): class Meta: model = Document @@ -42,6 +50,46 @@ class InterimMeetingForm(forms.Form): raise forms.ValidationError('A meeting already exists for this date.') return cleaned_data +class RecordingForm(forms.Form): + group = forms.CharField(max_length=40) + external_url = forms.CharField(max_length=255,label='Url') + session = AjaxChoiceField(choices=(('','----'),)) + + def clean_session(self): + ''' + Emulate ModelChoiceField functionality + ''' + id = self.cleaned_data.get('session') + try: + return Session.objects.get(id=id) + except Session.DoesNotExist: + raise forms.ValidationError('Invalid Session') + + def clean_group(self): + acronym = self.cleaned_data.get('group') + try: + return Group.objects.get(acronym=acronym) + except Group.DoesNotExist: + raise forms.ValidationError('Invalid group name') + +class RecordingEditForm(forms.ModelForm): + class Meta: + model = Document + fields = ['external_url'] + + def __init__(self, *args, **kwargs): + super(RecordingEditForm, self).__init__(*args, **kwargs) + self.fields['external_url'].label='Url' +""" +class RecordingEditForm(forms.Form): + name = forms.CharField(max_length=255,widget=forms.HiddenInput) + url = forms.CharField(max_length=255) + + def save(self): + recording = get_object_or_404(Document,name=name) + recording.external_url = self.cleaned_data['url'] +""" + class ReplaceSlideForm(forms.ModelForm): file = forms.FileField(label='Select File') diff --git a/ietf/secr/proceedings/proc_utils.py b/ietf/secr/proceedings/proc_utils.py index 04cc689f2..8a2f1c559 100644 --- a/ietf/secr/proceedings/proc_utils.py +++ b/ietf/secr/proceedings/proc_utils.py @@ -12,22 +12,68 @@ import shutil from django.conf import settings from django.shortcuts import render_to_response -from ietf.doc.models import Document, RelatedDocument, DocEvent +from ietf.doc.models import Document, RelatedDocument, DocEvent, NewRevisionDocEvent, State from ietf.group.models import Group, Role from ietf.group.utils import get_charter_text from ietf.meeting.helpers import get_schedule from ietf.meeting.models import Session, Meeting, ScheduledSession +from ietf.person.models import Person from ietf.secr.proceedings.models import InterimMeeting # proxy model from ietf.secr.proceedings.models import Registration from ietf.secr.utils.document import get_rfc_num from ietf.secr.utils.group import groups_by_session -from ietf.secr.utils.meeting import get_upload_root, get_proceedings_path, get_material, get_session - +from ietf.secr.utils.meeting import get_upload_root, get_proceedings_path, get_materials, get_session # ------------------------------------------------- # Helper Functions # ------------------------------------------------- +def check_audio_files(group,meeting): + ''' + Checks for audio files and creates corresponding materials (docs) for the Session + Expects audio files in the format ietf[meeting num]-[room]-YYYMMDD-HHMM-*, + Example: ietf90-salonb-20140721-1710-pm3.mp3 + ''' + for session in Session.objects.filter(group=group,meeting=meeting,status__in=('sched','schedw')): + timeslot = session.official_scheduledsession().timeslot + room = timeslot.location.name.lower() + room = room.replace(' ','') + room = room.replace('/','') + time = timeslot.time.strftime("%Y%m%d-%H%M") + filename = 'ietf{}-{}-{}-*'.format(meeting.number,room,time) + path = os.path.join(settings.MEETING_RECORDINGS_DIR,'ietf{}'.format(meeting.number),filename) + for file in glob.glob(path): + url = 'http://www.ietf.org/audio/ietf{}/{}'.format(meeting.number,os.path.basename(file)) + doc = Document.objects.filter(external_url=url).first() + if not doc: + create_recording(session,meeting,group,url) + +def create_recording(session,meeting,group,url): + sequence = get_next_sequence(group,meeting,'record') + name = 'record-{}-{}-{}'.format(meeting.number,group.acronym,sequence) + time = session.official_scheduledsession().timeslot.time.strftime('%Y-%m-%d %H:%M') + if url.endswith('mp3'): + title = 'Audio recording for {}'.format(time) + else: + title = 'Video recording for {}'.format(time) + + doc = Document.objects.create(name=name, + title=title, + external_url=url, + group=group, + rev='00', + type_id='record') + doc.set_state(State.objects.get(type='record', slug='active')) + + # create DocEvent + NewRevisionDocEvent.objects.create(type='new_revision', + by=Person.objects.get(name='(system)'), + doc=doc, + rev=doc.rev, + desc='New revision available', + time=doc.time) + session.materials.add(doc) + def mycomp(timeslot): ''' 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): return data +def get_next_sequence(group,meeting,type): + ''' + Returns the next sequence number to use for a document of type = type. + Takes a group=Group object, meeting=Meeting object, type = string + ''' + return Document.objects.filter(name__startswith='{}-{}-{}-'.format(type,meeting.number,group.acronym)).count() + 1 + def write_html(path,content): f = open(path,'w') f.write(content) @@ -188,14 +241,8 @@ def create_proceedings(meeting, group, is_final=False): if meeting.type_id == 'ietf' and int(meeting.number) < 79: return - sessions = Session.objects.filter(meeting=meeting,group=group) - if sessions: - session = sessions[0] - agenda,minutes,slides = get_material(session) - else: - agenda = None - minutes = None - slides = None + check_audio_files(group,meeting) + materials = get_materials(group,meeting) chairs = group.role_set.filter(name='chair') secretaries = group.role_set.filter(name='secr') @@ -215,7 +262,7 @@ def create_proceedings(meeting, group, is_final=False): settings.MEDIA_URL, meeting.date.strftime('%Y/%m/%d'), group.acronym) - + # Only do these tasks if we are running official proceedings generation, # otherwise skip them for expediency. This procedure is called any time meeting # 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): 'tas': tas, 'meeting': meeting, 'rfcs': rfcs, - 'slides': slides, - 'minutes': minutes, - 'agenda': agenda} + 'materials': materials} ) # save proceedings diff --git a/ietf/secr/proceedings/tests.py b/ietf/secr/proceedings/tests.py index ea8abf73c..21a787982 100644 --- a/ietf/secr/proceedings/tests.py +++ b/ietf/secr/proceedings/tests.py @@ -2,10 +2,12 @@ import debug # pyflakes:ignore from django.core.urlresolvers import reverse -from ietf.utils.test_utils import TestCase -from ietf.meeting.models import Meeting +from ietf.group.models import Group +from ietf.meeting.models import Meeting, Session +from ietf.meeting.test_data import make_meeting_test_data +from ietf.person.models import Person from ietf.utils.test_data import make_test_data - +from ietf.utils.test_utils import TestCase SECR_USER='secretary' @@ -18,11 +20,30 @@ class MainTestCase(TestCase): response = self.client.get(url) self.assertEqual(response.status_code, 200) - def test_view(self): - "View Test" +class RecordingsTestCase(TestCase): + def test_page(self): make_test_data() - meeting = Meeting.objects.all()[0] - url = reverse('meetings_view', kwargs={'meeting_id':meeting.number}) + meeting = Meeting.objects.first() + url = reverse('proceedings_recording', kwargs={'meeting_num':meeting.number}) self.client.login(username="secretary", password="secretary+password") response = self.client.get(url) self.assertEqual(response.status_code, 200) + def test_post(self): + make_meeting_test_data() + meeting = Meeting.objects.first() + group = Group.objects.get(acronym='mars') + #session = Session.objects.create(group=group, + # requested_by=Person.objects.get(name="(System)"), + # meeting=meeting, + # status_id='sched') + # need ss and timeslot + session = Session.objects.filter(meeting=meeting,group=group,status__in=('sched','schedw')).first() + url = reverse('proceedings_recording', kwargs={'meeting_num':meeting.number}) + data = dict(group=group.acronym,external_url='http://youtube.com/xyz',session=session.pk) + self.client.login(username="secretary", password="secretary+password") + response = self.client.post(url,data,follow=True) + self.assertEqual(response.status_code, 200) + print response.content + self.failUnless(group.acronym in response.content) + + #def test_edit(self): \ No newline at end of file diff --git a/ietf/secr/proceedings/urls.py b/ietf/secr/proceedings/urls.py index cc6e3a750..5212795c4 100644 --- a/ietf/secr/proceedings/urls.py +++ b/ietf/secr/proceedings/urls.py @@ -3,6 +3,7 @@ from django.conf.urls import patterns, url urlpatterns = patterns('ietf.secr.proceedings.views', url(r'^$', 'main', name='proceedings'), url(r'^ajax/generate-proceedings/(?P\d{1,3})/$', 'ajax_generate_proceedings', name='proceedings_ajax_generate_proceedings'), + url(r'^ajax/get-sessions/(?P\d{1,3})/(?P[A-Za-z0-9_\-\+]+)/', 'ajax_get_sessions', name='proceedings_ajax_get_sessions'), url(r'^ajax/order-slide/$', 'ajax_order_slide', name='proceedings_ajax_order_slide'), # special offline URL for testing proceedings build url(r'^build/(?P\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/(?P[A-Za-z0-9_\-\+]+)/$', @@ -15,6 +16,8 @@ urlpatterns = patterns('ietf.secr.proceedings.views', url(r'^progress-report/(?P\d{1,3})/$', 'progress_report', name='proceedings_progress_report'), url(r'^replace-slide/(?P[A-Za-z0-9._\-\+]+)/$', 'replace_slide', name='proceedings_replace_slide'), url(r'^(?P\d{1,3})/$', 'select', name='proceedings_select'), + url(r'^(?P\d{1,3})/recording/$', 'recording', name='proceedings_recording'), + url(r'^(?P\d{1,3})/recording/edit/(?P[A-Za-z0-9_\-\+]+)$', 'recording_edit', name='proceedings_recording_edit'), # NOTE: we have two entries here which both map to upload_unified, passing session_id or acronym url(r'^(?P\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/(?P\d{1,6})/$', 'upload_unified', name='proceedings_upload_unified'), diff --git a/ietf/secr/proceedings/views.py b/ietf/secr/proceedings/views.py index f4a42430b..21a64fdd8 100644 --- a/ietf/secr/proceedings/views.py +++ b/ietf/secr/proceedings/views.py @@ -10,7 +10,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse from django.db.models import Max from django.http import HttpResponseRedirect -from django.shortcuts import render_to_response, get_object_or_404 +from django.shortcuts import render_to_response, get_object_or_404, redirect from django.template import RequestContext from django.utils.text import slugify @@ -19,15 +19,15 @@ from ietf.secr.sreq.forms import GroupSelectForm from ietf.secr.utils.decorators import check_permissions, sec_only from ietf.secr.utils.document import get_full_path from ietf.secr.utils.group import get_my_groups, groups_by_session -from ietf.secr.utils.meeting import get_upload_root, get_material, get_timeslot +from ietf.secr.utils.meeting import get_upload_root, get_materials, get_timeslot from ietf.doc.models import Document, DocAlias, DocEvent, State, NewRevisionDocEvent from ietf.group.models import Group from ietf.ietfauth.utils import has_role from ietf.meeting.models import Meeting, Session, TimeSlot, ScheduledSession -from ietf.secr.proceedings.forms import EditSlideForm, InterimMeetingForm, ReplaceSlideForm, UnifiedUploadForm +from ietf.secr.proceedings.forms import EditSlideForm, InterimMeetingForm, RecordingForm, RecordingEditForm, ReplaceSlideForm, UnifiedUploadForm from ietf.secr.proceedings.proc_utils import ( gen_acknowledgement, gen_agenda, gen_areas, gen_attendees, gen_group_pages, gen_index, gen_irtf, gen_overview, gen_plenaries, gen_progress, gen_research, - gen_training, create_proceedings, create_interim_directory ) + gen_training, create_proceedings, create_interim_directory, create_recording ) from ietf.secr.proceedings.models import InterimMeeting # proxy model @@ -250,6 +250,33 @@ def ajax_generate_proceedings(request, meeting_num): RequestContext(request,{}), ) +@jsonapi +def ajax_get_sessions(request, meeting_num, acronym): + ''' + Ajax function to get session info for group / meeting + returns JSON format response: [{id:session_id, value:session info},...] + If there are no sessions an empty list is returned. + ''' + results=[] + try: + meeting = Meeting.objects.get(number=meeting_num) + group = Group.objects.get(acronym=acronym) + except ObjectDoesNotExist: + return results + + sessions = Session.objects.filter(meeting=meeting,group=group,status='sched') + + # order by time scheduled + sessions = sorted(sessions,key = lambda x: x.official_scheduledsession().timeslot.time) + + for n,session in enumerate(sessions,start=1): + timeslot = session.official_scheduledsession().timeslot + val = '{}: {} {}'.format(n,timeslot.time.strftime('%m-%d %H:%M'),timeslot.location.name) + d = {'id':session.id, 'value': val} + results.append(d) + + return results + @jsonapi def ajax_order_slide(request): ''' @@ -573,6 +600,73 @@ def progress_report(request, meeting_num): url = reverse('proceedings_select', kwargs={'meeting_num':meeting_num}) return HttpResponseRedirect(url) +@sec_only +def recording(request, meeting_num): + ''' + Enter Session recording info. Creates Document and associates it with Session + ''' + meeting = get_object_or_404(Meeting, number=meeting_num) + recordings = Document.objects.filter(name__startswith='record-{}'.format(meeting.number),states__slug='active').order_by('group__acronym') + + if request.method == 'POST': + form = RecordingForm(request.POST) + if form.is_valid(): + group = form.cleaned_data['group'] + external_url = form.cleaned_data['external_url'] + session = form.cleaned_data['session'] + + if Document.objects.filter(type='record',external_url=external_url): + messages.error(request, "Recording already exists") + return redirect('proceedings_recording', meeting_num=meeting_num) + else: + create_recording(session,meeting,group,external_url) + + # rebuild proceedings + create_proceedings(meeting,group) + + messages.success(request,'Recording added') + return redirect('proceedings_recording', meeting_num=meeting_num) + + else: + form = RecordingForm() + + return render_to_response('proceedings/recording.html',{ + 'meeting':meeting, + 'form':form, + 'recordings':recordings}, + RequestContext(request, {}), + ) + +@sec_only +def recording_edit(request, meeting_num, name): + ''' + Edit recording Document + ''' + recording = get_object_or_404(Document, name=name) + meeting = get_object_or_404(Meeting, number=meeting_num) + + if request.method == 'POST': + button_text = request.POST.get('submit', '') + if button_text == 'Cancel': + return redirect('proceedings_recording', meeting_num=meeting_num) + + form = RecordingEditForm(request.POST, instance=recording) + if form.is_valid(): + form.save() + # rebuild proceedings + create_proceedings(meeting,recording.group) + messages.success(request,'Recording saved') + return redirect('proceedings_recording', meeting_num=meeting_num) + else: + form = RecordingEditForm(instance=recording) + + return render_to_response('proceedings/recording_edit.html',{ + 'meeting':meeting, + 'form':form, + 'recording':recording}, + RequestContext(request, {}), + ) + @check_permissions def replace_slide(request, slide_id): ''' @@ -860,7 +954,7 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None): else: form = UnifiedUploadForm(initial={'meeting_id':meeting.id,'acronym':group.acronym,'material_type':'slides'}) - agenda,minutes,slides = get_material(session) + materials = get_materials(group,meeting) # gather DocEvents # include deleted material to catch deleted doc events @@ -877,11 +971,9 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None): 'docevents': docevents, 'meeting': meeting, 'group': group, - 'minutes': minutes, - 'agenda': agenda, + 'materials': materials, 'form': form, 'session_name': session_name, # for Tutorials, etc - 'slides':slides, 'proceedings_url': proceedings_url}, RequestContext(request, {}), ) diff --git a/ietf/secr/templates/includes/proceedings_functions.html b/ietf/secr/templates/includes/proceedings_functions.html index 5788662b9..4cb832de2 100644 --- a/ietf/secr/templates/includes/proceedings_functions.html +++ b/ietf/secr/templates/includes/proceedings_functions.html @@ -24,3 +24,8 @@   Progress Report +

Use this to input session recording information.

+
    +
  • +
  • +
\ No newline at end of file diff --git a/ietf/secr/templates/includes/slides.html b/ietf/secr/templates/includes/slides.html index 802460bee..71146bef6 100644 --- a/ietf/secr/templates/includes/slides.html +++ b/ietf/secr/templates/includes/slides.html @@ -11,7 +11,7 @@ - {% for slide in slides %} + {% for slide in materials.slides %} {{ slide.title }} {{ slide.external_url }}{% if slide.external_url|is_ppt %} *{% endif %} diff --git a/ietf/secr/templates/proceedings/proceedings.html b/ietf/secr/templates/proceedings/proceedings.html index 455b8b685..4f81613f0 100644 --- a/ietf/secr/templates/proceedings/proceedings.html +++ b/ietf/secr/templates/proceedings/proceedings.html @@ -22,16 +22,16 @@ and end with {% endif %}

-{% if minutes %} - Minutes +{% if materials.minutes %} + Minutes {% else %} Minutes {% endif %}  |   {% if meeting.type.slug == "ietf" %} - Audio Archives  |   + {% comment %}Audio Archives  |   {% endcomment %} {% else %} - {% if agenda %} - Agenda + {% if materials.agenda %} + Agenda {% else %} Agenda {% endif %}  |   @@ -84,10 +84,21 @@ and end with {% endif %}

-

Meeting Slides:

-{% if slides %} +

Recordings:

+{% if materials.record %}
    - {% for slide in slides %} + {% for record in materials.record %} +
  • {{ record.title }}
  • + {% endfor %} +
+{% else %} +

No Recordings Present

+{% endif %} + +

Meeting Slides:

+{% if materials.slides %} + diff --git a/ietf/secr/templates/proceedings/recording.html b/ietf/secr/templates/proceedings/recording.html new file mode 100755 index 000000000..613aa95ba --- /dev/null +++ b/ietf/secr/templates/proceedings/recording.html @@ -0,0 +1,80 @@ +{% extends "base_site.html" %} + +{% block title %}Proceedings{% endblock %} + +{% block extrastyle %}{{ block.super }} + +{% endblock %} + +{% block extrahead %}{{ block.super }} + + +{% endblock %} + +{% block breadcrumbs %}{{ block.super }} + {% if meeting.type_id == "interim" %} + » Proceedings + » Interim Select Group + » {{ group.acronym }} + » {{ meeting }} + {% else %} + » Proceedings + » {{ meeting.number }} + » Recording + {% endif %} +{% endblock %} + +{% block content %} + +
+ +

Recording Metadata

+
{% csrf_token %} + + {{ form.as_table }} +
+ + {% include "includes/buttons_submit_back.html" %} + +
+ +
+ +{% if docevents %} +
+
+ {% include "includes/docevents.html" %} +
+{% endif %} + +{% endblock %} + +{% block footer-extras %} + {% include "includes/upload_footer.html" %} +{% endblock %} diff --git a/ietf/secr/templates/proceedings/recording_edit.html b/ietf/secr/templates/proceedings/recording_edit.html new file mode 100755 index 000000000..cea5c2d97 --- /dev/null +++ b/ietf/secr/templates/proceedings/recording_edit.html @@ -0,0 +1,40 @@ +{% extends "base_site.html" %} + +{% block title %}Edit Recording{% endblock %} + +{% block extrahead %}{{ block.super }} + +{% endblock %} + +{% block breadcrumbs %}{{ block.super }} + {% if meeting.type_id == "interim" %} + » Proceedings + » Interim Select Group + » {{ group.acronym }} + » {{ meeting }} + » Recording + » {{ recording.name }} + {% else %} + » Proceedings + » {{ meeting.number }} + » Recording + » {{ recording.name }} + {% endif %} +{% endblock %} + +{% block content %} + +
+

Recording Metadata for Group: {{ form.instance.group.acronym }} | Session: {{ form.instance.session_set.first.official_scheduledsession.timeslot.time }}

+

Edit Recording Metadata:

+
{% csrf_token %} + + {{ form.as_table }} +
+ + {% include "includes/buttons_save_cancel.html" %} + +
+
+ +{% endblock %} diff --git a/ietf/secr/templates/proceedings/upload_unified.html b/ietf/secr/templates/proceedings/upload_unified.html index c8bedf627..0b2d64b7f 100755 --- a/ietf/secr/templates/proceedings/upload_unified.html +++ b/ietf/secr/templates/proceedings/upload_unified.html @@ -51,10 +51,10 @@ - {% if minutes %} - Minutes - {{ minutes.external_url }} - Delete + {% if materials.minutes %} + Minutes + {{ materials.minutes.external_url }} + Delete {% else %} Minutes (not uploaded) @@ -62,10 +62,10 @@ {% endif %} - {% if agenda %} - Agenda - {{ agenda.external_url }} - Delete + {% if materials.agenda %} + Agenda + {{ materials.agenda.external_url }} + Delete {% else %} Agenda (not uploaded) diff --git a/ietf/secr/utils/meeting.py b/ietf/secr/utils/meeting.py index c1afaea3a..d5b657c3f 100644 --- a/ietf/secr/utils/meeting.py +++ b/ietf/secr/utils/meeting.py @@ -1,27 +1,30 @@ import os +import re from django.conf import settings -from ietf.meeting.models import Meeting +from ietf.meeting.models import Meeting, Session def get_current_meeting(): '''Returns the most recent IETF meeting''' return Meeting.objects.filter(type='ietf').order_by('-number')[0] -def get_material(session): +def get_materials(group,meeting): ''' - This function takes a session object and returns a tuple of active materials: - agenda(Document), minutes(Document), slides(list of Documents) + Returns the materials as a dictionary with keys = doctype. + NOTE, if the group has multiple sessions all materials but recordings will be + attached to all sessions. ''' - active_materials = session.materials.exclude(states__slug='deleted') - slides = active_materials.filter(type='slides').order_by('order') - minutes = active_materials.filter(type='minutes') - minutes = minutes[0] if minutes else None - agenda = active_materials.filter(type='agenda') - agenda = agenda[0] if agenda else None - - return agenda,minutes,slides + materials = dict(slides=[],record=[]) + # TODO: status should only be sched, but there is a bug in the scheduler + for session in Session.objects.filter(group=group,meeting=meeting,status__in=('sched','schedw')): + for doc in session.materials.exclude(states__slug='deleted').order_by('order'): + if doc.type.slug in ('minutes','agenda'): + materials[doc.type.slug] = doc + elif doc not in materials[doc.type.slug]: + materials[doc.type.slug].append(doc) + return materials def get_proceedings_path(meeting, group): if meeting.type.slug == 'interim': diff --git a/ietf/settings.py b/ietf/settings.py index 3900e4b34..b98e1a5c8 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -273,6 +273,7 @@ IESG_ROLL_CALL_FILE = '/a/www/www6/iesg/internal/rollcall.txt' IESG_MINUTES_FILE = '/a/www/www6/iesg/internal/minutes.txt' IESG_WG_EVALUATION_DIR = "/a/www/www6/iesg/evaluation" INTERNET_DRAFT_ARCHIVE_DIR = '/a/www/www6s/draft-archive' +MEETING_RECORDINGS_DIR = '/a/www/audio' # Mailing list info URL for lists hosted on the IETF servers MAILING_LIST_INFO_URL = "https://www.ietf.org/mailman/listinfo/%(list_addr)s" @@ -295,6 +296,7 @@ MEETING_DOC_HREFS = { "agenda": "/meeting/{meeting}/agenda/{doc.group.acronym}/", "minutes": "http://www.ietf.org/proceedings/{meeting}/minutes/{doc.external_url}", "slides": "http://www.ietf.org/proceedings/{meeting}/slides/{doc.external_url}", + "record": "{doc.external_url}", } # Override this in settings_local.py if needed diff --git a/static/secretariat/css/custom.css b/static/secretariat/css/custom.css index 05b9a3819..76b284ae6 100644 --- a/static/secretariat/css/custom.css +++ b/static/secretariat/css/custom.css @@ -575,6 +575,10 @@ div.interim-scroll { border-left: 1px solid #CCCCCC; } +#recording-form #id_external_url { + width: 40em; +} + td.hidden { display: none; } diff --git a/static/secretariat/js/proceedings-recording.js b/static/secretariat/js/proceedings-recording.js new file mode 100644 index 000000000..e861a2ae6 --- /dev/null +++ b/static/secretariat/js/proceedings-recording.js @@ -0,0 +1,24 @@ +/* proceedings-recordings.js - utility functions */ + + +$(document).ready(function() { + // auto populate Session select list + $('#id_group').blur(function(){ + var loadUrl = "/secr/proceedings/ajax/get-sessions/"; + var url = window.location.pathname; + var parts = url.split("/"); + var acronym = $(this).val(); + loadUrl = loadUrl+parts[3]+"/"+acronym+"/"; + $('.errorlist').remove(); + $.getJSON(loadUrl,function(data) { + $('#id_session').find('option').remove(); + if (data.length == 0) { + $( '
  • No sessions found
' ).insertBefore( "#id_group" ); + } else { + $.each(data,function(i,item) { + $('#id_session').append(''); + }); + } + }); + }); +});