diff --git a/ietf/doc/tests_material.py b/ietf/doc/tests_material.py index 7fe5216ad..07072ea93 100644 --- a/ietf/doc/tests_material.py +++ b/ietf/doc/tests_material.py @@ -66,6 +66,7 @@ class GroupMaterialTests(TestCase): # post r = self.client.post(url, dict(title="Test File - with fancy title", + abstract = "Test Abstract", name="slides-%s-test-file" % group.acronym, state=State.objects.get(type="slides", slug="active").pk, material=test_file)) @@ -125,6 +126,7 @@ class GroupMaterialTests(TestCase): # post r = self.client.post(url, dict(title="New title", + abstract="New abstract", state=State.objects.get(type="slides", slug="active").pk, material=test_file)) self.assertEqual(r.status_code, 302) diff --git a/ietf/doc/urls_material.py b/ietf/doc/urls_material.py index 22cf0de6c..09e2645bd 100644 --- a/ietf/doc/urls_material.py +++ b/ietf/doc/urls_material.py @@ -3,5 +3,11 @@ from django.conf.urls import patterns, url urlpatterns = patterns('ietf.doc.views_material', url(r'^(?Pstate|title|abstract|revise)/$', "edit_material", name="material_edit"), url(r'^sessions/$', "material_presentations", name="material_presentations"), + (r'^sessions/(?P\d+)/$', "material_presentations"), + (r'^sessions/(?P[A-Za-z0-9_\-\+]+)/$', "material_presentations"), + (r'^sessions/(?P[A-Za-z0-9_\-\+]+)/(?P\d+)/$', "material_presentations"), + (r'^sessions/(?P[A-Za-z0-9_\-\+]+)/(?P[a-zA-Z]+)/$', "material_presentations"), + (r'^sessions/(?P[A-Za-z0-9_\-\+]+)/(?P\d{4}-\d{2}-\d{2}(-\d{4})?)/$', "material_presentations"), + (r'^sessions/(?P[A-Za-z0-9_\-\+]+)/(?P\d{4}-\d{2}-\d{2}(-\d{4})?)/(?P\d+)/$', "material_presentations"), ) diff --git a/ietf/doc/views_material.py b/ietf/doc/views_material.py index 4ccd1d175..10a73130d 100644 --- a/ietf/doc/views_material.py +++ b/ietf/doc/views_material.py @@ -173,17 +173,18 @@ def edit_material(request, name=None, acronym=None, action=None, doc_type=None): 'doc_name': doc.name if doc else "", }) -class MaterialPresentationForm(forms.Form): +class MaterialVersionForm(forms.Form): - sesspres = forms.MultipleChoiceField(required=False,widget=forms.CheckboxSelectMultiple,label='Place this document on the agenda for the selected sessions') + version = forms.ChoiceField(required=False, + label='Which version of this document will be presented at this session') - def __init__(self,*args,**kwargs): + def __init__(self, *args, **kwargs): choices = kwargs.pop('choices') - super(MaterialPresentationForm,self).__init__(*args,**kwargs) - self.fields['sesspres'].choices=choices + super(MaterialVersionForm,self).__init__(*args,**kwargs) + self.fields['version'].choices = choices @login_required -def material_presentations(request, name): +def material_presentations(request, name, acronym=None, date=None, seq=None, week_day=None): doc = get_object_or_404(Document, name=name) if not (doc.type_id=='slides' and doc.get_state('slides').slug=='active'): @@ -197,31 +198,93 @@ def material_presentations(request, name): # This motif is also in Document.future_presentations - it would be nice to consolodate it somehow candidate_sessions = Session.objects.filter(meeting__date__gte=datetime.date.today()-datetime.timedelta(days=15)) refined_candidates = [ sess for sess in candidate_sessions if sess.meeting.end_date()>=datetime.date.today()] + + if acronym: + refined_candidates = [ sess for sess in refined_candidates if sess.group.acronym==acronym] + + if date: + if len(date)==15: + start = datetime.datetime.strptime(date,"%Y-%m-%d-%H%M") + refined_candidates = [ sess for sess in refined_candidates if sess.scheduledsession_set.filter(schedule=sess.meeting.agenda,timeslot__time=start) ] + else: + start = datetime.datetime.strptime(date,"%Y-%m-%d").date() + end = start+datetime.timedelta(days=1) + refined_candidates = [ sess for sess in refined_candidates if sess.scheduledsession_set.filter(schedule=sess.meeting.agenda,timeslot__time__range=(start,end)) ] + + if week_day: + try: + dow = ['sun','mon','tue','wed','thu','fri','sat'].index(week_day.lower()[:3]) + 1 + except ValueError: + raise Http404 + refined_candidates = [ sess for sess in refined_candidates if sess.scheduledsession_set.filter(schedule=sess.meeting.agenda,timeslot__time__week_day=dow) ] + changeable_sessions = [ sess for sess in refined_candidates if can_manage_materials(request.user, sess.group) ] + + if not changeable_sessions: + raise Http404 + for sess in changeable_sessions: - sess.has_presentation = sess.sessionpresentation_set.filter(document=doc) - sorted_sessions = sorted(changeable_sessions,key=lambda x:'%s%s%s'%('0' if x.has_presentation else '1',x.meeting,x.short_name)) + sess.has_presentation = bool(sess.sessionpresentation_set.filter(document=doc)) + if sess.has_presentation: + sess.version = sess.sessionpresentation_set.get(document=doc).rev - choices=[(sess.pk,'%s: %s'%(sess.meeting,sess.short_name)) for sess in sorted_sessions] - initial = {'sesspres': [sess.pk for sess in sorted_sessions if sess.has_presentation]} + # Since Python 2.2 sorts are stable, so this series results in a list sorted first by whether + # the session has any presentations, then by the meeting 'number', then by session's group + # acronym, then by scheduled time (or the time of the session request if the session isn't + # scheduled). + + def time_sort_key(session): + official_sessions = session.scheduledsession_set.filter(schedule=session.meeting.agenda) + if official_sessions: + return official_sessions.first().timeslot.time + else: + return session.requested + + time_sorted = sorted(changeable_sessions,key=time_sort_key) + acronym_sorted = sorted(time_sorted,key=lambda x: x.group.acronym) + meeting_sorted = sorted(acronym_sorted,key=lambda x: x.meeting.number) + sorted_sessions = sorted(meeting_sorted,key=lambda x: '0' if x.has_presentation else '1') + + if seq: + iseq = int(seq) - 1 + if not iseq in range(0,len(sorted_sessions)): + raise Http404 + else: + sorted_sessions = [sorted_sessions[iseq]] + + for index,session in enumerate(sorted_sessions): + session.sequence = index+1 + + if len(sorted_sessions)==1: + session = sorted_sessions[0] + choices = [('notpresented','Not Presented')] + choices.extend([(x,x) for x in doc.docevent_set.filter(type='new_revision').values_list('newrevisiondocevent__rev',flat=True)]) + initial = {'version' : session.version if hasattr(session,'version') else 'notpresented'} + + if request.method == 'POST': + form = MaterialVersionForm(request.POST,choices=choices) + if form.is_valid(): + if request.POST.get("action", "") == "Save": + new_selection = form.cleaned_data['version'] + if initial['version'] != new_selection: + if initial['version'] == 'notpresented': + doc.sessionpresentation_set.create(session=session,rev=new_selection) + elif new_selection == 'notpresented': + doc.sessionpresentation_set.filter(session=session).delete() + else: + doc.sessionpresentation_set.filter(session=session).update(rev=new_selection) + return redirect('doc_view',name=doc.name) + else: + form = MaterialVersionForm(choices=choices,initial=initial) + + return render(request, 'doc/material/edit_material_presentations.html', { + 'session': session, + 'doc': doc, + 'form': form, + }) - if request.method == 'POST': - form = MaterialPresentationForm(request.POST,choices=choices) - if form.is_valid(): - print "STUFF",request.POST.get("action","nothing to be gotten") - if request.POST.get("action", "") == "Save": - new_selections = form.cleaned_data['sesspres'] - doc.sessionpresentation_set.filter(session_id__in=(set(initial['sesspres'])-set(new_selections))).delete() - for sess_pk in set(new_selections)-set(initial['sesspres']): - doc.sessionpresentation_set.create(session_id=sess_pk,rev=doc.rev) - return redirect('doc_view',name=doc.name) else: - form = MaterialPresentationForm(choices=choices, - initial=initial, - ) - - return render(request, 'doc/material/material_presentations.html', { - 'sessions' : sorted_sessions, - 'doc': doc, - 'form': form, - }) + return render(request, 'doc/material/material_presentations.html', { + 'sessions' : sorted_sessions, + 'doc': doc, + }) diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index 1c6f035c5..5fe9adbbe 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -839,10 +839,10 @@ class Session(models.Model): return self.meeting.number ss0name = "(unscheduled)" - ss = self.scheduledsession_set.order_by('timeslot__time') + ss = self.scheduledsession_set.filter(schedule=self.meeting.agenda).order_by('timeslot__time') if ss: - ss0name = ss[0].timeslot.time.strftime("%H%M") - return u"%s: %s %s[%u]" % (self.meeting, self.group.acronym, ss0name, self.pk) + ss0name = ','.join([x.timeslot.time.strftime("%a-%H%M") for x in ss]) + return u"%s: %s %s %s" % (self.meeting, self.group.acronym, self.name, ss0name) def is_bof(self): return self.group.is_bof(); diff --git a/ietf/meeting/urls.py b/ietf/meeting/urls.py index a4443f1fe..f8cce53f7 100644 --- a/ietf/meeting/urls.py +++ b/ietf/meeting/urls.py @@ -53,6 +53,13 @@ urlpatterns = patterns('', (r'^(?P\d+)/sessions.json', ajax.sessions_json), (r'^(?P\d+)/session/(?P\d+).json', ajax.session_json), (r'^(?P\d+)/session/(?P\d+)/constraints.json', ajax.session_constraints), + + (r'^(?P\d+)/session/(?P[A-Za-z0-9_\-\+]+)/$', views.session_details), + (r'^(?P\d+)/session/(?P[A-Za-z0-9_\-\+]+)/(?P\d+)/$', views.session_details), + (r'^(?P\d+)/session/(?P[A-Za-z0-9_\-\+]+)/(?P[a-zA-Z]+)/$', views.session_details), + (r'^(?P\d+)/session/(?P[A-Za-z0-9_\-\+]+)/(?P\d{4}-\d{2}-\d{2}(-\d{4})?)/$', views.session_details), + (r'^(?P\d+)/session/(?P[A-Za-z0-9_\-\+]+)/(?P\d{4}-\d{2}-\d{2}(-\d{4})?)/(?P\d+)/$', views.session_details), + (r'^(?P\d+)/constraint/(?P\d+).json', ajax.constraint_json), (r'^(?P\d+).json$', ajax.meeting_json), (r'^$', views.current_materials), diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index ceaae58b8..556b34e27 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -10,7 +10,7 @@ from tempfile import mkstemp import debug # pyflakes:ignore from django import forms -from django.shortcuts import render_to_response, redirect +from django.shortcuts import render, render_to_response, redirect from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404 from django.core.urlresolvers import reverse from django.db.models import Q @@ -616,3 +616,64 @@ def meeting_requests(request, num=None) : "groups_not_meeting": groups_not_meeting}, context_instance=RequestContext(request)) +def session_details(request, num, acronym, date=None, week_day=None, seq=None) : + meeting = get_meeting(num) + sessions = Session.objects.filter(meeting=meeting,group__acronym=acronym) + + if not sessions: + sessions = Session.objects.filter(meeting=meeting,short=acronym) + + if date: + if len(date)==15: + start = datetime.datetime.strptime(date,"%Y-%m-%d-%H%M") + sessions = sessions.filter(scheduledsession__schedule=meeting.agenda,scheduledsession__timeslot__time=start) + else: + start = datetime.datetime.strptime(date,"%Y-%m-%d").date() + end = start+datetime.timedelta(days=1) + sessions = sessions.filter(scheduledsession__schedule=meeting.agenda,scheduledsession__timeslot__time__range=(start,end)) + + if week_day: + try: + dow = ['sun','mon','tue','wed','thu','fri','sat'].index(week_day.lower()[:3]) + 1 + except ValueError: + raise Http404 + sessions = sessions.filter(scheduledsession__schedule=meeting.agenda,scheduledsession__timeslot__time__week_day=dow) + + + def sort_key(session): + official_sessions = session.scheduledsession_set.filter(schedule=session.meeting.agenda) + if official_sessions: + return official_sessions.first().timeslot.time + else: + return session.requested + + sessions = sorted(sessions,key=sort_key) + + if seq: + iseq = int(seq) - 1 + if not iseq in range(0,len(sessions)): + raise Http404 + else: + sessions= [sessions[iseq]] + + if not sessions: + raise Http404 + + if len(sessions)==1: + session = sessions[0] + scheduled_time = "Not yet scheduled" + ss = session.scheduledsession_set.filter(schedule=meeting.agenda).order_by('timeslot__time') + if ss: + scheduled_time = ','.join([x.timeslot.time.strftime("%A %b-%d %H%M") for x in ss]) + return render(request, "meeting/session_details.html", + { 'session':sessions[0] , + 'meeting' :meeting , + 'acronym' :acronym, + 'time': scheduled_time, + }) + else: + return render(request, "meeting/session_list.html", + { 'sessions':sessions , + 'meeting' :meeting , + 'acronym' :acronym, + }) diff --git a/ietf/templates/doc/material/material_presentations.html b/ietf/templates/doc/material/material_presentations.html index e7196819e..f84f14013 100644 --- a/ietf/templates/doc/material/material_presentations.html +++ b/ietf/templates/doc/material/material_presentations.html @@ -1,23 +1,28 @@ {% extends "base.html" %} {% block title %} -Edit Upcoming Presentations -{% endblock %} - -{% block morecss %} -{{ block.super }} -ul#id_sesspres { list-style-type: none; padding: 0px; margin: 0px; } +Upcoming Presentations {% endblock %} {% block content %} {% load ietf_filters %} -

Edit Upcoming Presentations of
{{doc.title}}
{{doc.name}}

+

Upcoming Presentations of
{{doc.title}}
{{doc.name}}

-
{% csrf_token %} -{{form.as_p}} - - -
+
    +{% regroup sessions by has_presentation as is_scheduled_list %} + {% for is_scheduled in is_scheduled_list %} +
  • {{ is_scheduled.grouper|yesno:"Presentation Scheduled,Presentation Not Scheduled"}} +
      + {% for session in is_scheduled.list %} +
    • + {{ session }} + {% if session.versions %} (version{{session.versions|pluralize}} {{session.versions|join:','}}) {% endif %} +
    • + {% endfor %} +
    +
  • + {% endfor %} +
{% endblock content %}