changes to support video content in proceedings ('recording' document type). Commit ready for merge
- Legacy-Id: 8237
This commit is contained in:
commit
96bccc7b17
|
@ -3,7 +3,9 @@
|
|||
import datetime, os
|
||||
|
||||
from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.core.validators import URLValidator
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.conf import settings
|
||||
from django.utils.html import mark_safe
|
||||
|
@ -90,6 +92,13 @@ class DocumentInfo(models.Model):
|
|||
return settings.DOCUMENT_PATH_PATTERN.format(doc=self)
|
||||
|
||||
def href(self):
|
||||
validator = URLValidator()
|
||||
try:
|
||||
validator(self.external_url)
|
||||
return self.external_url
|
||||
except ValidationError:
|
||||
pass
|
||||
|
||||
meeting_related = self.meeting_related()
|
||||
|
||||
settings_var = settings.DOC_HREFS
|
||||
|
|
|
@ -51,10 +51,10 @@ def has_role(user, role_names, *args, **kwargs):
|
|||
"IRTF Chair": Q(person=person, name="chair", group__acronym="irtf"),
|
||||
"IAB Chair": Q(person=person, name="chair", group__acronym="iab"),
|
||||
"IAB Group Chair": Q(person=person, name="chair", group__type="iab", group__state="active"),
|
||||
"WG Chair": Q(person=person,name="chair", group__type="wg", group__state__in=["active","bof"]),
|
||||
"WG Secretary": Q(person=person,name="secr", group__type="wg", group__state__in=["active","bof"]),
|
||||
"RG Chair": Q(person=person,name="chair", group__type="rg", group__state="active"),
|
||||
"RG Secretary": Q(person=person,name="secr", group__type="rg", group__state="active"),
|
||||
"WG Chair": Q(person=person,name="chair", group__type="wg", group__state__in=["active","bof","proposed"]),
|
||||
"WG Secretary": Q(person=person,name="secr", group__type="wg", group__state__in=["active","bof","proposed"]),
|
||||
"RG Chair": Q(person=person,name="chair", group__type="rg", group__state="active"),
|
||||
"RG Secretary": Q(person=person,name="secr", group__type="rg", group__state="active"),
|
||||
"Team Chair": Q(person=person,name="chair", group__type="team", group__state="active"),
|
||||
"Nomcom Chair": Q(person=person, name="chair", group__type="nomcom", group__state="active", group__acronym__icontains=kwargs.get('year', '0000')),
|
||||
"Nomcom Advisor": Q(person=person, name="advisor", group__type="nomcom", group__state="active", group__acronym__icontains=kwargs.get('year', '0000')),
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
"model": "name.constraintname",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"penalty": 0,
|
||||
"penalty": 100000,
|
||||
"used": true,
|
||||
"name": "Conflicts with",
|
||||
"desc": ""
|
||||
|
@ -92,7 +92,7 @@
|
|||
"model": "name.constraintname",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"penalty": 0,
|
||||
"penalty": 10000,
|
||||
"used": true,
|
||||
"name": "Conflicts with (secondary)",
|
||||
"desc": ""
|
||||
|
@ -103,7 +103,7 @@
|
|||
"model": "name.constraintname",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"penalty": 0,
|
||||
"penalty": 1000,
|
||||
"used": true,
|
||||
"name": "Conflicts with (tertiary)",
|
||||
"desc": ""
|
||||
|
@ -114,7 +114,7 @@
|
|||
"model": "name.constraintname",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"penalty": 0,
|
||||
"penalty": 200000,
|
||||
"used": true,
|
||||
"name": "Person must be present",
|
||||
"desc": ""
|
||||
|
@ -734,6 +734,16 @@
|
|||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "recording",
|
||||
"model": "name.doctypename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "Recording",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "uploaded",
|
||||
"model": "name.draftsubmissionstatename",
|
||||
|
@ -1109,6 +1119,26 @@
|
|||
"desc": "An IETF/IAB Nominating Committee. Use 'SDO' for external nominating committees."
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "iab",
|
||||
"model": "name.grouptypename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "IAB",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "isoc",
|
||||
"model": "name.grouptypename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "ISOC",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "ps",
|
||||
"model": "name.intendedstdlevelname",
|
||||
|
@ -1270,22 +1300,12 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"pk": "ad",
|
||||
"pk": "ceo",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "Area Director",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "pre-ad",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "Incoming Area Director",
|
||||
"name": "CEO",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
|
@ -1293,39 +1313,19 @@
|
|||
"pk": "chair",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 1,
|
||||
"used": true,
|
||||
"name": "Chair",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "editor",
|
||||
"pk": "ad",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 2,
|
||||
"used": true,
|
||||
"name": "Editor",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "secr",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "Secretary",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "techadv",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "Tech Advisor",
|
||||
"name": "Area Director",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
|
@ -1333,90 +1333,170 @@
|
|||
"pk": "execdir",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 2,
|
||||
"used": true,
|
||||
"name": "Executive Director",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "pre-ad",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 3,
|
||||
"used": true,
|
||||
"name": "Incoming Area Director",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "admdir",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 3,
|
||||
"used": true,
|
||||
"name": "Administrative Director",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "techadv",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 4,
|
||||
"used": true,
|
||||
"name": "Tech Advisor",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "liaiman",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 4,
|
||||
"used": true,
|
||||
"name": "Liaison Manager",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "advisor",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 4,
|
||||
"used": true,
|
||||
"name": "Advisor",
|
||||
"desc": "Advisor in a group that has explicit membership, such as the NomCom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "editor",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 5,
|
||||
"used": true,
|
||||
"name": "Editor",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "auth",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 5,
|
||||
"used": true,
|
||||
"name": "Authorized Individual",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "secr",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 6,
|
||||
"used": true,
|
||||
"name": "Secretary",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "delegate",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 6,
|
||||
"used": true,
|
||||
"name": "Delegate",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "atlarge",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "At Large Member",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "member",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 7,
|
||||
"used": true,
|
||||
"name": "Member",
|
||||
"desc": "Regular group member in a group that has explicit membership, such as the NomCom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "atlarge",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 10,
|
||||
"used": true,
|
||||
"name": "At Large Member",
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "liaison",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"order": 11,
|
||||
"used": true,
|
||||
"name": "Liaison Member",
|
||||
"desc": "Liaison group member in a group that has explicit membership, such as the NomCom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "advisor",
|
||||
"pk": "announce",
|
||||
"model": "name.rolename",
|
||||
"fields": {
|
||||
"order": 12,
|
||||
"used": true,
|
||||
"name": "List Announcer",
|
||||
"desc": "Authorised to send announcements to the ietf-announce and other lists"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "project",
|
||||
"model": "name.roomresourcename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "Advisor",
|
||||
"desc": "Advisor in a group that has explicit membership, such as the NomCom"
|
||||
"name": "LCD projector",
|
||||
"desc": "The room will have a computer projector"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "proj2",
|
||||
"model": "name.roomresourcename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "second LCD projector",
|
||||
"desc": "The room will have a second computer projector"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "meetecho",
|
||||
"model": "name.roomresourcename",
|
||||
"fields": {
|
||||
"order": 0,
|
||||
"used": true,
|
||||
"name": "Meetecho Remote Partition Support",
|
||||
"desc": "The room will have a meetecho wrangler"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1818,6 +1898,13 @@
|
|||
"label": "RFC Status Change"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "recording",
|
||||
"model": "doc.statetype",
|
||||
"fields": {
|
||||
"label": "Recording State"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 81,
|
||||
"model": "doc.state",
|
||||
|
@ -3487,6 +3574,32 @@
|
|||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 135,
|
||||
"model": "doc.state",
|
||||
"fields": {
|
||||
"used": true,
|
||||
"name": "Active",
|
||||
"next_states": [],
|
||||
"slug": "active",
|
||||
"type": "recording",
|
||||
"order": 0,
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 136,
|
||||
"model": "doc.state",
|
||||
"fields": {
|
||||
"used": true,
|
||||
"name": "deleted",
|
||||
"next_states": [],
|
||||
"slug": "deleted",
|
||||
"type": "recording",
|
||||
"order": 0,
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 77,
|
||||
"model": "doc.state",
|
||||
|
|
|
@ -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,37 @@ 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.URLField(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 ReplaceSlideForm(forms.ModelForm):
|
||||
file = forms.FileField(label='Select File')
|
||||
|
||||
|
|
|
@ -12,22 +12,74 @@ 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):
|
||||
'''
|
||||
Creates the Document type=recording, setting external_url and creating
|
||||
NewRevisionDocEvent
|
||||
'''
|
||||
sequence = get_next_sequence(group,meeting,'recording')
|
||||
name = 'recording-{}-{}-{}'.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='recording')
|
||||
doc.set_state(State.objects.get(type='recording', 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 +193,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 +247,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 +268,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 +366,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,11 @@ 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.utils.test_data import make_test_data
|
||||
|
||||
from ietf.utils.test_utils import TestCase
|
||||
|
||||
SECR_USER='secretary'
|
||||
|
||||
|
@ -18,11 +19,30 @@ class MainTestCase(TestCase):
|
|||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_view(self):
|
||||
"View Test"
|
||||
class RecordingTestCase(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.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)
|
||||
self.failUnless(group.acronym in response.content)
|
||||
|
||||
# now test edit
|
||||
doc = session.materials.filter(type='recording').first()
|
||||
external_url = 'http://youtube.com/aaa'
|
||||
url = reverse('proceedings_recording_edit', kwargs={'meeting_num':meeting.number,'name':doc.name})
|
||||
response = self.client.post(url,dict(external_url=external_url),follow=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.failUnless(external_url in response.content)
|
|
@ -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
|
||||
|
||||
|
@ -84,14 +84,14 @@ def get_extras(meeting):
|
|||
def get_next_interim_num(acronym,date):
|
||||
'''
|
||||
This function takes a group acronym and date object and returns the next number to use for an
|
||||
interim meeting. The format is interim-[year]-[acronym]-[1-9]
|
||||
interim meeting. The format is interim-[year]-[acronym]-[1-99]
|
||||
'''
|
||||
base = 'interim-%s-%s-' % (date.year, acronym)
|
||||
# can't use count() to calculate the next number in case one was deleted
|
||||
meetings = list(Meeting.objects.filter(type='interim',number__startswith=base).order_by('number'))
|
||||
meetings = Meeting.objects.filter(type='interim',number__startswith=base)
|
||||
if meetings:
|
||||
parts = meetings[-1].number.split('-')
|
||||
return base + str(int(parts[-1]) + 1)
|
||||
nums = sorted([ int(x.number.split('-')[-1]) for x in meetings ])
|
||||
return base + str(nums[-1] + 1)
|
||||
else:
|
||||
return base + '1'
|
||||
|
||||
|
@ -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,69 @@ 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='recording-{}'.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='recording',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':
|
||||
form = RecordingEditForm(request.POST, instance=recording)
|
||||
if form.is_valid():
|
||||
# save record and rebuild proceedings
|
||||
form.save()
|
||||
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 +950,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 +967,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 %}
|
45
ietf/secr/templates/proceedings/recording_edit.html
Executable file
45
ietf/secr/templates/proceedings/recording_edit.html
Executable file
|
@ -0,0 +1,45 @@
|
|||
{% 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>
|
||||
|
||||
<div class="button-group">
|
||||
<ul>
|
||||
<li><button type="submit" name="submit" value="Submit">Submit</button></li>
|
||||
<li><button type="button" onclick="window.location='{% url "proceedings_recording" meeting_num=meeting.number %}'">Cancel</button></li>
|
||||
</ul>
|
||||
</div> <!-- button-group -->
|
||||
|
||||
</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>
|
||||
|
|
|
@ -2,26 +2,27 @@ import os
|
|||
|
||||
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=[],recording=[])
|
||||
# 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':
|
||||
|
|
|
@ -275,6 +275,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"
|
||||
|
@ -297,6 +298,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}",
|
||||
"recording": "{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