Skip to content

Commit c436607

Browse files
committed
merged forward. Adjusted new proceedings code to new models. Found bug in earlier work on proceedings code exposing places that tests are not covering.
- Legacy-Id: 8589
2 parents b74d4e7 + c05d734 commit c436607

24 files changed

Lines changed: 1453 additions & 35 deletions

ietf/doc/migrations/0024_archive_slides.py

Lines changed: 398 additions & 0 deletions
Large diffs are not rendered by default.

ietf/doc/models.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,37 @@ def active_ballot(self):
186186
return None
187187

188188
def meeting_related(self):
189+
if self.type_id in ("agenda","minutes",):
190+
return (self.name.split("-")[1] == "interim"
191+
or (self.session_set.exists() if isinstance(self, Document) else self.doc.session_set.exists()))
192+
elif self.type_id in ("slides",):
193+
return (self.name.split("-")[1] == "interim"
194+
or (self.get_state('slides') in ("sessonly","archived") ))
195+
else:
196+
return False
197+
189198
return(self.type_id in ("agenda", "minutes", "slides") and (
190199
self.name.split("-")[1] == "interim"
191200
or (self.session_set.exists() if isinstance(self, Document) else self.doc.session_set.exists())))
192201

202+
def future_presentations(self):
203+
""" returns related SessionPresentation objects for meetings that
204+
have not yet ended. This implementation allows for 2 week meetings """
205+
candidate_presentations = self.sessionpresentation_set.filter(session__meeting__date__gte=datetime.date.today()-datetime.timedelta(days=15))
206+
return [pres for pres in candidate_presentations if pres.session.meeting.end_date()>=datetime.date.today()]
207+
208+
def last_presented(self):
209+
""" returns related SessionPresentation objects for the most recent meeting in the past"""
210+
# Assumes no two meetings have the same start date - if the assumption is violated, one will be chosen arbitrariy
211+
candidate_presentations = self.sessionpresentation_set.filter(session__meeting__date__lte=datetime.date.today())
212+
candidate_meetings = set([p.session.meeting for p in candidate_presentations if p.session.meeting.end_date()<datetime.date.today()])
213+
if candidate_meetings:
214+
mtg = sorted(list(candidate_meetings),key=lambda x:x.date,reverse=True)[0]
215+
return self.sessionpresentation_set.filter(session__meeting=mtg)
216+
else:
217+
return None
218+
219+
193220
class Meta:
194221
abstract = True
195222

ietf/doc/templatetags/ietf_filters.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,29 @@ def format_history_text(text):
448448
if text.startswith("This was part of a ballot set with:"):
449449
full = urlize_ietf_docs(full)
450450

451-
full = mark_safe(keep_spacing(linebreaksbr(urlize(sanitize_html(full)))))
451+
return format_snippet(full)
452+
453+
@register.filter
454+
def format_snippet(text):
455+
full = mark_safe(keep_spacing(linebreaksbr(urlize(sanitize_html(text)))))
452456
snippet = truncatewords_html(full, 25)
453457
if snippet != full:
454458
return mark_safe(u'<div class="snippet">%s<span class="show-all">[show all]</span></div><div style="display:none" class="full">%s</div>' % (snippet, full))
455459
return full
456460

461+
@register.filter
462+
def format_editable_snippet(text,link):
463+
full = mark_safe(keep_spacing(linebreaksbr(urlize(sanitize_html(text)))))
464+
snippet = truncatewords_html(full, 25)
465+
if snippet != full:
466+
return mark_safe(u'<div class="snippet">%s<span class="show-all">[show all]</span></div><div style="display:none" class="full">%s' % (format_editable(snippet,link),format_editable(full,link)) )
467+
else:
468+
return format_editable(full,link)
469+
470+
@register.filter
471+
def format_editable(text,link):
472+
return mark_safe(u'<a class="editlink" href="%s">%s</a>' % (link,text))
473+
457474
@register.filter
458475
def textify(text):
459476
text = re.sub("</?b>", "*", text)

