changes to support input of session recordings
- Legacy-Id: 8216
This commit is contained in:
parent
e9a7133fec
commit
61668dba80
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
|
@ -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<meeting_num>\d{1,3})/$', 'ajax_generate_proceedings', name='proceedings_ajax_generate_proceedings'),
|
||||
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'),
|
||||
url(r'^ajax/order-slide/$', 'ajax_order_slide', name='proceedings_ajax_order_slide'),
|
||||
# special offline URL for testing proceedings build
|
||||
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 @@ urlpatterns = patterns('ietf.secr.proceedings.views',
|
|||
url(r'^progress-report/(?P<meeting_num>\d{1,3})/$', 'progress_report', name='proceedings_progress_report'),
|
||||
url(r'^replace-slide/(?P<slide_id>[A-Za-z0-9._\-\+]+)/$', 'replace_slide', name='proceedings_replace_slide'),
|
||||
url(r'^(?P<meeting_num>\d{1,3})/$', 'select', name='proceedings_select'),
|
||||
url(r'^(?P<meeting_num>\d{1,3})/recording/$', 'recording', name='proceedings_recording'),
|
||||
url(r'^(?P<meeting_num>\d{1,3})/recording/edit/(?P<name>[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<meeting_num>\d{1,3}|interim-\d{4}-[A-Za-z0-9_\-\+]+)/(?P<session_id>\d{1,6})/$',
|
||||
'upload_unified', name='proceedings_upload_unified'),
|
||||
|
|
|
@ -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, {}),
|
||||
)
|
||||
|
|
|
@ -24,3 +24,8 @@
|
|||
<a href="{{ MEDIA_URL }}proceedings/{{ meeting.number }}/progress-report.html">Progress Report</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Use this to input session recording information.</p>
|
||||
<ul class="none">
|
||||
<li><button onclick="window.location='{% url "proceedings_recording" meeting_num=meeting.number %}'">Recordings</button>
|
||||
</li>
|
||||
</ul>
|
|
@ -11,7 +11,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for slide in slides %}
|
||||
{% for slide in materials.slides %}
|
||||
<tr id="slide_{{ forloop.counter }}" class="{% cycle 'row1' 'row2' %}">
|
||||
<td><span class="ui-icon ui-icon-arrowthick-2-n-s"></span><a href="{{ slide.get_absolute_url }}" target="_blank">{{ slide.title }}</a></td>
|
||||
<td>{{ slide.external_url }}{% if slide.external_url|is_ppt %}<span class="required"> *</span>{% endif %}</td>
|
||||
|
|
|
@ -22,16 +22,16 @@ and end with
|
|||
{% endif %}
|
||||
|
||||
<h3>
|
||||
{% if minutes %}
|
||||
<a href="{{ minutes.get_absolute_url }}">Minutes</a>
|
||||
{% if materials.minutes %}
|
||||
<a href="{{ materials.minutes.get_absolute_url }}">Minutes</a>
|
||||
{% else %}
|
||||
Minutes
|
||||
{% endif %} |
|
||||
{% if meeting.type.slug == "ietf" %}
|
||||
<a href="/audio/ietf{{ meeting.number }}/">Audio Archives</a> |
|
||||
{% comment %}<a href="/audio/ietf{{ meeting.number }}/">Audio Archives</a> | {% endcomment %}
|
||||
{% else %}
|
||||
{% if agenda %}
|
||||
<a href="{{ agenda.get_absolute_url }}">Agenda</a>
|
||||
{% if materials.agenda %}
|
||||
<a href="{{ materials.agenda.get_absolute_url }}">Agenda</a>
|
||||
{% else %}
|
||||
Agenda
|
||||
{% endif %} |
|
||||
|
@ -84,10 +84,21 @@ and end with
|
|||
{% endif %}
|
||||
<br /><br /></td></tr></table>
|
||||
|
||||
<h3>Meeting Slides:</h3>
|
||||
{% if slides %}
|
||||
<h3>Recordings:</h3>
|
||||
{% if materials.record %}
|
||||
<ul>
|
||||
{% for slide in slides %}
|
||||
{% for record in materials.record %}
|
||||
<li><a href="{{ record.href }}" target="_blank">{{ record.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>No Recordings Present</p>
|
||||
{% endif %}
|
||||
|
||||
<h3>Meeting Slides:</h3>
|
||||
{% if materials.slides %}
|
||||
<ul>
|
||||
{% for slide in materials.slides %}
|
||||
<li><a href="{{ slide.get_absolute_url }}" target="_blank">{{ slide.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
80
ietf/secr/templates/proceedings/recording.html
Executable file
80
ietf/secr/templates/proceedings/recording.html
Executable file
|
@ -0,0 +1,80 @@
|
|||
{% extends "base_site.html" %}
|
||||
|
||||
{% block title %}Proceedings{% endblock %}
|
||||
|
||||
{% block extrastyle %}{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{{ SECR_STATIC_URL }}css/jquery-ui-modified.css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/jquery-ui-1.8.9.min.js"></script>
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/proceedings-recording.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
{% if meeting.type_id == "interim" %}
|
||||
» <a href="{% url "proceedings" %}">Proceedings</a>
|
||||
» <a href="{% url "proceedings_select_interim" %}">Interim Select Group</a>
|
||||
» <a href="{% url "proceedings_interim" acronym=group.acronym%}">{{ group.acronym }}</a>
|
||||
» {{ meeting }}
|
||||
{% else %}
|
||||
» <a href="{% url "proceedings" %}">Proceedings</a>
|
||||
» <a href="{% url "proceedings_select" meeting_num=meeting.number %}">{{ meeting.number }}</a>
|
||||
» Recording
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="module interim-container">
|
||||
|
||||
<h2>Recording Metadata</h2>
|
||||
<form id="recording-form" enctype="multipart/form-data" action="." method="post">{% csrf_token %}
|
||||
<table class="center" id="proceedings-upload-table">
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
|
||||
{% include "includes/buttons_submit_back.html" %}
|
||||
|
||||
</form>
|
||||
<div class="inline-related">
|
||||
<h2>{{ meeting }} - Recordings
|
||||
</h2>
|
||||
<table class="center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Group</th>
|
||||
<th>Session</th>
|
||||
<th>Name</th>
|
||||
<th>URL</th>
|
||||
<th>Edit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in recordings %}
|
||||
<tr>
|
||||
<td>{{ record.group.acronym }}</td>
|
||||
<td>{{ record.session_set.first.official_scheduledsession.timeslot.time|date:"m-d H:i" }}</td>
|
||||
<td>{{ record.name }}</td>
|
||||
<td><a href="{{ record.href }}">{{ record.href }}</a></td>
|
||||
<td><a href="{% url "proceedings_recording_edit" meeting_num=meeting.number name=record.name %}">Edit</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div> <!-- inline-group -->
|
||||
</div> <!-- module -->
|
||||
|
||||
{% if docevents %}
|
||||
<br>
|
||||
<div class="module interim-container">
|
||||
{% include "includes/docevents.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block footer-extras %}
|
||||
{% include "includes/upload_footer.html" %}
|
||||
{% endblock %}
|
40
ietf/secr/templates/proceedings/recording_edit.html
Executable file
40
ietf/secr/templates/proceedings/recording_edit.html
Executable file
|
@ -0,0 +1,40 @@
|
|||
{% extends "base_site.html" %}
|
||||
|
||||
{% block title %}Edit Recording{% endblock %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
{% if meeting.type_id == "interim" %}
|
||||
» <a href="{% url "proceedings" %}">Proceedings</a>
|
||||
» <a href="{% url "proceedings_select_interim" %}">Interim Select Group</a>
|
||||
» <a href="{% url "proceedings_interim" acronym=group.acronym%}">{{ group.acronym }}</a>
|
||||
» {{ meeting }}
|
||||
» <a href="{% url "proceedings_recording" meeting_num=meeting.number %}">Recording</a>
|
||||
» {{ recording.name }}
|
||||
{% else %}
|
||||
» <a href="{% url "proceedings" %}">Proceedings</a>
|
||||
» <a href="{% url "proceedings_select" meeting_num=meeting.number %}">{{ meeting.number }}</a>
|
||||
» <a href="{% url "proceedings_recording" meeting_num=meeting.number %}">Recording</a>
|
||||
» {{ recording.name }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="module interim-container">
|
||||
<h2>Recording Metadata for Group: {{ form.instance.group.acronym }} | Session: {{ form.instance.session_set.first.official_scheduledsession.timeslot.time }}</h2>
|
||||
<p><h3>Edit Recording Metadata:</h3></p>
|
||||
<form id="recording-form" action="" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
|
||||
{% include "includes/buttons_save_cancel.html" %}
|
||||
|
||||
</form>
|
||||
</div> <!-- module -->
|
||||
|
||||
{% endblock %}
|
|
@ -51,10 +51,10 @@
|
|||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
{% if minutes %}
|
||||
<td><a href="{{ minutes.get_absolute_url }}" target="_blank">Minutes</td>
|
||||
<td>{{ minutes.external_url }}</td>
|
||||
<td><a href="{% url "proceedings_delete_material" slide_id=minutes.name %}">Delete</a></td>
|
||||
{% if materials.minutes %}
|
||||
<td><a href="{{ materials.minutes.get_absolute_url }}" target="_blank">Minutes</td>
|
||||
<td>{{ materials.minutes.external_url }}</td>
|
||||
<td><a href="{% url "proceedings_delete_material" slide_id=materials.minutes.name %}">Delete</a></td>
|
||||
{% else %}
|
||||
<td>Minutes</td>
|
||||
<td>(not uploaded)</td>
|
||||
|
@ -62,10 +62,10 @@
|
|||
{% endif %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% if agenda %}
|
||||
<td><a href="{{ agenda.get_absolute_url }}" target="_blank">Agenda</a></td>
|
||||
<td>{{ agenda.external_url }}</td>
|
||||
<td><a href="{% url "proceedings_delete_material" slide_id=agenda.name %}">Delete</a></td>
|
||||
{% if materials.agenda %}
|
||||
<td><a href="{{ materials.agenda.get_absolute_url }}" target="_blank">Agenda</a></td>
|
||||
<td>{{ materials.agenda.external_url }}</td>
|
||||
<td><a href="{% url "proceedings_delete_material" slide_id=materials.agenda.name %}">Delete</a></td>
|
||||
{% else %}
|
||||
<td>Agenda</td>
|
||||
<td>(not uploaded)</td>
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -575,6 +575,10 @@ div.interim-scroll {
|
|||
border-left: 1px solid #CCCCCC;
|
||||
}
|
||||
|
||||
#recording-form #id_external_url {
|
||||
width: 40em;
|
||||
}
|
||||
|
||||
td.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
24
static/secretariat/js/proceedings-recording.js
Normal file
24
static/secretariat/js/proceedings-recording.js
Normal file
|
@ -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) {
|
||||
$( '<ul class="errorlist"><li>No sessions found</li></ul>' ).insertBefore( "#id_group" );
|
||||
} else {
|
||||
$.each(data,function(i,item) {
|
||||
$('#id_session').append('<option value="'+item.id+'">'+item.value+'</option>');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue