# Copyright The IETF Trust 2007, All Rights Reserved import datetime import os import re import tarfile import debug import urllib import json from tempfile import mkstemp from django import forms from django.shortcuts import render_to_response, get_object_or_404, redirect from django.http import HttpResponseRedirect, HttpResponse, Http404 from django.core.urlresolvers import reverse from django.db.models import Q from django.template import RequestContext from django.template.loader import render_to_string from django.conf import settings from django.utils.decorators import decorator_from_middleware from django.middleware.gzip import GZipMiddleware from django.db.models import Max from django.forms.models import modelform_factory from ietf.utils.pipe import pipe from ietf.ietfauth.utils import role_required, has_role from ietf.doc.models import Document, State from ietf.person.models import Person from ietf.meeting.models import Meeting, TimeSlot, Session, Schedule from ietf.group.models import Group from ietf.meeting.helpers import get_areas from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list from ietf.meeting.helpers import get_scheduledsessions_from_schedule, get_all_scheduledsessions_from_schedule from ietf.meeting.helpers import get_modified_from_scheduledsessions from ietf.meeting.helpers import get_wg_list, find_ads_for_meeting from ietf.meeting.helpers import get_meeting, get_schedule, agenda_permissions, meeting_updated @decorator_from_middleware(GZipMiddleware) def materials(request, meeting_num=None): meeting = get_meeting(meeting_num) begin_date = meeting.get_submission_start_date() cut_off_date = meeting.get_submission_cut_off_date() cor_cut_off_date = meeting.get_submission_correction_date() now = datetime.date.today() if settings.SERVER_MODE != 'production' and '_testoverride' in request.REQUEST: pass elif now > cor_cut_off_date: return render_to_response("meeting/materials_upload_closed.html", { 'meeting_num': meeting_num, 'begin_date': begin_date, 'cut_off_date': cut_off_date, 'cor_cut_off_date': cor_cut_off_date }, context_instance=RequestContext(request)) #sessions = Session.objects.filter(meeting__number=meeting_num, timeslot__isnull=False) schedule = get_schedule(meeting, None) sessions = Session.objects.filter(meeting__number=meeting_num, scheduledsession__schedule=schedule) plenaries = sessions.filter(name__icontains='plenary') ietf = sessions.filter(group__parent__type__slug = 'area').exclude(group__acronym='edu') irtf = sessions.filter(group__parent__acronym = 'irtf') training = sessions.filter(group__acronym__in=['edu','iaoc']) iab = sessions.filter(group__parent__acronym = 'iab') cache_version = Document.objects.filter(session__meeting__number=meeting_num).aggregate(Max('time'))["time__max"] return render_to_response("meeting/materials.html", { 'meeting_num': meeting_num, 'plenaries': plenaries, 'ietf': ietf, 'training': training, 'irtf': irtf, 'iab': iab, 'cut_off_date': cut_off_date, 'cor_cut_off_date': cor_cut_off_date, 'submission_started': now > begin_date, 'cache_version': cache_version, }, context_instance=RequestContext(request)) def current_materials(request): meetings = Meeting.objects.exclude(number__startswith='interim-').order_by('-number') if meetings: return redirect(materials, meetings[0].number) else: raise Http404 def get_user_agent(request): if settings.SERVER_MODE != 'production' and '_testiphone' in request.REQUEST: user_agent = "iPhone" elif 'user_agent' in request.REQUEST: user_agent = request.REQUEST['user_agent'] elif 'HTTP_USER_AGENT' in request.META: user_agent = request.META["HTTP_USER_AGENT"] else: user_agent = "" return user_agent class SaveAsForm(forms.Form): savename = forms.CharField(max_length=100) @role_required('Area Director','Secretariat') def agenda_create(request, num=None, schedule_name=None): meeting = get_meeting(num) schedule = get_schedule(meeting, schedule_name) if schedule is None: # here we have to return some ajax to display an error. raise Http404("No meeting information for meeting %s schedule %s available" % (num,schedule_name)) # authorization was enforced by the @group_require decorator above. saveasform = SaveAsForm(request.POST) if not saveasform.is_valid(): return HttpResponse(status=404) savedname = saveasform.cleaned_data['savename'] # create the new schedule, and copy the scheduledsessions try: sched = meeting.schedule_set.get(name=savedname, owner=request.user.person) if sched: # XXX needs to record a session error and redirect to where? return HttpResponseRedirect( reverse(edit_agenda, args=[meeting.number, sched.name])) except Schedule.DoesNotExist: pass # must be done newschedule = Schedule(name=savedname, owner=request.user.person, meeting=meeting, visible=False, public=False) newschedule.save() if newschedule is None: return HttpResponse(status=500) # keep a mapping so that extendedfrom references can be chased. mapping = {}; for ss in schedule.scheduledsession_set.all(): # hack to copy the object, creating a new one # just reset the key, and save it again. oldid = ss.pk ss.pk = None ss.schedule=newschedule ss.save() mapping[oldid] = ss.pk #print "Copying %u to %u" % (oldid, ss.pk) # now fix up any extendedfrom references to new set. for ss in newschedule.scheduledsession_set.all(): if ss.extendedfrom is not None: oldid = ss.extendedfrom.id newid = mapping[oldid] #print "Fixing %u to %u" % (oldid, newid) ss.extendedfrom = newschedule.scheduledsession_set.get(pk = newid) ss.save() # now redirect to this new schedule. return HttpResponseRedirect( reverse(edit_agenda, args=[meeting.number, newschedule.name])) @decorator_from_middleware(GZipMiddleware) def edit_timeslots(request, num=None): meeting = get_meeting(num) timeslots = meeting.timeslot_set.exclude(location__isnull = True).all() time_slices,date_slices,slots = meeting.build_timeslices() meeting_base_url = request.build_absolute_uri(meeting.base_url()) site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash rooms = meeting.room_set.order_by("capacity") rooms = rooms.all() from ietf.meeting.ajax import timeslot_roomsurl, AddRoomForm, timeslot_slotsurl, AddSlotForm roomsurl =reverse(timeslot_roomsurl, args=[meeting.number]) adddayurl =reverse(timeslot_slotsurl, args=[meeting.number]) return HttpResponse(render_to_string("meeting/timeslot_edit.html", {"timeslots": timeslots, "meeting_base_url": meeting_base_url, "site_base_url": site_base_url, "rooms":rooms, "addroom": AddRoomForm(), "roomsurl": roomsurl, "addday": AddSlotForm(), "adddayurl":adddayurl, "time_slices":time_slices, "slot_slices": slots, "date_slices":date_slices, "meeting":meeting}, RequestContext(request)), content_type="text/html") ############################################################################## #@role_required('Area Director','Secretariat') # disable the above security for now, check it below. @decorator_from_middleware(GZipMiddleware) def edit_agenda(request, num=None, schedule_name=None): if request.method == 'POST': return agenda_create(request, num, schedule_name) user = request.user requestor = "AnonymousUser" if not user.is_anonymous(): #print "user: %s" % (user) try: requestor = user.get_profile() except Person.DoesNotExist: # if we can not find them, leave them alone, only used for debugging. pass meeting = get_meeting(num) #sys.stdout.write("requestor: %s for sched_name: %s \n" % ( requestor, schedule_name )) schedule = get_schedule(meeting, schedule_name) #sys.stdout.write("2 requestor: %u for sched owned by: %u \n" % ( requestor.id, schedule.owner.id )) meeting_base_url = request.build_absolute_uri(meeting.base_url()) site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash rooms = meeting.room_set.order_by("capacity") rooms = rooms.all() saveas = SaveAsForm() saveasurl=reverse(edit_agenda, args=[meeting.number, schedule.name]) can_see, can_edit = agenda_permissions(meeting, schedule, user) if not can_see: #sys.stdout.write("visible: %s public: %s owner: %s request from: %s\n" % ( # schedule.visible, schedule.public, schedule.owner, requestor)) return HttpResponse(render_to_string("meeting/private_agenda.html", {"schedule":schedule, "meeting": meeting, "meeting_base_url":meeting_base_url}, RequestContext(request)), status=403, content_type="text/html") sessions = meeting.sessions_that_can_meet.order_by("id", "group", "requested_by") scheduledsessions = get_all_scheduledsessions_from_schedule(schedule) session_jsons = [ json.dumps(s.json_dict(site_base_url)) for s in sessions ] # useful when debugging javascript #session_jsons = session_jsons[1:20] # get_modified_from needs the query set, not the list modified = get_modified_from_scheduledsessions(scheduledsessions) area_list = get_areas() wg_name_list = get_wg_name_list(scheduledsessions) wg_list = get_wg_list(wg_name_list) ads = find_ads_for_meeting(meeting) for ad in ads: # set the default to avoid needing extra arguments in templates # django 1.3+ ad.default_hostscheme = site_base_url time_slices,date_slices = build_all_agenda_slices(scheduledsessions, True) return HttpResponse(render_to_string("meeting/landscape_edit.html", {"schedule":schedule, "saveas": saveas, "saveasurl": saveasurl, "meeting_base_url": meeting_base_url, "site_base_url": site_base_url, "rooms":rooms, "time_slices":time_slices, "date_slices":date_slices, "modified": modified, "meeting":meeting, "area_list": area_list, "area_directors" : ads, "wg_list": wg_list , "session_jsons": session_jsons, "scheduledsessions": scheduledsessions, "show_inline": set(["txt","htm","html"]) }, RequestContext(request)), content_type="text/html") ############################################################################## # show the properties associated with an agenda (visible, public) # this page uses ajax PUT requests to the API # AgendaPropertiesForm = modelform_factory(Schedule, fields=('name','visible', 'public')) @role_required('Area Director','Secretariat') @decorator_from_middleware(GZipMiddleware) def edit_agenda_properties(request, num=None, schedule_name=None): meeting = get_meeting(num) schedule = get_schedule(meeting, schedule_name) form = AgendaPropertiesForm(instance=schedule) return HttpResponse(render_to_string("meeting/properties_edit.html", {"schedule":schedule, "form":form, "meeting":meeting}, RequestContext(request)), content_type="text/html") ############################################################################## # show list of agendas. # @role_required('Area Director','Secretariat') @decorator_from_middleware(GZipMiddleware) def edit_agendas(request, num=None, order=None): #if request.method == 'POST': # return agenda_create(request, num, schedule_name) meeting = get_meeting(num) user = request.user schedules = meeting.schedule_set if not has_role(user, 'Secretariat'): schedules = schedules.filter(visible = True) | schedules.filter(owner = user.get_profile()) schedules = schedules.order_by('owner', 'name') return HttpResponse(render_to_string("meeting/agenda_list.html", {"meeting": meeting, "schedules": schedules.all() }, RequestContext(request)), content_type="text/html") def agenda(request, num=None, name=None, base=None, ext=None): base = base if base else 'agenda' ext = ext if ext else '.html' if 'iPhone' in get_user_agent(request) and ext == ".html": base = 'm_agenda' mimetype = {".html":"text/html", ".txt": "text/plain", ".ics":"text/calendar", ".csv":"text/csv"} meeting = get_meeting(num) schedule = get_schedule(meeting, name) updated = meeting_updated(meeting) return HttpResponse(render_to_string("meeting/"+base+ext, {"schedule":schedule, "updated": updated}, RequestContext(request)), content_type=mimetype[ext]) def read_agenda_file(num, doc): # XXXX FIXME: the path fragment in the code below should be moved to # settings.py. The *_PATH settings should be generalized to format() # style python format, something like this: # DOC_PATH_FORMAT = { "agenda": "/foo/bar/agenda-{meeting.number}/agenda-{meeting-number}-{doc.group}*", } path = os.path.join(settings.AGENDA_PATH, "%s/agenda/%s" % (num, doc.external_url)) if os.path.exists(path): with open(path) as f: return f.read() else: return None def session_agenda(request, num, session): d = Document.objects.filter(type="agenda", session__meeting__number=num) if session == "plenaryt": d = d.filter(session__name__icontains="technical", session__slots__type="plenary") elif session == "plenaryw": d = d.filter(session__name__icontains="admin", session__slots__type="plenary") else: d = d.filter(session__group__acronym=session) if d: agenda = d[0] content = read_agenda_file(num, agenda) or "Could not read agenda file" _, ext = os.path.splitext(agenda.external_url) ext = ext.lstrip(".").lower() if ext == "txt": return HttpResponse(content, content_type="text/plain") elif ext == "pdf": return HttpResponse(content, content_type="application/pdf") else: return HttpResponse(content) raise Http404("No agenda for the %s session of IETF %s is available" % (session, num)) def convert_to_pdf(doc_name): inpath = os.path.join(settings.IDSUBMIT_REPOSITORY_PATH, doc_name + ".txt") outpath = os.path.join(settings.INTERNET_DRAFT_PDF_PATH, doc_name + ".pdf") try: infile = open(inpath, "r") except IOError: return t,tempname = mkstemp() tempfile = open(tempname, "w") pageend = 0; newpage = 0; formfeed = 0; for line in infile: line = re.sub("\r","",line) line = re.sub("[ \t]+$","",line) if re.search("\[?[Pp]age [0-9ivx]+\]?[ \t]*$",line): pageend=1 tempfile.write(line) continue if re.search("^[ \t]*\f",line): formfeed=1 tempfile.write(line) continue if re.search("^ *INTERNET.DRAFT.+[0-9]+ *$",line) or re.search("^ *Internet.Draft.+[0-9]+ *$",line) or re.search("^draft-[-a-z0-9_.]+.*[0-9][0-9][0-9][0-9]$",line) or re.search("^RFC.+[0-9]+$",line): newpage=1 if re.search("^[ \t]*$",line) and pageend and not newpage: continue if pageend and newpage and not formfeed: tempfile.write("\f") pageend=0 formfeed=0 newpage=0 tempfile.write(line) infile.close() tempfile.close() t,psname = mkstemp() pipe("enscript --margins 76::76: -B -q -p "+psname + " " +tempname) os.unlink(tempname) pipe("ps2pdf "+psname+" "+outpath) os.unlink(psname) def session_draft_list(num, session): try: agenda = Document.objects.filter(type="agenda", session__meeting__number=num, session__group__acronym=session, states=State.objects.get(type="agenda", slug="active")).distinct().get() except Document.DoesNotExist: raise Http404 drafts = set() content = read_agenda_file(num, agenda) if content: drafts.update(re.findall('(draft-[-a-z0-9]*)', content)) result = [] for draft in drafts: try: if re.search('-[0-9]{2}$', draft): doc_name = draft else: doc = Document.objects.get(name=draft) doc_name = draft + "-" + doc.rev if doc_name not in result: result.append(doc_name) except Document.DoesNotExist: pass return sorted(result) def session_draft_tarfile(request, num, session): drafts = session_draft_list(num, session); response = HttpResponse(content_type='application/octet-stream') response['Content-Disposition'] = 'attachment; filename=%s-drafts.tgz'%(session) tarstream = tarfile.open('','w:gz',response) mfh, mfn = mkstemp() manifest = open(mfn, "w") for doc_name in drafts: pdf_path = os.path.join(settings.INTERNET_DRAFT_PDF_PATH, doc_name + ".pdf") if (not os.path.exists(pdf_path)): convert_to_pdf(doc_name) if os.path.exists(pdf_path): try: tarstream.add(pdf_path, str(doc_name + ".pdf")) manifest.write("Included: "+pdf_path+"\n") except Exception, e: manifest.write(("Failed (%s): "%e)+pdf_path+"\n") else: manifest.write("Not found: "+pdf_path+"\n") manifest.close() tarstream.add(mfn, "manifest.txt") tarstream.close() os.unlink(mfn) return response def pdf_pages(file): try: infile = open(file, "r") except IOError: return 0 for line in infile: m = re.match('\] /Count ([0-9]+)',line) if m: return int(m.group(1)) return 0 def session_draft_pdf(request, num, session): drafts = session_draft_list(num, session); curr_page = 1 pmh, pmn = mkstemp() pdfmarks = open(pmn, "w") pdf_list = "" for draft in drafts: pdf_path = os.path.join(settings.INTERNET_DRAFT_PDF_PATH, draft + ".pdf") if (not os.path.exists(pdf_path)): convert_to_pdf(draft) if (os.path.exists(pdf_path)): pages = pdf_pages(pdf_path) pdfmarks.write("[/Page "+str(curr_page)+" /View [/XYZ 0 792 1.0] /Title (" + draft + ") /OUT pdfmark\n") pdf_list = pdf_list + " " + pdf_path curr_page = curr_page + pages pdfmarks.close() pdfh, pdfn = mkstemp() pipe("gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=" + pdfn + " " + pdf_list + " " + pmn) pdf = open(pdfn,"r") pdf_contents = pdf.read() pdf.close() os.unlink(pmn) os.unlink(pdfn) return HttpResponse(pdf_contents, content_type="application/pdf") def week_view(request, num=None): meeting = get_meeting(num) timeslots = TimeSlot.objects.filter(meeting__id = meeting.id) template = "meeting/week-view.html" return render_to_response(template, {"timeslots":timeslots,"render_types":["Session","Other","Break","Plenary"]}, context_instance=RequestContext(request)) def ical_agenda(request, num=None, name=None, ext=None): meeting = get_meeting(num) schedule = get_schedule(meeting, name) updated = meeting_updated(meeting) q = request.META.get('QUERY_STRING','') or "" filter = set(urllib.unquote(q).lower().split(',')) include = [ i for i in filter if not (i.startswith('-') or i.startswith('~')) ] include_types = set(["plenary","other"]) exclude = [] # Process the special flags. # "-wgname" will remove a working group from the output. # "~Type" will add that type to the output. # "-~Type" will remove that type from the output # Current types are: # Session, Other (default on), Break, Plenary (default on) # Non-Working Group "wg names" include: # edu, ietf, tools, iesg, iab for item in filter: if item: if item[0] == '-' and item[1] == '~': include_types -= set([item[2:]]) elif item[0] == '-': exclude.append(item[1:]) elif item[0] == '~': include_types |= set([item[1:]]) assignments = schedule.assignments.filter( Q(timeslot__type__slug__in = include_types) | Q(session__group__acronym__in = include) | Q(session__group__parent__acronym__in = include) ).exclude(session__group__acronym__in = exclude).distinct() #.exclude(Q(session__group__isnull = False), #Q(session__group__acronym__in = exclude) | #Q(session__group__parent__acronym__in = exclude)) return HttpResponse(render_to_string("meeting/agenda.ics", {"schedule":schedule, "assignments":assignments, "updated":updated}, RequestContext(request)), content_type="text/calendar") def meeting_requests(request, num=None) : meeting = get_meeting(num) sessions = Session.objects.filter(meeting__number=meeting.number,group__parent__isnull = False).exclude(requested_by=0).order_by("group__parent__acronym","status__slug","group__acronym") groups_not_meeting = Group.objects.filter(state='Active',type__in=['WG','RG','BOF']).exclude(acronym__in = [session.group.acronym for session in sessions]).order_by("parent__acronym","acronym") return render_to_response("meeting/requests.html", {"meeting": meeting, "sessions":sessions, "groups_not_meeting": groups_not_meeting}, context_instance=RequestContext(request))