ietf/doc/tests_material.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def test_upload_slides(self):
6666

6767
# post
6868
r = self.client.post(url, dict(title="Test File - with fancy title",
69+
abstract = "Test Abstract",
6970
name="slides-%s-test-file" % group.acronym,
7071
state=State.objects.get(type="slides", slug="active").pk,
7172
material=test_file))
@@ -125,6 +126,7 @@ def test_revise(self):
125126

126127
# post
127128
r = self.client.post(url, dict(title="New title",
129+
abstract="New abstract",
128130
state=State.objects.get(type="slides", slug="active").pk,
129131
material=test_file))
130132
self.assertEqual(r.status_code, 302)

ietf/doc/urls_material.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
from django.conf.urls import patterns, url
22

33
urlpatterns = patterns('ietf.doc.views_material',
4-
url(r'^(?P<action>state|title|revise)/$', "edit_material", name="material_edit"),
4+
url(r'^(?P<action>state|title|abstract|revise)/$', "edit_material", name="material_edit"),
5+
url(r'^sessions/$', "material_presentations", name="material_presentations"),
6+
(r'^sessions/(?P<seq>\d+)/$', "material_presentations"),
7+
(r'^sessions/(?P<acronym>[A-Za-z0-9_\-\+]+)/$', "material_presentations"),
8+
(r'^sessions/(?P<acronym>[A-Za-z0-9_\-\+]+)/(?P<seq>\d+)/$', "material_presentations"),
9+
(r'^sessions/(?P<acronym>[A-Za-z0-9_\-\+]+)/(?P<week_day>[a-zA-Z]+)/$', "material_presentations"),
10+
(r'^sessions/(?P<acronym>[A-Za-z0-9_\-\+]+)/(?P<date>\d{4}-\d{2}-\d{2}(-\d{4})?)/$', "material_presentations"),
11+
(r'^sessions/(?P<acronym>[A-Za-z0-9_\-\+]+)/(?P<date>\d{4}-\d{2}-\d{2}(-\d{4})?)/(?P<seq>\d+)/$', "material_presentations"),
512
)
613

ietf/doc/views_doc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,9 @@ def document_main(request, name, rev=None):
497497

498498
if doc.type_id in ("slides", "agenda", "minutes"):
499499
can_manage_material = can_manage_materials(request.user, doc.group)
500+
presentations = None
501+
if doc.type_id=='slides' and doc.get_state_slug('slides') in ['sessonly','active']:
502+
presentations = doc.future_presentations()
500503
if doc.meeting_related():
501504
# disallow editing meeting-related stuff through this
502505
# interface for the time being
@@ -534,6 +537,7 @@ def document_main(request, name, rev=None):
534537
snapshot=snapshot,
535538
can_manage_material=can_manage_material,
536539
other_types=other_types,
540+
presentations=presentations,
537541
),
538542
context_instance=RequestContext(request))
539543

ietf/doc/views_material.py

Lines changed: 128 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from ietf.doc.utils import add_state_change_event, check_common_doc_name_rules
1818
from ietf.group.models import Group
1919
from ietf.group.utils import can_manage_materials
20+
from ietf.meeting.models import Session
2021

2122
@login_required
2223
def choose_material_type(request, acronym):
@@ -32,8 +33,9 @@ def choose_material_type(request, acronym):
3233
class UploadMaterialForm(forms.Form):
3334
title = forms.CharField(max_length=Document._meta.get_field("title").max_length)
3435
name = forms.CharField(max_length=Document._meta.get_field("name").max_length)
36+
abstract = forms.CharField(max_length=Document._meta.get_field("abstract").max_length,widget=forms.Textarea)
3537
state = forms.ModelChoiceField(State.objects.all(), empty_label=None)
36-
material = forms.FileField(label='File', help_text="PDF or text file (ASCII/UTF-8)")
38+
material = forms.FileField(label='File')
3739

