diff --git a/ietf/secr/meetings/forms.py b/ietf/secr/meetings/forms.py index 84c81f2a9..16aea5cf3 100644 --- a/ietf/secr/meetings/forms.py +++ b/ietf/secr/meetings/forms.py @@ -90,7 +90,7 @@ class TimeChoiceField(forms.ChoiceField): class MeetingModelForm(forms.ModelForm): class Meta: model = Meeting - exclude = ('type','agenda') + exclude = ('type') def clean_number(self): number = self.cleaned_data['number'] diff --git a/ietf/secr/meetings/views.py b/ietf/secr/meetings/views.py index 7018d35e3..1b1c37207 100644 --- a/ietf/secr/meetings/views.py +++ b/ietf/secr/meetings/views.py @@ -12,7 +12,7 @@ from django.utils.functional import curry from django.utils import simplejson from ietf.utils.mail import send_mail -from ietf.meeting.models import Meeting, Session, Room, TimeSlot, Schedule, ScheduledSession +from ietf.meeting.models import Meeting, Session, Room, TimeSlot from ietf.group.models import Group from ietf.name.models import SessionStatusName, TimeSlotTypeName from ietf.person.models import Person @@ -21,48 +21,24 @@ from ietf.secr.proceedings.views import build_choices, handle_upload_file from ietf.secr.sreq.forms import GroupSelectForm from ietf.secr.sreq.views import get_initial_session, session_conflicts_as_string from ietf.secr.utils.mail import get_cc_list -from ietf.secr.utils.meeting import get_upload_root, get_session, get_timeslot +from ietf.secr.utils.meeting import get_upload_root from forms import * import os import datetime +# prep for agenda changes # -------------------------------------------------- # Helper Functions # -------------------------------------------------- -def assign(session,timeslot,meeting): - ''' - Robust function to assign a session to a timeslot - ''' - qs = timeslot.scheduledsession_set.filter(schedule=meeting.agenda) - # this should never happen, but just in case - if not qs: - ScheduledSession.objects.create(schedule=meeting.agenda, - session=session, - timeslot=timeslot) - else: - # find the first unassigned scheduled session or create a new one - for ss in qs: - if not ss.session: - ss.session = session - ss.save() - break - else: - ScheduledSession.objects.create(schedule=meeting.agenda, - session=session, - timeslot=timeslot) - session.status_id = 'sched' - session.save() - -def build_timeslots(request,meeting,room=None): +def build_timeslots(meeting,room=None): ''' This function takes a Meeting object and an optional room argument. If room isn't passed we pre-create the full set of timeslot records using the last meeting as a template. If room is passed pre-create timeslots for the new room. Call this after saving new rooms or adding a room. ''' - schedule = get_schedule(request,meeting) slots = meeting.timeslot_set.filter(type='session') if room: rooms = [room] @@ -87,15 +63,14 @@ def build_timeslots(request,meeting,room=None): for t in timeslots: new_time = t.time + delta for room in rooms: - ts = TimeSlot.objects.create(type_id='session', + TimeSlot.objects.create(type_id='session', meeting=meeting, name=t.name, time=new_time, location=room, duration=t.duration) - ScheduledSession.objects.create(schedule=schedule,timeslot=ts) -def build_nonsession(request, meeting): +def build_nonsession(meeting): ''' This function takes a meeting object and creates non-session records for a new meeting, based on the last meeting @@ -103,8 +78,6 @@ def build_nonsession(request, meeting): last_meeting = get_last_meeting(meeting) delta = meeting.date - last_meeting.date system = Person.objects.get(name='(system)') - schedule = get_schedule(request, meeting) - for slot in TimeSlot.objects.filter(meeting=last_meeting,type__in=('break','reg','other','plenary')): new_time = slot.time + delta session = None @@ -112,48 +85,29 @@ def build_nonsession(request, meeting): if slot.type.slug in ('other','plenary'): session = Session(meeting=meeting, name=slot.name, - short=get_session(slot).short, - group=get_session(slot).group, + short=slot.session.short, + group=slot.session.group, requested_by=system, status_id='sched') session.save() - ts = TimeSlot.objects.create(type=slot.type, + TimeSlot.objects.create(type=slot.type, meeting=meeting, + session=session, name=slot.name, time=new_time, duration=slot.duration, show_location=slot.show_location) - ScheduledSession.create(schedule=schedule,session=session,timeslot=ts) def get_last_meeting(meeting): last_number = int(meeting.number) - 1 return Meeting.objects.get(number=last_number) -def get_schedule(request,meeting): - ''' - This function takes a Request object and Meeting object and returns the Official - Schdeule for the meeting. It will create the Schedule if it doesn't already - exist. Call this function after creating a new meeting. - ''' - schedule = meeting.agenda - if schedule is None: - name = "mtg:%s" % meeting.number - person = request.user.get_profile() - schedule, created = Schedule.objects.get_or_create(meeting=meeting, - name=name, - defaults={'owner':person, - 'visible':True, - 'public':True}) - meeting.agenda = schedule - meeting.save() - return schedule - -def is_combined(session, meeting): +def is_combined(session): ''' Check to see if this session is using two combined timeslots ''' - if session.scheduledsession_set.filter(schedule=meeting.agenda).count() > 1: + if session.timeslot_set.count() > 1: return True else: return False @@ -172,8 +126,7 @@ def make_directories(meeting): def send_notification(request, sessions): ''' - This view generates notifications for schedule sessions. It takes an HttpRequest object and - a query set, sessions, consisting of one or more sessions scheduled for a particular group. + This view generates notifications for schedule sessions ''' session_info_template = '''{0} Session {1} ({2}) {3}, {4} {5} @@ -193,7 +146,7 @@ def send_notification(request, sessions): # easier to populate template from timeslot perspective. assuming one-to-one timeslot-session count = 0 session_info = '' - data = [ (s,get_timeslot(s)) for s in sessions ] + data = [ (s,s.timeslot_set.all()[0]) for s in sessions ] for s,t in data: count += 1 session_info += session_info_template.format(group.acronym, @@ -222,16 +175,18 @@ def send_notification(request, sessions): def sort_groups(meeting): ''' Similar to sreq.views.sort_groups - Takes a Meeting object and returns a tuple scheduled_groups, unscheduled groups. + Takes a Django User object and a Meeting object + Returns a tuple scheduled_groups, unscheduled groups. ''' scheduled_groups = [] unscheduled_groups = [] + #sessions = Session.objects.filter(meeting=meeting,status__in=('schedw','apprw','appr','sched','notmeet','canceled')) sessions = Session.objects.filter(meeting=meeting,status__in=('schedw','apprw','appr','sched','canceled')) - groups_with_sessions = [ s.group for s in sessions if s.group.type.slug in ('wg','rg','ag') ] + groups_with_sessions = [ s.group for s in sessions ] gset = set(groups_with_sessions) sorted_groups_with_sessions = sorted(gset, key = lambda instance: instance.acronym) - scheduled_sessions = ScheduledSession.objects.filter(schedule=meeting.agenda,session__isnull=False) - groups_with_timeslots = [ x.session.group for x in scheduled_sessions ] + slots = TimeSlot.objects.filter(meeting=meeting,session__isnull=False) + groups_with_timeslots = [ s.session.group for s in slots ] for group in sorted_groups_with_sessions: if group in groups_with_timeslots: scheduled_groups.append(group) @@ -283,9 +238,6 @@ def add(request): if form.is_valid(): meeting = form.save() - #Create initial Official schedule - get_schedule(request,meeting) - #Create Physical new meeting directory and subdirectories make_directories(meeting) @@ -420,7 +372,7 @@ def non_session(request, meeting_id): # if the Break/Registration records don't exist yet (new meeting) create them if not TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other')): - build_nonsession(request, meeting) + build_nonsession(meeting) slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary')).order_by('-type__name','time') @@ -437,14 +389,6 @@ def non_session(request, meeting_id): t = meeting.date + datetime.timedelta(days=int(day)) new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute) - # create TimeSlot object - timeslot = TimeSlot.objects.create(type=form.cleaned_data['type'], - meeting=meeting, - name=name, - time=new_time, - duration=duration, - show_location=form.cleaned_data['show_location']) - # create a dummy Session object to hold materials # NOTE: we're setting group to none here, but the set_room page will force user # to pick a legitimate group @@ -458,10 +402,14 @@ def non_session(request, meeting_id): status_id='sched') session.save() - # create association - ScheduledSession.objects.create(timeslot=timeslot, - session=session, - schedule=meeting.agenda) + # create TimeSlot object + TimeSlot.objects.create(type=form.cleaned_data['type'], + meeting=meeting, + session=session, + name=name, + time=new_time, + duration=duration, + show_location=form.cleaned_data['show_location']) messages.success(request, 'Non-Sessions updated successfully') url = reverse('meetings_non_session', kwargs={'meeting_id':meeting_id}) @@ -487,14 +435,13 @@ def non_session_delete(request, meeting_id, slot_id): ''' slot = get_object_or_404(TimeSlot, id=slot_id) if slot.type_id in ('other','plenary'): - session = get_session(slot) - if session and session.materials.exclude(states__slug='deleted'): + if slot.session.materials.exclude(states__slug='deleted'): messages.error(request, 'Materials have already been uploaded for "%s". You must delete those before deleting the timeslot.' % slot.name) url = reverse('meetings_non_session', kwargs={'meeting_id':meeting_id}) return HttpResponseRedirect(url) else: - slot.sessions.all().delete() + slot.session.delete() slot.delete() messages.success(request, 'Non-Session timeslot deleted successfully') @@ -507,7 +454,6 @@ def non_session_edit(request, meeting_id, slot_id): ''' meeting = get_object_or_404(Meeting, number=meeting_id) slot = get_object_or_404(TimeSlot, id=slot_id) - session = get_session(slot) if request.method == 'POST': button_text = request.POST.get('submit', '') @@ -515,7 +461,7 @@ def non_session_edit(request, meeting_id, slot_id): url = reverse('meetings_non_session', kwargs={'meeting_id':meeting_id}) return HttpResponseRedirect(url) - form = NonSessionEditForm(request.POST,meeting=meeting, session=session) + form = NonSessionEditForm(request.POST,meeting=meeting, session=slot.session) if form.is_valid(): location = form.cleaned_data['location'] group = form.cleaned_data['group'] @@ -525,6 +471,7 @@ def non_session_edit(request, meeting_id, slot_id): slot.name = name slot.save() # save group to session object + session = slot.session session.group = group session.name = name session.short = short @@ -538,10 +485,10 @@ def non_session_edit(request, meeting_id, slot_id): # we need to pass the session to the form in order to disallow changing # of group after materials have been uploaded initial = {'location':slot.location, - 'group':session.group, - 'name':session.name, - 'short':session.short} - form = NonSessionEditForm(meeting=meeting,session=session,initial=initial) + 'group':slot.session.group, + 'name':slot.session.name, + 'short':slot.session.short} + form = NonSessionEditForm(meeting=meeting,session=slot.session,initial=initial) return render_to_response('meetings/non_session_edit.html', { 'meeting': meeting, @@ -562,10 +509,10 @@ def remove_session(request, meeting_id, acronym): now = datetime.datetime.now() for session in sessions: - ss = session.official_scheduledsession() - ss.session = None - ss.modified = now - ss.save() + for timeslot in session.timeslot_set.all(): + timeslot.session = None + timeslot.modified = now + timeslot.save() session.status_id = 'canceled' session.modified = now session.save() @@ -597,14 +544,14 @@ def rooms(request, meeting_id): # if we are creating rooms for the first time create full set of timeslots if first_time: - build_timeslots(request, meeting) + build_timeslots(meeting) # otherwise if we're modifying rooms else: # add timeslots for new rooms, deleting rooms automatically deletes timeslots for form in formset.forms[formset.initial_form_count():]: if form.instance.pk: - build_timeslots(request, meeting,room=form.instance) + build_timeslots(meeting,room=form.instance) messages.success(request, 'Meeting Rooms changed successfully') url = reverse('meetings_rooms', kwargs={'meeting_id':meeting_id}) @@ -634,16 +581,14 @@ def schedule(request, meeting_id, acronym): for s in sessions: d = {'session':s.id, 'note':s.agenda_note} - - timeslot = get_timeslot(s) - - if timeslot: - d['room'] = timeslot.location.id - d['day'] = timeslot.time.isoweekday() % 7 + 1 # adjust to django week_day - d['time'] = timeslot.time.strftime('%H%M') + qs = s.timeslot_set.all() + if qs: + d['room'] = qs[0].location.id + d['day'] = qs[0].time.isoweekday() % 7 + 1 # adjust to django week_day + d['time'] = qs[0].time.strftime('%H%M') else: - d['day'] = 2 # default - if is_combined(s,meeting): + d['day'] = 2 + if is_combined(s): d['combine'] = True initial.append(d) @@ -673,39 +618,74 @@ def schedule(request, meeting_id, acronym): day = form.cleaned_data['day'] combine = form.cleaned_data.get('combine',None) session = Session.objects.get(id=id) - initial_timeslot = get_timeslot(session) + was_combined = is_combined(session) + initial_timeslots = session.timeslot_set.all() + if initial_timeslots: + initial_timeslot = initial_timeslots[0] + else: + initial_timeslot = None # find new timeslot new_day = meeting.date + datetime.timedelta(days=int(day)-1) hour = datetime.time(int(time[:2]),int(time[2:])) new_time = datetime.datetime.combine(new_day,hour) - timeslot = TimeSlot.objects.filter(meeting=meeting,time=new_time,location=room)[0] + qs = TimeSlot.objects.filter(meeting=meeting,time=new_time,location=room) + if qs.filter(session=None): + timeslot = qs.filter(session=None)[0] + else: + # we need to create another, identical timeslot + timeslot = TimeSlot.objects.create(meeting=qs[0].meeting, + type=qs[0].type, + name=qs[0].name, + time=qs[0].time, + duration=qs[0].duration, + location=qs[0].location, + show_location=qs[0].show_location, + modified=now) + messages.warning(request, 'WARNING: There are now two sessions scheduled for the timeslot: %s' % timeslot) # COMBINE SECTION - BEFORE -------------- if 'combine' in form.changed_data and not combine: next_slot = get_next_slot(initial_timeslot) - for ss in next_slot.scheduledsession_set.filter(schedule=meeting.agenda,session=session): - ss.session = None - ss.save() + next_slot.session = None + next_slot.modified = now + next_slot.save() # --------------------------------------- - if any(x in form.changed_data for x in ('day','time','room')): - # clear the old association - if initial_timeslot: - # get SS record(s) and unschedule by removing the session reference - for ss in session.scheduledsession_set.filter(schedule=meeting.agenda): - ss.session = None - ss.save() + if any(x in form.changed_data for x in ('day','time','room')): + # clear the old timeslot(s) + for ts in initial_timeslots: + # if the initial timeslot is one of multiple we should delete it + tqs = TimeSlot.objects.filter(meeting=meeting, + type='session', + time=ts.time, + location=ts.location) + if tqs.count() > 1: + ts.delete() + else: + ts.session = None + ts.modified = now + ts.save() + # assign new timeslot(s) + new_slots = [] if timeslot: - assign(session,timeslot,meeting) - if timeslot.sessions.all().count() > 1: - messages.warning(request, 'WARNING: There are now multiple sessions scheduled for the timeslot: %s' % timeslot) + new_slots.append(timeslot) + if was_combined: + new_slots.append(get_next_slot(timeslot)) + for ts in new_slots: + timeslot.session = session + timeslot.modified = now + timeslot.save() + + if new_slots: + session.status_id = 'sched' else: session.status_id = 'schedw' session.modified = now session.save() + if 'note' in form.changed_data: session.agenda_note = note session.modified = now @@ -714,7 +694,9 @@ def schedule(request, meeting_id, acronym): # COMBINE SECTION - AFTER --------------- if 'combine' in form.changed_data and combine: next_slot = get_next_slot(timeslot) - assign(session,next_slot,meeting) + next_slot.session = session + next_slot.modified = now + next_slot.save() # --------------------------------------- # notify. dont send if Tutorial, BOF or indicated on form @@ -722,7 +704,7 @@ def schedule(request, meeting_id, acronym): if (has_changed and not extra_form.cleaned_data.get('no_notify',False) and group.state.slug != 'bof' - and get_timeslot(session)): # and the session is scheduled, else skip + and session.timeslot_set.all()): # and the session is scheduled, else skip send_notification(request, sessions) notification_message = "Notification sent." @@ -826,19 +808,19 @@ def times(request, meeting_id): new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute) # don't allow creation of timeslots with same start time as existing timeslots + # assert False, (new_time, time_seen) if new_time in time_seen: messages.error(request, 'There is already a timeslot for %s. To change you must delete the old one first.' % new_time.strftime('%a %H:%M')) url = reverse('meetings_times', kwargs={'meeting_id':meeting_id}) return HttpResponseRedirect(url) for room in meeting.room_set.all(): - ts = TimeSlot.objects.create(type_id='session', - meeting=meeting, - name=name, - time=new_time, - location=room, - duration=duration) - ScheduledSession.objects.create(schedule=meeting.agenda,timeslot=ts) + TimeSlot.objects.create(type_id='session', + meeting=meeting, + name=name, + time=new_time, + location=room, + duration=duration) messages.success(request, 'Timeslots created') url = reverse('meetings_times', kwargs={'meeting_id':meeting_id}) @@ -864,10 +846,7 @@ def times_delete(request, meeting_id, time): parts = [ int(x) for x in time.split(':') ] dtime = datetime.datetime(*parts) - qs = meeting.agenda.scheduledsession_set.filter(timeslot__time=dtime, - session__isnull=False) - - if qs: + if Session.objects.filter(timeslot__time=dtime,timeslot__meeting=meeting): messages.error(request, 'ERROR deleting timeslot. There is one or more sessions scheduled for this timeslot.') url = reverse('meetings_times', kwargs={'meeting_id':meeting_id}) return HttpResponseRedirect(url) diff --git a/ietf/secr/proceedings/proc_utils.py b/ietf/secr/proceedings/proc_utils.py index b191982ca..bc3f599af 100644 --- a/ietf/secr/proceedings/proc_utils.py +++ b/ietf/secr/proceedings/proc_utils.py @@ -14,7 +14,7 @@ from itertools import chain 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, get_timeslot +from ietf.secr.utils.meeting import get_upload_root, get_proceedings_path, get_material from models import InterimMeeting # proxy model from urllib2 import urlopen @@ -32,7 +32,7 @@ def mycomp(timeslot): This takes a timeslot object and returns a key to sort by the area acronym or None ''' try: - group = get_session(timeslot).group + group = timeslot.session.group key = '%s:%s' % (group.parent.acronym, group.acronym) except AttributeError: key = None @@ -349,7 +349,6 @@ def gen_areas(context): 'bof':filter(lambda a: a.parent==area and a.state.slug=='bof' and a.type_id=='wg',gnot), 'ag':filter(lambda a: a.parent==area and a.type_id=='ag',gnot)} - html = render_to_response('proceedings/area.html',{ 'area': area, 'meeting': meeting, diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py index 7deb6daee..cd28a622e 100644 --- a/ietf/secr/sreq/views.py +++ b/ietf/secr/sreq/views.py @@ -216,8 +216,14 @@ def cancel(request, acronym): session.status_id = 'deleted' session.save() - # clear schedule assignments if already scheduled - session.scheduledsession_set.all().delete() + # clear timeslot assignment if already scheduled + if session.timeslot_set.all(): + timeslot = session.timeslot_set.all()[0] + timeslot.session = None + timeslot.save() + + # log activity + #add_session_activity(group,'Session was cancelled',meeting,user) # send notifitcation to_email = SESSION_REQUEST_EMAIL diff --git a/ietf/secr/utils/meeting.py b/ietf/secr/utils/meeting.py index eff298597..f458e382b 100644 --- a/ietf/secr/utils/meeting.py +++ b/ietf/secr/utils/meeting.py @@ -1,5 +1,5 @@ from django.conf import settings -from ietf.meeting.models import Meeting, Session, Schedule, TimeSlot +from ietf.meeting.models import Meeting import os @@ -28,34 +28,6 @@ def get_proceedings_path(meeting, group): path = os.path.join(get_upload_root(meeting),'%s.html' % group.acronym) return path -def get_session(timeslot, schedule=None): - ''' - Helper function to get the session given a timeslot, assume Official schedule if one isn't - provided. Replaces "timeslot.session" - ''' - # todo, doesn't account for shared timeslot - if not schedule: - schedule = timeslot.meeting.agenda - qs = timeslot.sessions.filter(scheduledsession__schedule=schedule) #.exclude(states__slug='deleted') - if qs: - return qs[0] - else: - return None - -def get_timeslot(session, schedule=None): - ''' - Helper function to get the timeslot associated with a session. Created for Agenda Tool - db schema changes. Use this function in place of session.timeslot_set.all()[0]. Don't specify - schedule to use the meeting "official" schedule. - ''' - if not schedule: - schedule = session.meeting.agenda - ss = session.scheduledsession_set.filter(schedule=schedule) - if ss: - return ss[0].timeslot - else: - return None - def get_upload_root(meeting): path = '' if meeting.type.slug == 'ietf': diff --git a/ietf/settings.py b/ietf/settings.py index 95030344a..9234e4fa0 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -79,7 +79,7 @@ MEDIA_ROOT = BASE_DIR + "/../static/" # URL that handles the media served from MEDIA_ROOT. # Example: "http://media.lawrence.com" -MEDIA_URL = '' +MEDIA_URL = 'http://www.ietf.org' # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. @@ -126,6 +126,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.debug', 'django.core.context_processors.i18n', 'django.core.context_processors.request', + 'django.core.context_processors.media', 'django.contrib.messages.context_processors.messages', 'ietf.context_processors.server_mode', 'ietf.context_processors.revision_info', @@ -339,7 +340,7 @@ SECR_BLUE_SHEET_URL = 'https://datatracker.ietf.org/documents/blue_sheet.rtf' SECR_INTERIM_LISTING_DIR = '/a/www/www6/meeting/interim' SECR_MAX_UPLOAD_SIZE = 40960000 SECR_PROCEEDINGS_DIR = '/a/www/www6s/proceedings/' -SECR_STATIC_URL = '/secr-static/' +SECR_STATIC_URL = '/secretariat/' # Put SECRET_KEY in here, or any other sensitive or site-specific # changes. DO NOT commit settings_local.py to svn.