add new section to proceedings, 6

- Legacy-Id: 5924
This commit is contained in:
Ryan Cross 2013-07-31 07:12:31 +00:00
parent 744e302159
commit 7d6d324c81
2 changed files with 136 additions and 114 deletions

View file

@ -48,7 +48,7 @@ import zipfile
def build_choices(queryset):
'''
This function takes a queryset (or list) of Groups and builds a list of tuples for use
This function takes a queryset (or list) of Groups and builds a list of tuples for use
as choices in a select widget. Using acronym for both value and label.
'''
choices = [ (g.acronym,g.acronym) for g in queryset ]
@ -79,6 +79,17 @@ def get_doc_filename(doc):
# TODO we might want to choose from among multiple files using some logic
return files[0]
def get_extras(meeting):
'''
Gather "extras" which are one off groups. ie iab-wcit(86)
'''
groups = []
sessions = Session.objects.filter(meeting=meeting).exclude(group__parent__acronym__in=('app','gen','int','ops','rai','rtg','sec','tsv','irtf')).filter(timeslot__type='session')
for session in sessions:
if session.materials.all():
groups.append(session.group)
return groups
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
@ -92,13 +103,13 @@ def get_next_interim_num(acronym,date):
return base + str(int(parts[-1]) + 1)
else:
return base + '1'
def get_next_slide_num(session):
'''
This function takes a session object and returns the
next slide number to use for a newly added slide as a string.
'''
"""
slides = session.materials.filter(type='slides').order_by('-name')
if slides:
@ -127,7 +138,7 @@ def get_next_order_num(session):
next slide order number to use for a newly added slide as an integer.
'''
max_order = session.materials.aggregate(Max('order'))['order__max']
return max_order + 1 if max_order else 1
# --- These could be properties/methods on meeting
@ -143,15 +154,15 @@ def get_proceedings_url(meeting,group=None):
url = "%s/proceedings/%s/" % (settings.MEDIA_URL,meeting.number)
if group:
url = url + "%s.html" % group.acronym
elif meeting.type_id == 'interim':
url = "%s/proceedings/interim/%s/%s/proceedings.html" % (
settings.MEDIA_URL,
meeting.date.strftime('%Y/%m/%d'),
group.acronym)
return url
def handle_upload_file(file,filename,meeting,subdir):
def handle_upload_file(file,filename,meeting,subdir):
'''
This function takes a file object, a filename and a meeting object and subdir as string.
It saves the file to the appropriate directory, get_upload_root() + subdir.
@ -159,21 +170,21 @@ def handle_upload_file(file,filename,meeting,subdir):
zip file and unzips the file in the new directory.
'''
base, extension = os.path.splitext(filename)
if extension == '.zip':
path = os.path.join(get_upload_root(meeting),subdir,base)
if not os.path.exists(path):
os.mkdir(path)
else:
path = os.path.join(get_upload_root(meeting),subdir)
# agendas and minutes can only have one file instance so delete file if it already exists
if subdir in ('agenda','minutes'):
old_files = glob.glob(os.path.join(path,base) + '.*')
for f in old_files:
os.remove(f)
destination = open(os.path.join(path,filename), 'wb+')
destination = open(os.path.join(path,filename), 'wb+')
for chunk in file.chunks():
destination.write(chunk)
destination.close()
@ -193,13 +204,13 @@ def make_directories(meeting):
target = os.path.join(path,leaf)
if not os.path.exists(target):
os.makedirs(target)
def parsedate(d):
'''
This function takes a date object and returns a tuple of year,month,day
'''
return (d.strftime('%Y'),d.strftime('%m'),d.strftime('%d'))
# -------------------------------------------------
# AJAX Functions
# -------------------------------------------------
@ -207,17 +218,19 @@ def parsedate(d):
def ajax_generate_proceedings(request, meeting_num):
'''
Ajax function which takes a meeting number and generates the proceedings
pages for the meeting. It returns a snippet of HTML that gets placed in the
pages for the meeting. It returns a snippet of HTML that gets placed in the
Secretariat Only section of the select page.
'''
meeting = get_object_or_404(Meeting, number=meeting_num)
areas = Group.objects.filter(type='area',state='active').order_by('name')
others = TimeSlot.objects.filter(meeting=meeting,type='other').order_by('time')
extras = get_extras(meeting)
context = {'meeting':meeting,
'areas':areas,
'others':others}
'others':others,
'extras':extras}
proceedings_url = get_proceedings_url(meeting)
# the acknowledgement page can be edited manually so only produce if it doesn't already exist
path = os.path.join(settings.SECR_PROCEEDINGS_DIR,meeting.number,'acknowledgement.html')
if not os.path.exists(path):
@ -233,16 +246,16 @@ def ajax_generate_proceedings(request, meeting_num):
gen_irtf(context)
gen_research(context)
gen_group_pages(context)
# get the time proceedings were generated
path = os.path.join(settings.SECR_PROCEEDINGS_DIR,meeting.number,'index.html')
last_run = datetime.datetime.fromtimestamp(os.path.getmtime(path))
return render_to_response('includes/proceedings_functions.html',{
'meeting':meeting,
'last_run':last_run,
'proceedings_url':proceedings_url},
RequestContext(request,{}),
RequestContext(request,{}),
)
@jsonapi
@ -258,13 +271,13 @@ def ajax_order_slide(request):
slide_name = request.POST.get('slide_name',None)
order = request.POST.get('order',None)
slide = get_object_or_404(Document, name=slide_name)
# get all the slides for this session
session = slide.session_set.all()[0]
meeting = session.meeting
group = session.group
qs = session.materials.exclude(states__slug='deleted').filter(type='slides').order_by('order')
# move slide and reorder list
slides = list(qs)
index = slides.index(slide)
@ -274,9 +287,9 @@ def ajax_order_slide(request):
if item.order != ord:
item.order = ord
item.save()
return {'success':True,'order':order,'slide':slide_name}
# --------------------------------------------------
# STANDARD VIEW FUNCTIONS
# --------------------------------------------------
@ -288,13 +301,13 @@ def build(request,meeting_num,acronym):
'''
meeting = Meeting.objects.get(number=meeting_num)
group = get_object_or_404(Group,acronym=acronym)
create_proceedings(meeting,group)
messages.success(request,'proceedings.html was rebuilt')
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting_num,'acronym':acronym})
return HttpResponseRedirect(url)
@check_permissions
def delete_material(request,slide_id):
'''
@ -306,30 +319,30 @@ def delete_material(request,slide_id):
session = doc.session_set.all()[0]
meeting = session.meeting
group = session.group
path = get_full_path(doc)
if path and os.path.exists(path):
os.remove(path)
# leave it related
#session.materials.remove(doc)
state = State.objects.get(type=doc.type,slug='deleted')
doc.set_state(state)
# create deleted_document
DocEvent.objects.create(doc=doc,
by=request.user.get_profile(),
type='deleted')
create_proceedings(meeting,group)
messages.success(request,'The material was deleted successfully')
if group.type.slug in ('wg','rg'):
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':group.acronym})
else:
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'session_id':session.id})
return HttpResponseRedirect(url)
@sec_only
@ -342,17 +355,17 @@ def delete_interim_meeting(request, meeting_num):
meeting = get_object_or_404(Meeting, number=meeting_num)
sessions = Session.objects.filter(meeting=meeting)
group = sessions[0].group
# delete directories
path = get_upload_root(meeting)
# do a quick sanity check on this path before we go and delete it
parts = path.split('/')
assert parts[-1] == group.acronym
if os.path.exists(path):
shutil.rmtree(path)
meeting.delete()
sessions.delete()
@ -369,27 +382,27 @@ def edit_slide(request, slide_id):
session = slide.session_set.all()[0]
meeting = session.meeting
group = session.group
if group.type.slug in ('wg','rg'):
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':group.acronym})
else:
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'session_id':session.id})
if request.method == 'POST': # If the form has been submitted...
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return HttpResponseRedirect(url)
form = EditSlideForm(request.POST, instance=slide) # A form bound to the POST data
if form.is_valid():
if form.is_valid():
form.save()
# rebuild proceedings.html
create_proceedings(meeting,group)
return HttpResponseRedirect(url)
else:
form = EditSlideForm(instance=slide)
return render_to_response('proceedings/edit_slide.html',{
'group': group,
'meeting':meeting,
@ -410,7 +423,7 @@ def interim(request, acronym):
if button_text == 'Back':
url = reverse('proceedings_select_interim')
return HttpResponseRedirect(url)
form = InterimMeetingForm(request.POST) # A form bound to the POST data
if form.is_valid():
date = form.cleaned_data['date']
@ -418,13 +431,13 @@ def interim(request, acronym):
meeting=Meeting.objects.create(type_id='interim',
date=date,
number=number)
# create session to associate this meeting with a group and hold material
Session.objects.create(meeting=meeting,
group=group,
requested_by=request.user.get_profile(),
status_id='sched')
create_interim_directory()
make_directories(meeting)
@ -433,9 +446,9 @@ def interim(request, acronym):
return HttpResponseRedirect(url)
else:
form = InterimMeetingForm(initial={'group_acronym_id':acronym}) # An unbound form
meetings = Meeting.objects.filter(type='interim',session__group__acronym=acronym).order_by('date')
return render_to_response('proceedings/interim_meeting.html',{
'group': group,
'meetings':meetings,
@ -444,7 +457,7 @@ def interim(request, acronym):
)
def interim_directory(request, sortby=None):
if sortby == 'group':
qs = InterimMeeting.objects.all()
meetings = sorted(qs, key=lambda a: a.group.acronym)
@ -474,27 +487,27 @@ def main(request):
person = request.user.get_profile()
except Person.DoesNotExist:
return HttpResponseForbidden('ACCESS DENIED: user=%s' % request.META['REMOTE_USER'])
if has_role(request.user,'Secretariat'):
meetings = Meeting.objects.filter(type='ietf').order_by('-number')
else:
# select meetings still within the cutoff period
meetings = Meeting.objects.filter(type='ietf',date__gt=datetime.datetime.today() - datetime.timedelta(days=settings.SUBMISSION_CORRECTION_DAYS)).order_by('number')
groups = get_my_groups(request.user)
interim_meetings = Meeting.objects.filter(type='interim',session__group__in=groups).order_by('-date')
# tac on group for use in templates
for m in interim_meetings:
m.group = m.session_set.all()[0].group
# we today's date to see if we're past the submissio cutoff
today = datetime.date.today()
return render_to_response('proceedings/main.html',{
'meetings': meetings,
'interim_meetings': interim_meetings,
'today': today},
RequestContext(request,{}),
RequestContext(request,{}),
)
@check_permissions
@ -504,13 +517,13 @@ def move_slide(request, slide_id, direction):
a direction argument which is a string [up|down].
'''
slide = get_object_or_404(Document, name=slide_id)
# derive other objects
session = slide.session_set.all()[0]
meeting = session.meeting
group = session.group
qs = session.materials.exclude(states__slug='deleted').filter(type='slides').order_by('order')
# if direction is up and we aren't already the first slide
if direction == 'up' and slide_id != str(qs[0].pk):
index = find_index(slide_id, qs)
@ -526,7 +539,7 @@ def move_slide(request, slide_id, direction):
slide_after.order, slide.order = slide.order, slide_after.order
slide.save()
slide_after.save()
if group.type.slug in ('wg','rg'):
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':group.acronym})
else:
@ -539,7 +552,7 @@ def process_pdfs(request, meeting_num):
This function is used to update the database once meeting materials in PPT format
are converted to PDF format and uploaded to the server. It basically finds every PowerPoint
slide document for the given meeting and checks to see if there is a PDF version. If there
is external_url is changed. Then when proceedings are generated the URL will refer to the
is external_url is changed. Then when proceedings are generated the URL will refer to the
PDF document.
'''
warn_count = 0
@ -557,14 +570,14 @@ def process_pdfs(request, meeting_num):
count += 1
else:
warn_count += 1
if warn_count:
messages.warning(request, '%s PDF files processed. %s PowerPoint files still not converted.' % (count, warn_count))
else:
messages.success(request, '%s PDF files processed' % count)
url = reverse('proceedings_select', kwargs={'meeting_num':meeting_num})
return HttpResponseRedirect(url)
@sec_only
def progress_report(request, meeting_num):
'''
@ -572,10 +585,10 @@ def progress_report(request, meeting_num):
'''
meeting = get_object_or_404(Meeting, number=meeting_num)
gen_progress({'meeting':meeting},final=False)
url = reverse('proceedings_select', kwargs={'meeting_num':meeting_num})
return HttpResponseRedirect(url)
@check_permissions
def replace_slide(request, slide_id):
'''
@ -591,37 +604,37 @@ def replace_slide(request, slide_id):
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':group.acronym})
else:
url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'session_id':session.id})
if request.method == 'POST': # If the form has been submitted...
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return HttpResponseRedirect(url)
form = ReplaceSlideForm(request.POST,request.FILES,instance=slide) # A form bound to the POST data
if form.is_valid():
if form.is_valid():
new_slide = form.save(commit=False)
new_slide.time = datetime.datetime.now()
file = request.FILES[request.FILES.keys()[0]]
file_ext = os.path.splitext(file.name)[1]
disk_filename = new_slide.name + file_ext
handle_upload_file(file,disk_filename,meeting,'slides')
new_slide.external_url = disk_filename
new_slide.save()
# create DocEvent uploaded
DocEvent.objects.create(doc=slide,
by=request.user.get_profile(),
type='uploaded')
# rebuild proceedings.html
create_proceedings(meeting,group)
return HttpResponseRedirect(url)
else:
form = ReplaceSlideForm(instance=slide)
return render_to_response('proceedings/replace_slide.html',{
'group': group,
'meeting':meeting,
@ -645,31 +658,31 @@ def select(request, meeting_num):
else:
messages.error(request, 'No Group selected')
meeting = get_object_or_404(Meeting, number=meeting_num)
user = request.user
person = user.get_profile()
groups_session, groups_no_session = groups_by_session(user, meeting)
proceedings_url = get_proceedings_url(meeting)
# get the time proceedings were generated
path = os.path.join(settings.SECR_PROCEEDINGS_DIR,meeting.number,'index.html')
if os.path.exists(path):
last_run = datetime.datetime.fromtimestamp(os.path.getmtime(path))
else:
last_run = None
# initialize group form
wgs = filter(lambda x: x.type_id in ('wg','ag','team'),groups_session)
group_form = GroupSelectForm(choices=build_choices(wgs))
# intialize IRTF form, only show if user is sec or irtf chair
if has_role(user,'Secretariat') or person.role_set.filter(name__slug='chair',group__type__slug__in=('irtf','rg')):
rgs = filter(lambda x: x.type_id == 'rg',groups_session)
irtf_form = GroupSelectForm(choices=build_choices(rgs))
else:
irtf_form = None
# initialize Training form, this select widget needs to have a session id, because it's
# utilmately the session that we associate material with
# NOTE: there are two ways to query for the groups we want, the later seems more specific
@ -681,7 +694,7 @@ def select(request, meeting_num):
training_form = GroupSelectForm(choices=choices)
else:
training_form = None
# iniialize plenary form
if has_role(user,['Secretariat','IETF Chair','IAB Chair']):
choices = []
@ -691,7 +704,7 @@ def select(request, meeting_num):
plenary_form = GroupSelectForm(choices=choices)
else:
plenary_form = None
# count PowerPoint files waiting to be converted
if has_role(user,'Secretariat'):
ppt = Document.objects.filter(session__meeting=meeting,type='slides',external_url__endswith='.ppt').exclude(states__slug='deleted')
@ -699,7 +712,7 @@ def select(request, meeting_num):
ppt_count = ppt.count() + pptx.count()
else:
ppt_count = 0
return render_to_response('proceedings/select.html', {
'group_form': group_form,
'irtf_form': irtf_form,
@ -709,7 +722,7 @@ def select(request, meeting_num):
'last_run': last_run,
'proceedings_url': proceedings_url,
'ppt_count': ppt_count},
RequestContext(request,{}),
RequestContext(request,{}),
)
def select_interim(request):
@ -720,17 +733,17 @@ def select_interim(request):
if request.method == 'POST':
redirect_url = reverse('proceedings_interim', kwargs={'acronym':request.POST['group']})
return HttpResponseRedirect(redirect_url)
if request.user_is_secretariat:
# initialize working groups form
choices = build_choices(Group.objects.active_wgs())
group_form = GroupSelectForm(choices=choices)
# per Alexa, not supporting Interim IRTF meetings at this time
# intialize IRTF form
#choices = build_choices(Group.objects.filter(type='wg', state='active')
#irtf_form = GroupSelectForm(choices=choices)
else:
# these forms aren't used for non-secretariat
groups = get_my_groups(request.user)
@ -738,11 +751,11 @@ def select_interim(request):
group_form = GroupSelectForm(choices=choices)
irtf_form = None
training_form = None
return render_to_response('proceedings/interim_select.html', {
'group_form': group_form},
#'irtf_form': irtf_form,
RequestContext(request,{}),
RequestContext(request,{}),
)
@check_permissions
@ -750,9 +763,9 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
'''
This view is the main view for uploading / re-ordering material for regular and interim
meetings. There are two urls.py entries which map to this view. The acronym_id option is used
most often for groups of regular and interim meetings. session_id is used for uploading
most often for groups of regular and interim meetings. session_id is used for uploading
material for Training sessions (where group is not a unique identifier). We could have used
session_id all the time but this makes for an ugly URL which most of the time would be
session_id all the time but this makes for an ugly URL which most of the time would be
avoided by using acronym.
'''
meeting = get_object_or_404(Meeting, number=meeting_num)
@ -767,7 +780,7 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
session = get_object_or_404(Session, id=int(session_id))
group = session.group
session_name = session.name
if request.method == 'POST':
button_text = request.POST.get('submit','')
if button_text == 'Back':
@ -776,12 +789,12 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
else:
url = reverse('proceedings_select', kwargs={'meeting_num':meeting_num})
return HttpResponseRedirect(url)
form = UnifiedUploadForm(request.POST,request.FILES)
if form.is_valid():
material_type = form.cleaned_data['material_type']
slide_name = form.cleaned_data['slide_name']
file = request.FILES[request.FILES.keys()[0]]
file_ext = os.path.splitext(file.name)[1]
@ -790,21 +803,21 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
filename = '%s-%s-%s' % (material_type.slug,meeting.number,group.acronym)
elif meeting.type.slug == 'interim':
filename = '%s-%s' % (material_type.slug,meeting.number)
# NonSession material, use short name for shorter URLs
if session.short:
filename += "-%s" % session.short
elif session_name:
filename += "-%s" % slugify(session_name)
# --------------------------------
if material_type.slug == 'slides':
order_num = get_next_order_num(session)
slide_num = get_next_slide_num(session)
filename += "-%s" % slide_num
disk_filename = filename + file_ext
# create the Document object, in the case of slides the name will always be unique
# so you'll get a new object, agenda and minutes will reuse doc object if it exists
doc, created = Document.objects.get_or_create(type=material_type,
@ -827,13 +840,13 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
doc.save()
DocAlias.objects.get_or_create(name=doc.name, document=doc)
handle_upload_file(file,disk_filename,meeting,material_type.slug)
# set Doc state
state = State.objects.get(type=doc.type,slug='active')
doc.set_state(state)
# create session relationship, per Henrik we should associate documents to all sessions
# for the current meeting (until tools support different materials for diff sessions)
if sessions:
@ -841,7 +854,7 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
s.materials.add(doc)
else:
session.materials.add(doc)
# create NewRevisionDocEvent instead of uploaded, per Ole
NewRevisionDocEvent.objects.create(type='new_revision',
by=request.user.get_profile(),
@ -849,26 +862,26 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
rev=doc.rev,
desc='New revision available',
time=now)
create_proceedings(meeting,group)
messages.success(request,'File uploaded sucessfully')
else:
form = UnifiedUploadForm(initial={'meeting_id':meeting.id,'acronym':group.acronym,'material_type':'slides'})
agenda,minutes,slides = get_material(session)
# gather DocEvents
# include deleted material to catch deleted doc events
docs = session.materials.all()
docevents = DocEvent.objects.filter(doc__in=docs)
path = get_proceedings_path(meeting,group)
if os.path.exists(path):
proceedings_url = get_proceedings_url(meeting,group)
else:
else:
proceedings_url = ''
return render_to_response('proceedings/upload_unified.html', {
'docevents': docevents,
'meeting': meeting,

View file

@ -10,11 +10,11 @@
<hr />
<h3><a name="intro" id="intro"></a>1. Introduction </h3>
<ul>
<li><a href="acknowledgement.html">1.1. Acknowledgements</a></li>
<li><a href="overview.html">1.2. IETF Overview</a></li>
<li><a href="progress-report.html">1.3. Progress Report</a></li>
<li><a href="agenda.html">1.4. Agenda</a></li>
<li><a href="attendee.html">1.5. Attendees</a></li>
<li><a href="acknowledgement.html">1.1 Acknowledgements</a></li>
<li><a href="overview.html">1.2 IETF Overview</a></li>
<li><a href="progress-report.html">1.3 Progress Report</a></li>
<li><a href="agenda.html">1.4 Agenda</a></li>
<li><a href="attendee.html">1.5 Attendees</a></li>
</ul>
<hr />
<h3><a name="wgreports" id="wgreports"></a>2. Area, Working Group and BoF Reports </h3>
@ -26,8 +26,8 @@
<hr />
<h3><a name="plenary" id="plenary"></a>3. Plenaries</h3>
<ul>
<li><a href ="administrative-plenary.html">3.1. Administrative Plenary</a></li>
<li><a href="technical-plenary.html">3.2. Technical Plenary</a></li>
<li><a href ="administrative-plenary.html">3.1 Administrative Plenary</a></li>
<li><a href="technical-plenary.html">3.2 Technical Plenary</a></li>
</ul>
<hr />
<h3><a name="training" id="training">4. Training</h3>
@ -39,9 +39,18 @@
<hr />
<h3><a name="irtf" id="irtf">5. Internet Research Task Force </h3>
<ul>
<li><a href="irtf.html">5.1. IRTF introduction</a></li>
<li><a href="rg_irtf.html">5.2. Research Groups</a></li>
<li><a href="irtf.html">5.1 IRTF introduction</a></li>
<li><a href="rg_irtf.html">5.2 Research Groups</a></li>
</ul>
<hr />
{% if extras %}
<h3>6. Other</h3>
<ul>
{% for group in extras %}
<li><a href="{{ group.acronym }}.html">6.{{ forloop.counter }} {{ group.name }}</a><
/li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}