3840
def __init__(self, doc_type, action, group, doc, *args, **kwargs):
3941
super(UploadMaterialForm, self).__init__(*args, **kwargs)
@@ -53,16 +55,15 @@ def __init__(self, doc_type, action, group, doc, *args, **kwargs):
5355
del self.fields["name"]
5456

5557
self.fields["title"].initial = doc.title
58+
self.fields["abstract"].initial = doc.abstract
5659
self.fields["state"].initial = doc.get_state().pk if doc.get_state() else None
5760
if doc.get_state_slug() == "deleted":
5861
self.fields["state"].help_text = "Note: If you wish to revise this document, you may wish to change the state so it's not deleted."
5962

60-
if action == "title":
61-
del self.fields["state"]
62-
del self.fields["material"]
63-
elif action == "state":
64-
del self.fields["title"]
65-
del self.fields["material"]
63+
if action in ["title","state","abstract"]:
64+
for fieldname in ["title","state","material","abstract"]:
65+
if fieldname != action:
66+
del self.fields[fieldname]
6667

6768
def clean_name(self):
6869
name = self.cleaned_data["name"].strip().rstrip("-")
@@ -120,6 +121,9 @@ def edit_material(request, name=None, acronym=None, action=None, doc_type=None):
120121
if "title" in form.cleaned_data:
121122
doc.title = form.cleaned_data["title"]
122123

124+
if "abstract" in form.cleaned_data:
125+
doc.abstract = form.cleaned_data["abstract"]
126+
123127
doc.time = datetime.datetime.now()
124128

125129
if "material" in form.fields:
@@ -168,3 +172,120 @@ def edit_material(request, name=None, acronym=None, action=None, doc_type=None):
168172
'document_type': document_type,
169173
'doc_name': doc.name if doc else "",
170174
})
175+
176+
class MaterialVersionForm(forms.Form):
177+
178+
version = forms.ChoiceField(required=False,
179+
label='Which version of this document will be presented at this session')
180+
181+
def __init__(self, *args, **kwargs):
182+
choices = kwargs.pop('choices')
183+
super(MaterialVersionForm,self).__init__(*args,**kwargs)
184+
self.fields['version'].choices = choices
185+
186+
@login_required
187+
def material_presentations(request, name, acronym=None, date=None, seq=None, week_day=None):
188+
189+
doc = get_object_or_404(Document, name=name)
190+
if not (doc.type_id=='slides' and doc.get_state('slides').slug=='active'):
191+
raise Http404
192+
193+
group = doc.group
194+
if not (group.features.has_materials and can_manage_materials(request.user,group)):
195+
raise Http404
196+
197+
# Find all the sessions for meetings that haven't ended that the user could affect
198+
# This motif is also in Document.future_presentations - it would be nice to consolodate it somehow
199+
200+
candidate_sessions = Session.objects.exclude(status__in=['canceled','disappr','notmeet','deleted']).filter(meeting__date__gte=datetime.date.today()-datetime.timedelta(days=15))
201+
refined_candidates = [ sess for sess in candidate_sessions if sess.meeting.end_date()>=datetime.date.today()]
202+
203+
if acronym:
204+
refined_candidates = [ sess for sess in refined_candidates if sess.group.acronym==acronym]
205+
206+
if date:
207+
if len(date)==15:
208+
start = datetime.datetime.strptime(date,"%Y-%m-%d-%H%M")
209+
refined_candidates = [ sess for sess in refined_candidates if sess.scheduledsession_set.filter(schedule=sess.meeting.agenda,timeslot__time=start) ]
210+
else:
211+
start = datetime.datetime.strptime(date,"%Y-%m-%d").date()
212+
end = start+datetime.timedelta(days=1)
213+
refined_candidates = [ sess for sess in refined_candidates if sess.scheduledsession_set.filter(schedule=sess.meeting.agenda,timeslot__time__range=(start,end)) ]
214+
215+
if week_day:
216+
try:
217+
dow = ['sun','mon','tue','wed','thu','fri','sat'].index(week_day.lower()[:3]) + 1
218+
except ValueError:
219+
raise Http404
220+
refined_candidates = [ sess for sess in refined_candidates if sess.scheduledsession_set.filter(schedule=sess.meeting.agenda,timeslot__time__week_day=dow) ]
221+
222+
changeable_sessions = [ sess for sess in refined_candidates if can_manage_materials(request.user, sess.group) ]
223+
224+
if not changeable_sessions:
225+
raise Http404
226+
227+
for sess in changeable_sessions:
228+
sess.has_presentation = bool(sess.sessionpresentation_set.filter(document=doc))
229+
if sess.has_presentation:
230+
sess.version = sess.sessionpresentation_set.get(document=doc).rev
231+
232+
# Since Python 2.2 sorts are stable, so this series results in a list sorted first by whether
233+
# the session has any presentations, then by the meeting 'number', then by session's group
234+
# acronym, then by scheduled time (or the time of the session request if the session isn't
235+
# scheduled).
236+
237+
def time_sort_key(session):
238+
official_sessions = session.scheduledsession_set.filter(schedule=session.meeting.agenda)
239+
if official_sessions:
240+
return official_sessions.first().timeslot.time
241+
else:
242+
return session.requested
243+
244+
time_sorted = sorted(changeable_sessions,key=time_sort_key)
245+
acronym_sorted = sorted(time_sorted,key=lambda x: x.group.acronym)
246+
meeting_sorted = sorted(acronym_sorted,key=lambda x: x.meeting.number)
247+
sorted_sessions = sorted(meeting_sorted,key=lambda x: '0' if x.has_presentation else '1')
248+
249+
if seq:
250+
iseq = int(seq) - 1
251+
if not iseq in range(0,len(sorted_sessions)):
252+
raise Http404
253+
else:
254+
sorted_sessions = [sorted_sessions[iseq]]
255+
256+
for index,session in enumerate(sorted_sessions):
257+
session.sequence = index+1
258+
259+
if len(sorted_sessions)==1:
260+
session = sorted_sessions[0]
261+
choices = [('notpresented','Not Presented')]
262+
choices.extend([(x,x) for x in doc.docevent_set.filter(type='new_revision').values_list('newrevisiondocevent__rev',flat=True)])
263+
initial = {'version' : session.version if hasattr(session,'version') else 'notpresented'}
264+
265+
if request.method == 'POST':
266+
form = MaterialVersionForm(request.POST,choices=choices)
267+
if form.is_valid():
268+
if request.POST.get("action", "") == "Save":
269+
new_selection = form.cleaned_data['version']
270+
if initial['version'] != new_selection:
271+
if initial['version'] == 'notpresented':
272+
doc.sessionpresentation_set.create(session=session,rev=new_selection)
273+
elif new_selection == 'notpresented':
274+
doc.sessionpresentation_set.filter(session=session).delete()
275+
else:
276+
doc.sessionpresentation_set.filter(session=session).update(rev=new_selection)
277+
return redirect('doc_view',name=doc.name)
278+
else:
279+
form = MaterialVersionForm(choices=choices,initial=initial)
280+
281+
return render(request, 'doc/material/edit_material_presentations.html', {
282+
'session': session,
283+
'doc': doc,
284+
'form': form,
285+
})
286+
287+
else:
288+
return render(request, 'doc/material/material_presentations.html', {
289+
'sessions' : sorted_sessions,
290+
'doc': doc,
291+
})

ietf/group/info.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ def concluded_groups(request):
267267
dict(group_types=group_types))
268268

269269
def get_group_materials(group):
270-
return Document.objects.filter(group=group, type__in=group.features.material_types, session=None).exclude(states__slug="deleted")
270+
# return Document.objects.filter(group=group, type__in=group.features.material_types, session=None).exclude(states__slug="deleted")
271+
return Document.objects.filter(group=group, type__in=group.features.material_types).exclude(states__slug__in=['deleted','archived'])
271272

272273
def construct_group_menu_context(request, group, selected, group_type, others):
273274
"""Return context with info for the group menu filled in."""

0 commit comments

Comments
 (0)