From ad2784962fd9fffcd3ab927a887c0a1df26165bd Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Sat, 8 Mar 2014 10:37:24 +0000 Subject: [PATCH] ported forward from personal/rjs/trunk-7174. Still one existing test failing - Legacy-Id: 7449 --- ietf/group/models.py | 3 + ietf/meeting/ajax.py | 303 +++++-- ietf/meeting/management/__init__.py | 0 ietf/meeting/management/commands/__init__.py | 0 ietf/meeting/management/commands/autoplace.py | 124 +++ ietf/meeting/migrations/0014_add_room.py | 350 +++++++++ .../migrations/0015_add_basic_resources.py | 339 ++++++++ .../0016_add_resource_to_session.py | 341 ++++++++ ..._remove_empty_session_scheduledsessions.py | 335 ++++++++ ietf/meeting/models.py | 256 ++++-- ietf/meeting/placement.py | 738 ++++++++++++++++++ ietf/meeting/test_data.py | 4 +- ietf/meeting/tests_api.py | 88 ++- ietf/meeting/tests_views.py | 4 +- ietf/meeting/urls.py | 10 +- ietf/meeting/views.py | 69 +- .../migrations/0020_add_default_penalties.py | 205 +++++ ietf/name/migrations/0021_add_room.py | 200 +++++ .../migrations/0022_add_room_resources.py | 201 +++++ ietf/name/models.py | 4 +- ietf/secr/meetings/views.py | 11 +- ietf/secr/sreq/forms.py | 25 +- ietf/secr/sreq/tests.py | 16 +- ietf/secr/sreq/urls.py | 2 + ietf/secr/sreq/views.py | 125 ++- .../includes/sessions_request_form.html | 34 +- .../includes/sessions_request_view.html | 6 +- ietf/secr/templates/sreq/main.html | 2 +- ietf/secr/templates/sreq/view.html | 4 +- ietf/settings.py | 29 + ietf/templates/meeting/agenda-utc.html | 6 +- ietf/templates/meeting/landscape_edit.html | 349 +++++---- ietf/templates/meeting/requests.html | 2 +- ietf/templates/meeting/room_edit.html | 60 ++ ietf/templates/meeting/timeslot_edit.html | 63 +- 35 files changed, 3847 insertions(+), 461 deletions(-) create mode 100644 ietf/meeting/management/__init__.py create mode 100644 ietf/meeting/management/commands/__init__.py create mode 100644 ietf/meeting/management/commands/autoplace.py create mode 100644 ietf/meeting/migrations/0014_add_room.py create mode 100644 ietf/meeting/migrations/0015_add_basic_resources.py create mode 100644 ietf/meeting/migrations/0016_add_resource_to_session.py create mode 100644 ietf/meeting/migrations/0017_remove_empty_session_scheduledsessions.py create mode 100644 ietf/meeting/placement.py create mode 100644 ietf/name/migrations/0020_add_default_penalties.py create mode 100644 ietf/name/migrations/0021_add_room.py create mode 100644 ietf/name/migrations/0022_add_room_resources.py create mode 100644 ietf/templates/meeting/room_edit.html diff --git a/ietf/group/models.py b/ietf/group/models.py index 3d0161fbf..7d604d3b6 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -63,6 +63,9 @@ class Group(GroupInfo): role_names = [role_names] return user.is_authenticated() and self.role_set.filter(name__in=role_names, person__user=user).exists() + def is_bof(self): + return (self.state.slug in ["bof", "bof-conc"]) + def get_chair(self): chair = self.role_set.filter(name__slug='chair')[:1] return chair and chair[0] or None diff --git a/ietf/meeting/ajax.py b/ietf/meeting/ajax.py index a5794a790..1ded28ecd 100644 --- a/ietf/meeting/ajax.py +++ b/ietf/meeting/ajax.py @@ -1,4 +1,5 @@ import json +import datetime from django.core.urlresolvers import reverse from django.shortcuts import get_object_or_404, redirect @@ -9,10 +10,11 @@ from ietf.ietfauth.utils import role_required, has_role, user_is_person from ietf.name.models import TimeSlotTypeName from ietf.meeting.helpers import get_meeting, get_schedule, get_schedule_by_id, agenda_permissions +from ietf.meeting.models import ScheduledSession from ietf.meeting.views import edit_timeslots, edit_agenda from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint -import debug +#import debug def dajaxice_core_js(request): # this is a slightly weird hack to get, we seem to need this because @@ -64,83 +66,48 @@ def update_timeslot_pinned(request, schedule_id, scheduledsession_id, pinned=Fal if scheduledsession_id is not None: ss_id = int(scheduledsession_id) - if ss_id != 0: - ss = get_object_or_404(schedule.scheduledsession_set, pk=ss_id) + if ss_id == 0: + return json.dumps({'error':'no permission'}) + ss = get_object_or_404(schedule.scheduledsession_set, pk=ss_id) ss.pinned = pinned ss.save() return json.dumps({'message':'valid'}) - - -@role_required('Area Director','Secretariat') -@dajaxice_register -def update_timeslot(request, schedule_id, session_id, scheduledsession_id=None, extended_from_id=None, duplicate=False): - schedule = get_object_or_404(Schedule, pk = int(schedule_id)) - meeting = schedule.meeting - ss_id = 0 - ess_id = 0 - ess = None - ss = None - - #print "duplicate: %s schedule.owner: %s user: %s" % (duplicate, schedule.owner, request.user.person) - cansee,canedit = agenda_permissions(meeting, schedule, request.user) - - if not canedit: - #raise Exception("Not permitted") - return json.dumps({'error':'no permission'}) - - session_id = int(session_id) - session = get_object_or_404(meeting.session_set, pk=session_id) - - if scheduledsession_id is not None: - ss_id = int(scheduledsession_id) - - if extended_from_id is not None: - ess_id = int(extended_from_id) - - if ss_id != 0: - ss = get_object_or_404(schedule.scheduledsession_set, pk=ss_id) - - # this cleans up up two sessions in one slot situation, the - # ... extra scheduledsessions need to be cleaned up. - - if ess_id == 0: - # if this is None, then we must be moving. - for ssO in schedule.scheduledsession_set.filter(session=session): - #print "sched(%s): removing session %s from slot %u" % ( schedule, session, ssO.pk ) - #if ssO.extendedfrom is not None: - # ssO.extendedfrom.session = None - # ssO.extendedfrom.save() - ssO.session = None - ssO.extendedfrom = None - ssO.save() - else: - ess = get_object_or_404(schedule.scheduledsession_set, pk = ess_id) - ss.extendedfrom = ess - - try: - # find the scheduledsession, assign the Session to it. - if ss: - #print "ss.session: %s session:%s duplicate=%s"%(ss, session, duplicate) - ss.session = session - if duplicate: - ss.id = None - ss.save() - except Exception: - return json.dumps({'error':'invalid scheduledsession'}) - - return json.dumps({'message':'valid'}) - @role_required('Secretariat') @dajaxice_register -def update_timeslot_purpose(request, timeslot_id=None, purpose=None): +def update_timeslot_purpose(request, + meeting_num, + timeslot_id=None, + purpose =None, + room_id = None, + duration= None, + time = None): + + meeting = get_meeting(meeting_num) ts_id = int(timeslot_id) - try: - timeslot = TimeSlot.objects.get(pk=ts_id) - except: - return json.dumps({'error':'invalid timeslot'}) + time_str = time + if ts_id == 0: + try: + time = datetime.datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S') + except: + return json.dumps({'error':'invalid time: %s' % (time_str)}) + + try: + room = meeting.room_set.get(pk = int(room_id)) + except Room.DoesNotExist: + return json.dumps({'error':'invalid room id'}) + + timeslot = TimeSlot(meeting=meeting, + location = room, + time = time, + duration = duration) + else: + try: + timeslot = TimeSlot.objects.get(pk=ts_id) + except: + return json.dumps({'error':'invalid timeslot'}) try: timeslottypename = TimeSlotTypeName.objects.get(pk = purpose) @@ -149,9 +116,17 @@ def update_timeslot_purpose(request, timeslot_id=None, purpose=None): 'extra': purpose}) timeslot.type = timeslottypename - timeslot.save() + try: + timeslot.save() + except: + return json.dumps({'error':'failed to save'}) - return json.dumps(timeslot.json_dict(request.build_absolute_uri('/'))) + try: + # really should return 201 created, but dajaxice sucks. + json_dict = timeslot.json_dict(request.build_absolute_uri('/')) + return json.dumps(json_dict) + except: + return json.dumps({'error':'failed to save'}) ############################################################################# ## ROOM API @@ -192,6 +167,25 @@ def timeslot_delroom(request, meeting, roomid): room.delete() return HttpResponse('{"error":"none"}', status = 200) +@role_required('Secretariat') +def timeslot_updroom(request, meeting, roomid): + room = get_object_or_404(meeting.room_set, pk=roomid) + + if "name" in request.POST: + room.name = request.POST["name"] + + if "capacity" in request.POST: + room.capacity = request.POST["capacity"] + + if "resources" in request.POST: + new_resource_ids = request.POST["resources"] + new_resources = [ ResourceAssociation.objects.get(pk=a) + for a in new_resource_ids] + room.resources = new_resources + + room.save() + return HttpResponse('{"error":"none"}', status = 200) + def timeslot_roomsurl(request, num=None): meeting = get_meeting(num) @@ -210,14 +204,14 @@ def timeslot_roomurl(request, num=None, roomid=None): room = get_object_or_404(meeting.room_set, pk=roomid) return HttpResponse(json.dumps(room.json_dict(request.build_absolute_uri('/'))), content_type="application/json") -# XXX FIXME: timeslot_updroom() doesn't exist -# elif request.method == 'POST': -# return timeslot_updroom(request, meeting) + elif request.method == 'PUT': + return timeslot_updroom(request, meeting, roomid) elif request.method == 'DELETE': return timeslot_delroom(request, meeting, roomid) ############################################################################# ## DAY/SLOT API +## -- this creates groups of timeslots, and associated scheduledsessions. ############################################################################# AddSlotForm = modelform_factory(TimeSlot, exclude=('meeting','name','location','sessions', 'modified')) @@ -227,7 +221,7 @@ def timeslot_slotlist(request, mtg): json_array=[] for slot in slots: json_array.append(slot.json_dict(request.build_absolute_uri('/'))) - return HttpResponse(json.dumps(json_array), + return HttpResponse(json.dumps(json_array, sort_keys=True, indent=2), content_type="application/json") @role_required('Secretariat') @@ -326,6 +320,10 @@ def agenda_update(request, meeting, schedule): # (schedule, update_dict, request.body)) user = request.user + + cansee,canedit = agenda_permissions(meeting, schedule, request.user) + read_only = not canedit + if has_role(user, "Secretariat"): if "public" in request.POST: value1 = True @@ -335,13 +333,20 @@ def agenda_update(request, meeting, schedule): #debug.log("setting public for %s to %s" % (schedule, value1)) schedule.public = value1 - if "visible" in request.POST: + if "visible" in request.POST and cansee: value1 = True value = request.POST["visible"] if value == "0" or value == 0 or value=="false": value1 = False #debug.log("setting visible for %s to %s" % (schedule, value1)) schedule.visible = value1 + if has_role(user, "Secretariat") and canedit: + if "name" in request.POST: + value = request.POST["name"] + #log.debug("setting name for %s to %s" % (schedule, value)) + schedule.name = value + else: + return HttpResponse({'error':'no permission'}, status=401) if "name" in request.POST: value = request.POST["name"] @@ -382,11 +387,11 @@ def agenda_infosurl(request, num=None): # unacceptable action return HttpResponse(status=406) -def agenda_infourl(request, num=None, schedule_name=None): +def agenda_infourl(request, num=None, name=None): meeting = get_meeting(num) - #debug.log("agenda: %s / %s" % (meeting, schedule_name)) + #log.debug("agenda: %s / %s" % (meeting, name)) - schedule = get_schedule(meeting, schedule_name) + schedule = get_schedule(meeting, name) #debug.log("results in agenda: %u / %s" % (schedule.id, request.method)) if request.method == 'GET': @@ -417,13 +422,13 @@ def meeting_update(request, meeting): value = request.POST["agenda"] #debug.log("4 meeting.agenda: %s" % (value)) if not value or value == "None": # value == "None" is just weird, better with empty string - meeting.agenda = None + meeting.set_official_agenda(None) else: schedule = get_schedule(meeting, value) if not schedule.public: return HttpResponse(status = 406) #debug.log("3 meeting.agenda: %s" % (schedule)) - meeting.agenda = schedule + meeting.set_official_agenda(schedule) #debug.log("2 meeting.agenda: %s" % (meeting.agenda)) meeting.save() @@ -441,7 +446,7 @@ def meeting_json(request, num): ############################################################################# -## Agenda Editing API functions +## Session details API functions ############################################################################# def session_json(request, num, sessionid): @@ -459,6 +464,140 @@ def session_json(request, num, sessionid): return HttpResponse(json.dumps(sess1, sort_keys=True, indent=2), content_type="application/json") +# get group of all sessions. +def sessions_json(request, num): + meeting = get_meeting(num) + + sessions = meeting.sessions_that_can_meet.all() + + sess1_dict = [ x.json_dict(request.build_absolute_uri('/')) for x in sessions ] + return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2), + content_type="application/json") + +############################################################################# +## Scheduledsesion +############################################################################# + +def scheduledsessions_post(request, meeting, schedule): + cansee,canedit = agenda_permissions(meeting, schedule, request.user) + if not canedit: + return HttpResponse(json.dumps({'error':'no permission to modify this agenda'}), + status = 403, + content_type="application/json") + + # get JSON out of raw body. XXX should check Content-Type! + newvalues = json.loads(request.body) + if not ("session_id" in newvalues) or not ("timeslot_id" in newvalues): + return HttpResponse(json.dumps({'error':'missing values, timeslot_id and session_id required'}), + status = 406, + content_type="application/json") + + ss1 = ScheduledSession(schedule = schedule, + session_id = newvalues["session_id"], + timeslot_id = newvalues["timeslot_id"]) + if("extendedfrom_id" in newvalues): + val = int(newvalues["extendedfrom_id"]) + try: + ss2 = schedule.scheduledsessions_set.get(pk = val) + ss1.extendedfrom = ss2 + except ScheduledSession.DoesNotExist: + return HttpResponse(json.dumps({'error':'invalid extendedfrom value: %u' % val}), + status = 406, + content_type="application/json") + + ss1.save() + ss1_dict = ss1.json_dict(request.build_absolute_uri('/')) + response = HttpResponse(json.dumps(ss1_dict), + status = 201, + content_type="application/json") + # 201 code needs a Location: header. + response['Location'] = ss1_dict["href"], + return response + +def scheduledsessions_get(request, num, schedule): + scheduledsessions = schedule.scheduledsession_set.all() + + sess1_dict = [ x.json_dict(request.build_absolute_uri('/')) for x in scheduledsessions ] + return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2), + content_type="application/json") + +# this returns the list of scheduled sessions for the given named agenda +def scheduledsessions_json(request, num, name): + meeting = get_meeting(num) + schedule = get_schedule(meeting, name) + + if request.method == 'GET': + return scheduledsessions_get(request, meeting, schedule) + elif request.method == 'POST': + return scheduledsessions_post(request, meeting, schedule) + else: + return HttpResponse(json.dumps({'error':'inappropriate action: %s' % (request.method)}), + status = 406, + content_type="application/json") + + +def scheduledsession_update(request, meeting, schedule, scheduledsession_id): + cansee,canedit = agenda_permissions(meeting, schedule, request.user) + if not canedit or True: + return HttpResponse(json.dumps({'error':'no permission to update this agenda'}), + status = 403, + content_type="application/json") + + +def scheduledsession_delete(request, meeting, schedule, scheduledsession_id): + cansee,canedit = agenda_permissions(meeting, schedule, request.user) + if not canedit: + return HttpResponse(json.dumps({'error':'no permission to update this agenda'}), + status = 403, + content_type="application/json") + + scheduledsessions = schedule.scheduledsession_set.filter(pk = scheduledsession_id) + if len(scheduledsessions) == 0: + return HttpResponse(json.dumps({'error':'no such object'}), + status = 404, + content_type="application/json") + + count=0 + for ss in scheduledsessions: + ss.delete() + count += 1 + + return HttpResponse(json.dumps({'result':"%u objects deleted"%(count)}), + status = 200, + content_type="application/json") + +def scheduledsession_get(request, meeting, schedule, scheduledsession_id): + cansee,canedit = agenda_permissions(meeting, schedule, request.user) + + if not cansee: + return HttpResponse(json.dumps({'error':'no permission to see this agenda'}), + status = 403, + content_type="application/json") + + scheduledsessions = schedule.scheduledsession_set.filter(pk = scheduledsession_id) + if len(scheduledsessions) == 0: + return HttpResponse(json.dumps({'error':'no such object'}), + status = 404, + content_type="application/json") + + sess1_dict = scheduledsessions[0].json_dict(request.build_absolute_uri('/')) + return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2), + content_type="application/json") + +# this returns the list of scheduled sessions for the given named agenda +def scheduledsession_json(request, num, name, scheduledsession_id): + meeting = get_meeting(num) + schedule = get_schedule(meeting, name) + + scheduledsession_id = int(scheduledsession_id) + + if request.method == 'GET': + return scheduledsession_get(request, meeting, schedule, scheduledsession_id) + elif request.method == 'PUT': + return scheduledsession_update(request, meeting, schedule, scheduledsession_id) + elif request.method == 'DELETE': + return scheduledsession_delete(request, meeting, schedule, scheduledsession_id) + # Would like to cache for 1 day, but there are invalidation issues. #@cache_page(86400) def constraint_json(request, num, constraintid): diff --git a/ietf/meeting/management/__init__.py b/ietf/meeting/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ietf/meeting/management/commands/__init__.py b/ietf/meeting/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ietf/meeting/management/commands/autoplace.py b/ietf/meeting/management/commands/autoplace.py new file mode 100644 index 000000000..8a96616b4 --- /dev/null +++ b/ietf/meeting/management/commands/autoplace.py @@ -0,0 +1,124 @@ +""" +Runs the automatic placement code (simulated annealing of glass) +for a given meeting number, using a schedule given by the schedule database ID. + +for help on this file: +https://docs.djangoproject.com/en/dev/howto/custom-management-commands/ + +""" + +from django.core.management.base import BaseCommand, CommandError +from optparse import make_option +#import cProfile, pstats, io +import os +import sys +import gzip +import time +import csv +import codecs +from ietf.meeting.models import Schedule, Meeting + +class Command(BaseCommand): + args = ' ' + help = 'perform automatic placement' + stderr = sys.stderr + stdout = sys.stdout + + verbose = False + profile = False + permit_movement = False + maxstep = 20000 + seed = None + recordsteps = False + + option_list = BaseCommand.option_list + ( + make_option('--profile', + action='store_true', + dest='profile', + default=False, + help='Enable verbose mode'), + make_option('--recordsteps', + action='store_true', + dest='recordsteps', + default=False, + help='Enable recording progress to table'), + make_option('--verbose', + action='count', + dest='verbose', + default=False, + help='Enable verbose mode'), + make_option('--maxstep', + action="store", type="int", + dest='maxstep', + default=20000, + help='Maximum number of steps'), + make_option('--seed', + action="store", type="int", + dest='seed', + default=None, + help='Seed to use for calculation'), + ) + + def handle(self, *labels, **options): + self.verbose = options.get('verbose', 1) + + meetingname = labels[0] + schedname = labels[1] + targetname = None + if labels[2] is not None: + targetname = labels[2] + + seed = options.get('seed', None) + maxstep = options.get('maxstep', 20000) + verbose = options.get('verbose', False) + profile = options.get('profile', False) + recordsteps = options.get('recordsteps', False) + + from ietf.meeting.helpers import get_meeting,get_schedule + try: + meeting = get_meeting(meetingname) + except Meeting.DoesNotExist: + print "No such meeting: %s" % (meetingname) + return + + try: + schedule = meeting.schedule_set.get(name = schedname) + except Schedule.DoesNotExist: + print "No such schedule: %s in meeting: %s" % (schedname, meeting) + return + + if targetname is not None: + try: + targetsched = meeting.schedule_set.get(name=targetname) + except Schedule.DoesNotExist: + print "Creating new schedule %s" % (targetname) + targetsched = Schedule(meeting = meeting, + owner = schedule.owner, + name = targetname) + targetsched.save() + else: + targetsched = schedule + + print "Saving results to %s" % (targetsched.name) + + from ietf.meeting.placement import CurrentScheduleState + css = CurrentScheduleState(schedule, seed) + css.recordsteps = recordsteps + css.verbose = verbose + + if profile: + import cProfile + import re + cProfile.runctx('css.do_placement(maxstep, targetsched)', + vars(), + vars(), + 'placestats.pyprof') + + import pstats + p = pstats.Stats('placestats.pyprof') + p.strip_dirs().sort_stats(-1).print_stats() + else: + css.do_placement(maxstep, targetsched) + + + diff --git a/ietf/meeting/migrations/0014_add_room.py b/ietf/meeting/migrations/0014_add_room.py new file mode 100644 index 000000000..eaa54b0b8 --- /dev/null +++ b/ietf/meeting/migrations/0014_add_room.py @@ -0,0 +1,350 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'ResourceAssociation' + db.create_table('meeting_resourceassociation', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.RoomResourceName'])), + ('icon', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('desc', self.gf('django.db.models.fields.CharField')(max_length=256)), + )) + db.send_create_signal('meeting', ['ResourceAssociation']) + + # Adding M2M table for field resources on 'Room' + m2m_table_name = db.shorten_name('meeting_room_resources') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('room', models.ForeignKey(orm['meeting.room'], null=False)), + ('resourceassociation', models.ForeignKey(orm['meeting.resourceassociation'], null=False)) + )) + db.create_unique(m2m_table_name, ['room_id', 'resourceassociation_id']) + + def backwards(self, orm): + # Deleting model 'ResourceAssociation' + db.delete_table('meeting_resourceassociation') + + # Removing M2M table for field resources on 'Room' + db.delete_table(db.shorten_name('meeting_room_resources')) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'meeting.constraint': { + 'Meta': {'object_name': 'Constraint'}, + 'day': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.ConstraintName']"}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_source_set'", 'to': "orm['group.Group']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_target_set'", 'null': 'True', 'to': "orm['group.Group']"}) + }, + 'meeting.meeting': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Meeting'}, + 'agenda': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['meeting.Schedule']"}), + 'agenda_note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'break_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'reg_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'time_zone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.MeetingTypeName']"}), + 'venue_addr': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'meeting.resourceassociation': { + 'Meta': {'object_name': 'ResourceAssociation'}, + 'desc': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'icon': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.RoomResourceName']"}) + }, + 'meeting.room': { + 'Meta': {'object_name': 'Room'}, + 'capacity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['meeting.ResourceAssociation']", 'symmetrical': 'False'}) + }, + 'meeting.schedule': { + 'Meta': {'object_name': 'Schedule'}, + 'badness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'meeting.scheduledsession': { + 'Meta': {'ordering': "['timeslot__time', 'session__group__parent__name', 'session__group__acronym', 'session__name']", 'object_name': 'ScheduledSession'}, + 'badness': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'extendedfrom': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.ScheduledSession']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pinned': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'schedule': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assignments'", 'to': "orm['meeting.Schedule']"}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.Session']", 'null': 'True'}), + 'timeslot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.TimeSlot']"}) + }, + 'meeting.session': { + 'Meta': {'object_name': 'Session'}, + 'agenda_note': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'attendees': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'materials': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'requested': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'requested_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'requested_duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {'default': '0'}), + 'scheduled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.SessionStatusName']"}) + }, + 'meeting.timeslot': { + 'Meta': {'object_name': 'TimeSlot'}, + 'duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Room']", 'null': 'True', 'blank': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'sessions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'slots'", 'to': "orm['meeting.Session']", 'through': "orm['meeting.ScheduledSession']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'show_location': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.TimeSlotTypeName']"}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.roomresourcename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['meeting'] diff --git a/ietf/meeting/migrations/0015_add_basic_resources.py b/ietf/meeting/migrations/0015_add_basic_resources.py new file mode 100644 index 000000000..a1c87e780 --- /dev/null +++ b/ietf/meeting/migrations/0015_add_basic_resources.py @@ -0,0 +1,339 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + orm.ResourceAssociation(name_id = "project", + icon = "12161797831849255690jcartier_board.svg", + desc = "Projector in room").save() + orm.ResourceAssociation(name_id = "proj2", + icon = "projector2.svg", + desc = "Second projector in room").save() + orm.ResourceAssociation(name_id = "meetecho", + icon = "meetecho-mini.png", + desc = "Meetecho support in room").save() + + + def backwards(self, orm): + "Write your backwards methods here." + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'meeting.constraint': { + 'Meta': {'object_name': 'Constraint'}, + 'day': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.ConstraintName']"}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_source_set'", 'to': "orm['group.Group']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_target_set'", 'null': 'True', 'to': "orm['group.Group']"}) + }, + 'meeting.meeting': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Meeting'}, + 'agenda': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['meeting.Schedule']"}), + 'agenda_note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'break_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'reg_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'time_zone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.MeetingTypeName']"}), + 'venue_addr': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'meeting.resourceassociation': { + 'Meta': {'object_name': 'ResourceAssociation'}, + 'desc': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'icon': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.RoomResourceName']"}) + }, + 'meeting.room': { + 'Meta': {'object_name': 'Room'}, + 'capacity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['meeting.ResourceAssociation']", 'symmetrical': 'False'}) + }, + 'meeting.schedule': { + 'Meta': {'object_name': 'Schedule'}, + 'badness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'meeting.scheduledsession': { + 'Meta': {'ordering': "['timeslot__time', 'session__group__parent__name', 'session__group__acronym', 'session__name']", 'object_name': 'ScheduledSession'}, + 'badness': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'extendedfrom': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.ScheduledSession']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pinned': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'schedule': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assignments'", 'to': "orm['meeting.Schedule']"}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.Session']", 'null': 'True'}), + 'timeslot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.TimeSlot']"}) + }, + 'meeting.session': { + 'Meta': {'object_name': 'Session'}, + 'agenda_note': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'attendees': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'materials': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'requested': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'requested_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'requested_duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {'default': '0'}), + 'scheduled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.SessionStatusName']"}) + }, + 'meeting.timeslot': { + 'Meta': {'object_name': 'TimeSlot'}, + 'duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Room']", 'null': 'True', 'blank': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'sessions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'slots'", 'to': "orm['meeting.Session']", 'through': "orm['meeting.ScheduledSession']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'show_location': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.TimeSlotTypeName']"}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.roomresourcename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['meeting'] + symmetrical = True diff --git a/ietf/meeting/migrations/0016_add_resource_to_session.py b/ietf/meeting/migrations/0016_add_resource_to_session.py new file mode 100644 index 000000000..c3a4a5eb3 --- /dev/null +++ b/ietf/meeting/migrations/0016_add_resource_to_session.py @@ -0,0 +1,341 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding M2M table for field resources on 'Session' + m2m_table_name = db.shorten_name('meeting_session_resources') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('session', models.ForeignKey(orm['meeting.session'], null=False)), + ('resourceassociation', models.ForeignKey(orm['meeting.resourceassociation'], null=False)) + )) + db.create_unique(m2m_table_name, ['session_id', 'resourceassociation_id']) + + + def backwards(self, orm): + # Removing M2M table for field resources on 'Session' + db.delete_table(db.shorten_name('meeting_session_resources')) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + 'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + 'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + 'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'meeting.constraint': { + 'Meta': {'object_name': 'Constraint'}, + 'day': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.ConstraintName']"}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_source_set'", 'to': "orm['group.Group']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_target_set'", 'null': 'True', 'to': "orm['group.Group']"}) + }, + 'meeting.meeting': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Meeting'}, + 'agenda': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['meeting.Schedule']"}), + 'agenda_note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'break_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'reg_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'time_zone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.MeetingTypeName']"}), + 'venue_addr': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'meeting.resourceassociation': { + 'Meta': {'object_name': 'ResourceAssociation'}, + 'desc': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'icon': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.RoomResourceName']"}) + }, + 'meeting.room': { + 'Meta': {'object_name': 'Room'}, + 'capacity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['meeting.ResourceAssociation']", 'symmetrical': 'False'}) + }, + 'meeting.schedule': { + 'Meta': {'object_name': 'Schedule'}, + 'badness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'meeting.scheduledsession': { + 'Meta': {'ordering': "['timeslot__time', 'session__group__parent__name', 'session__group__acronym', 'session__name']", 'object_name': 'ScheduledSession'}, + 'badness': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'extendedfrom': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.ScheduledSession']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pinned': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'schedule': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assignments'", 'to': "orm['meeting.Schedule']"}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.Session']", 'null': 'True'}), + 'timeslot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.TimeSlot']"}) + }, + 'meeting.session': { + 'Meta': {'object_name': 'Session'}, + 'agenda_note': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'attendees': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'materials': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'requested': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'requested_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'requested_duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {'default': '0'}), + 'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['meeting.ResourceAssociation']", 'symmetrical': 'False'}), + 'scheduled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.SessionStatusName']"}) + }, + 'meeting.timeslot': { + 'Meta': {'object_name': 'TimeSlot'}, + 'duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Room']", 'null': 'True', 'blank': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'sessions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'slots'", 'to': "orm['meeting.Session']", 'through': "orm['meeting.ScheduledSession']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'show_location': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.TimeSlotTypeName']"}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.roomresourcename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['meeting'] \ No newline at end of file diff --git a/ietf/meeting/migrations/0017_remove_empty_session_scheduledsessions.py b/ietf/meeting/migrations/0017_remove_empty_session_scheduledsessions.py new file mode 100644 index 000000000..d6427cec7 --- /dev/null +++ b/ietf/meeting/migrations/0017_remove_empty_session_scheduledsessions.py @@ -0,0 +1,335 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + # Note: Don't use "from appname.models import ModelName". + # Use orm.ModelName to refer to models in this application, + # and orm['appname.ModelName'] for models in other applications. + orm.ScheduledSession.objects.exclude(session__isnull=False).delete() + + def backwards(self, orm): + "Write your backwards methods here." + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'doc.document': { + 'Meta': {'object_name': 'Document'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': u"orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocumentAuthor']", 'blank': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}), + 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': u"orm['person.Person']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + u'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + u'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'doc.statetype': { + 'Meta': {'object_name': 'StateType'}, + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}) + }, + u'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': u"orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'meeting.constraint': { + 'Meta': {'object_name': 'Constraint'}, + 'day': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']"}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.ConstraintName']"}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_source_set'", 'to': u"orm['group.Group']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_target_set'", 'null': 'True', 'to': u"orm['group.Group']"}) + }, + u'meeting.meeting': { + 'Meta': {'ordering': "['-date']", 'object_name': 'Meeting'}, + 'agenda': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': u"orm['meeting.Schedule']"}), + 'agenda_note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'break_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'reg_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'time_zone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.MeetingTypeName']"}), + 'venue_addr': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + u'meeting.resourceassociation': { + 'Meta': {'object_name': 'ResourceAssociation'}, + 'desc': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'icon': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.RoomResourceName']"}) + }, + u'meeting.room': { + 'Meta': {'object_name': 'Room'}, + 'capacity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['meeting.ResourceAssociation']", 'symmetrical': 'False'}) + }, + u'meeting.schedule': { + 'Meta': {'object_name': 'Schedule'}, + 'badness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'meeting.scheduledsession': { + 'Meta': {'ordering': "['timeslot__time', 'session__group__parent__name', 'session__group__acronym', 'session__name']", 'object_name': 'ScheduledSession'}, + 'badness': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'extendedfrom': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['meeting.ScheduledSession']", 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pinned': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'schedule': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assignments'", 'to': u"orm['meeting.Schedule']"}), + 'session': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['meeting.Session']", 'null': 'True'}), + 'timeslot': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.TimeSlot']"}) + }, + u'meeting.session': { + 'Meta': {'object_name': 'Session'}, + 'agenda_note': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'attendees': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'materials': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'requested': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'requested_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}), + 'requested_duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {'default': '0'}), + 'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['meeting.ResourceAssociation']", 'symmetrical': 'False'}), + 'scheduled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.SessionStatusName']"}) + }, + u'meeting.timeslot': { + 'Meta': {'object_name': 'TimeSlot'}, + 'duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Room']", 'null': 'True', 'blank': 'True'}), + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']"}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'sessions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'slots'", 'to': u"orm['meeting.Session']", 'through': u"orm['meeting.ScheduledSession']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}), + 'show_location': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.TimeSlotTypeName']"}) + }, + u'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.roomresourcename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'person.email': { + 'Meta': {'object_name': 'Email'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + u'person.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}), + 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['meeting'] + symmetrical = True diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index 7dac474ee..2c50b8de5 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -4,6 +4,7 @@ import pytz, datetime from urlparse import urljoin import copy import os +import sys import re import debug @@ -18,7 +19,7 @@ from django.template.defaultfilters import slugify, date as date_format, time as from ietf.group.models import Group from ietf.person.models import Person from ietf.doc.models import Document -from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName +from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName countries = pytz.country_names.items() countries.sort(lambda x,y: cmp(x[1], y[1])) @@ -111,6 +112,15 @@ class Meeting(models.Model): def sessions_that_can_meet(self): return self.session_set.exclude(status__slug='notmeet').exclude(status__slug='disappr').exclude(status__slug='deleted').exclude(status__slug='apprw') + def sessions_that_can_be_placed(self): + from django.db.models import Q + donotplace_groups = Q(group__acronym="edu") + donotplace_groups |= Q(group__acronym="tools") + donotplace_groups |= Q(group__acronym="iesg") + donotplace_groups |= Q(group__acronym="ietf") + donotplace_groups |= Q(group__acronym="iepg") + donotplace_groups |= Q(group__acronym="iab") + return self.sessions_that_can_meet.exclude(donotplace_groups) def json_url(self): return "/meeting/%s.json" % (self.number, ) @@ -157,8 +167,8 @@ class Meeting(models.Model): if ymd in time_slices: # only keep unique entries - if [ts.time, ts.time + ts.duration] not in time_slices[ymd]: - time_slices[ymd].append([ts.time, ts.time + ts.duration]) + if [ts.time, ts.time + ts.duration, ts.duration.seconds] not in time_slices[ymd]: + time_slices[ymd].append([ts.time, ts.time + ts.duration, ts.duration.seconds]) slots[ymd].append(ts) days.sort() @@ -195,13 +205,38 @@ class Meeting(models.Model): pass return '' + def set_official_agenda(self, agenda): + if self.agenda != agenda: + self.agenda = agenda + self.save() + if self.agenda is not None: + self.agenda.sendEmail() + class Meta: ordering = ["-date", ] +class ResourceAssociation(models.Model): + name = models.ForeignKey(RoomResourceName) + #url = models.UrlField() # not sure what this was for. + icon = models.CharField(max_length=64) # icon to be found in /static/img + desc = models.CharField(max_length=256) + + def __unicode__(self): + return self.desc + + def json_dict(self, host_scheme): + res1 = dict() + res1['name'] = self.name.slug + res1['icon'] = "/images/%s" % (self.icon) + res1['desc'] = self.desc + res1['resource_id'] = self.pk + return res1 + class Room(models.Model): meeting = models.ForeignKey(Meeting) name = models.CharField(max_length=255) capacity = models.IntegerField(null=True, blank=True) + resources = models.ManyToManyField(ResourceAssociation, blank = True) def __unicode__(self): return "%s size: %u" % (self.name, self.capacity) @@ -223,6 +258,9 @@ class Room(models.Model): duration=ts.duration) self.meeting.create_all_timeslots() + def domid(self): + return "room%u" % (self.pk) + def json_url(self): return "/meeting/%s/room/%s.json" % (self.meeting.number, self.id) @@ -325,16 +363,25 @@ class TimeSlot(models.Model): # {{s.timeslot.time|date:'Y-m-d'}}_{{ s.timeslot.time|date:'Hi' }}" # also must match: # {{r|slugify}}_{{day}}_{{slot.0|date:'Hi'}} - return "%s_%s_%s" % (slugify(self.get_location()), self.time.strftime('%Y-%m-%d'), self.time.strftime('%H%M')) + domid="ts%u" % (self.pk) + if self.location is not None: + domid = self.location.domid() + return "%s_%s_%s" % (domid, self.time.strftime('%Y-%m-%d'), self.time.strftime('%H%M')) - def json_dict(self, selfurl): + def json_dict(self, host_scheme): ts = dict() ts['timeslot_id'] = self.id - ts['room'] = slugify(self.location) + ts['href'] = urljoin(host_scheme, self.json_url()) + ts['room'] = self.get_location() ts['roomtype'] = self.type.slug + if self.location is not None: + ts['capacity'] = self.location.capacity ts["time"] = date_format(self.time, 'Hi') - ts["date"] = time_format(self.time, 'Y-m-d') + ts["date"] = fmt_date(self.time) ts["domid"] = self.js_identifier + following = self.slot_to_the_right + if following is not None: + ts["following_timeslot_id"] = following.id return ts def json_url(self): @@ -418,11 +465,18 @@ class Schedule(models.Model): # def url_edit(self): # return "/meeting/%s/agenda/%s/edit" % (self.meeting.number, self.name) -# +# # @property # def relurl_edit(self): # return self.url_edit("") + def owner_email(self): + emails = self.owner.email_set.all() + if len(emails)>0: + return emails[0].address + else: + return "noemail" + @property def visible_token(self): if self.visible: @@ -512,12 +566,11 @@ class Schedule(models.Model): scheduled += 1 return assignments,sessions,total,scheduled - cached_sessions_that_can_meet = None @property def sessions_that_can_meet(self): - if self.cached_sessions_that_can_meet is None: - self.cached_sessions_that_can_meet = self.meeting.sessions_that_can_meet.all() - return self.cached_sessions_that_can_meet + if not hasattr(self, "_cached_sessions_that_can_meet"): + self._cached_sessions_that_can_meet = self.meeting.sessions_that_can_meet.all() + return self._cached_sessions_that_can_meet def area_list(self): return ( self.assignments.filter(session__group__type__slug__in=['wg', 'rg', 'ag', 'iab'], @@ -529,6 +582,41 @@ class Schedule(models.Model): def groups(self): return Group.objects.filter(type__slug__in=['wg', 'rg', 'ag', 'iab'], session__scheduledsession__schedule=self).distinct().order_by('parent__acronym', 'acronym') + @property + def qs_scheduledsessions_with_assignments(self): + return self.scheduledsession_set.filter(session__isnull=False) + + # calculate badness of entire schedule + def calc_badness(self): + # now calculate badness + assignments = self.group_mapping + return self.calc_badness1(assignments) + + # calculate badness of entire schedule + def calc_badness1(self, assignments): + badness = 0 + for sess in self.sessions_that_can_meet: + badness += sess.badness(assignments) + self.badness = badness + return badness + + def delete_schedule(self): + self.scheduledsession_set.all().delete() + self.delete() + + # send email to every session requester whose session is now scheduled. + # mark the sessions as now state scheduled, rather than waiting. + def sendEmail(self): + for ss in self.scheduledsession_set.all(): + session = ss.session + if session.status.slug == "schedw": + session.status_id = "sched" + session.scheduled = datetime.datetime.now() + session.save() + from ietf.secr.meetings.views import send_notification + send_notification(None, Session.objects.filter(id=session.id)) + +# to be renamed ScheduleTimeslotSessionAssignments (stsa) class ScheduledSession(models.Model): """ This model provides an N:M relationship between Session and TimeSlot. @@ -601,27 +689,31 @@ class ScheduledSession(models.Model): else: return "" - @property - def empty_str(self): - # return JS happy value - if self.session: - return "False" - else: - return "True" + def json_url(self): + return "/meeting/%s/schedule/%s/session/%u.json" % (self.schedule.meeting.number, + self.schedule.name, self.id) - def json_dict(self, selfurl): + def json_dict(self, host_scheme): ss = dict() ss['scheduledsession_id'] = self.id - #ss['href'] = self.url(host_scheme) - ss['empty'] = self.empty_str + ss['href'] = urljoin(host_scheme, self.json_url()) ss['timeslot_id'] = self.timeslot.id + + efset = self.session.scheduledsession_set.filter(schedule=self.schedule).order_by("timeslot__time") + if efset.count() > 1: + # now we know that there is some work to do finding the extendedfrom_id. + # loop through the list of items + previous = None + for efss in efset: + if efss.pk == self.pk: + extendedfrom = previous + break + previous = efss + if extendedfrom is not None: + ss['extendedfrom_id'] = extendedfrom.id + if self.session: ss['session_id'] = self.session.id - ss['room'] = slugify(self.timeslot.location) - ss['roomtype'] = self.timeslot.type.slug - ss["time"] = date_format(self.timeslot.time, 'Hi') - ss["date"] = time_format(self.timeslot.time, 'Y-m-d') - ss["domid"] = self.timeslot.js_identifier ss["pinned"] = self.pinned return ss @@ -670,9 +762,8 @@ class Constraint(models.Model): return True return False - @property def constraint_cost(self): - return self.name.cost(); + return self.name.penalty; def json_url(self): return "/meeting/%s/constraint/%s.json" % (self.meeting.number, self.id) @@ -715,6 +806,7 @@ class Session(models.Model): modified = models.DateTimeField(default=datetime.datetime.now) materials = models.ManyToManyField(Document, blank=True) + resources = models.ManyToManyField(ResourceAssociation) unique_constraints_dict = None @@ -747,6 +839,9 @@ class Session(models.Model): ss0name = ss[0].timeslot.time.strftime("%H%M") return u"%s: %s %s[%u]" % (self.meeting, self.group.acronym, ss0name, self.pk) + def is_bof(self): + return self.group.is_bof(); + @property def short_name(self): if self.name: @@ -817,18 +912,20 @@ class Session(models.Model): sess1['href'] = urljoin(host_scheme, self.json_url()) if self.group is not None: sess1['group'] = self.group.json_dict(host_scheme) - # nuke rest of these as soon as JS cleaned up. sess1['group_href'] = urljoin(host_scheme, self.group.json_url()) - sess1['group_acronym'] = str(self.group.acronym) if self.group.parent is not None: sess1['area'] = str(self.group.parent.acronym).upper() - sess1['GroupInfo_state']= str(self.group.state) sess1['description'] = str(self.group.name) sess1['group_id'] = str(self.group.pk) + reslist = [] + for r in self.resources.all(): + reslist.append(r.json_dict(host_scheme)) + sess1['resources'] = reslist sess1['session_id'] = str(self.pk) sess1['name'] = str(self.name) sess1['title'] = str(self.short_name) sess1['short_name'] = str(self.short_name) + sess1['bof'] = str(self.is_bof()) sess1['agenda_note'] = str(self.agenda_note) sess1['attendees'] = str(self.attendees) sess1['status'] = str(self.status) @@ -843,17 +940,57 @@ class Session(models.Model): pass sess1['requested_duration']= "%.1f" % (float(self.requested_duration.seconds) / 3600) - sess1['duration'] = sess1['requested_duration'] sess1['special_request'] = str(self.special_request_token) return sess1 + def agenda_text(self): + doc = self.agenda() + if doc: + path = os.path.join(settings.AGENDA_PATH, self.meeting.number, "agenda", doc.external_url) + if os.path.exists(path): + with open(path) as f: + return f.read() + else: + return "No agenda file found" + else: + return "The agenda has not been uploaded yet." + + def type(self): + if self.group.type.slug in [ "wg" ]: + return "BOF" if self.group.state.slug in ["bof", "bof-conc"] else "WG" + else: + return "" + + def ical_status(self): + if self.status.slug == 'canceled': # sic + return "CANCELLED" + elif (datetime.date.today() - self.meeting.date) > datetime.timedelta(days=5): + # this is a bit simpleminded, better would be to look at the + # time(s) of the timeslot(s) of the official meeting schedule. + return "CONFIRMED" + else: + return "TENTATIVE" + + def agenda_file(self): + if not hasattr(self, '_agenda_file'): + self._agenda_file = "" + + docs = self.materials.filter(type="agenda", states__type="agenda", states__slug="active") + if not docs: + return "" + + # we use external_url at the moment, should probably regularize + # the filenames to match the document name instead + filename = docs[0].external_url + self._agenda_file = "%s/agenda/%s" % (self.meeting.number, filename) + + return self._agenda_file def badness_test(self, num): from settings import BADNESS_CALC_LOG #sys.stdout.write("num: %u / BAD: %u\n" % (num, BADNESS_CALC_LOG)) return BADNESS_CALC_LOG >= num def badness_log(self, num, msg): - import sys if self.badness_test(num): sys.stdout.write(msg) @@ -878,7 +1015,7 @@ class Session(models.Model): conflicts = self.unique_constraints() if self.badness_test(2): - self.badness_log(2, "badgroup: %s badness calculation has %u constraints\n" % (self.group.acronym, len(conflicts))) + self.badness_log(2, "badness for group: %s has %u constraints\n" % (self.group.acronym, len(conflicts))) from settings import BADNESS_UNPLACED, BADNESS_TOOSMALL_50, BADNESS_TOOSMALL_100, BADNESS_TOOBIG, BADNESS_MUCHTOOBIG count = 0 myss_list = assignments[self.group] @@ -946,9 +1083,9 @@ class Session(models.Model): if self.badness_test(3): self.badness_log(3, " [%u] 4group: %s my_sessions: %s vs %s\n" % (count, group.acronym, myss.timeslot.time, ss.timeslot.time)) if ss.timeslot.time == myss.timeslot.time: - newcost = constraint.constraint_cost + newcost = constraint.constraint_cost() if self.badness_test(2): - self.badness_log(2, " [%u] 5group: %s conflicts: %s on %s cost %u\n" % (count, self.group.acronym, ss.session.group.acronym, ss.timeslot.time, newcost)) + self.badness_log(2, " [%u] 5group: %s conflict(%s): %s on %s cost %u\n" % (count, self.group.acronym, constraint.name_id, ss.session.group.acronym, ss.timeslot.time, newcost)) # yes accumulate badness. conflictbadness += newcost ss.badness = conflictbadness @@ -1035,51 +1172,8 @@ class Session(models.Model): if ss.timeslot is not None and ss.timeslot.location == timeslot.location: continue # ignore conflicts when two sessions in the same room constraint = conflict[1] - badness += constraint.constraint_cost + badness += constraint.constraint_cost() if self.badness_test(1): self.badness_log(1, "badgroup: %s badness = %u\n" % (self.group.acronym, badness)) return badness - - def agenda_text(self): - doc = self.agenda() - if doc: - path = os.path.join(settings.AGENDA_PATH, self.meeting.number, "agenda", doc.external_url) - if os.path.exists(path): - with open(path) as f: - return f.read() - else: - return "No agenda file found" - else: - return "The agenda has not been uploaded yet." - - def type(self): - if self.group.type.slug in [ "wg" ]: - return "BOF" if self.group.state.slug in ["bof", "bof-conc"] else "WG" - else: - return "" - - def ical_status(self): - if self.status.slug == 'canceled': # sic - return "CANCELLED" - elif (datetime.date.today() - self.meeting.date) > datetime.timedelta(days=5): - # this is a bit simpleminded, better would be to look at the - # time(s) of the timeslot(s) of the official meeting schedule. - return "CONFIRMED" - else: - return "TENTATIVE" - - def agenda_file(self): - if not hasattr(self, '_agenda_file'): - self._agenda_file = "" - - docs = self.materials.filter(type="agenda", states__type="agenda", states__slug="active") - if not docs: - return "" - - # we use external_url at the moment, should probably regularize - # the filenames to match the document name instead - filename = docs[0].external_url - self._agenda_file = "%s/agenda/%s" % (self.meeting.number, filename) - - return self._agenda_file diff --git a/ietf/meeting/placement.py b/ietf/meeting/placement.py new file mode 100644 index 000000000..bcd8d47b3 --- /dev/null +++ b/ietf/meeting/placement.py @@ -0,0 +1,738 @@ +# FILE: ietf/meeting/placement.py +# +# Copyright (c) 2013, The IETF Trust. See ../../../LICENSE. +# +# This file contains a model that encapsulates the progress of the automatic placer. +# Each step of placement is stored as a row in a table, not because this is necessary, +# but because it helps to debug things. +# +# A production run of the placer would do the same work, but simply not save anything. +# + +import sys +import datetime + +from random import Random +from datetime import datetime + +from django.db import models +from settings import BADNESS_UNPLACED, BADNESS_TOOSMALL_50, BADNESS_TOOSMALL_100, BADNESS_TOOBIG, BADNESS_MUCHTOOBIG +from ietf.meeting.models import Schedule, ScheduledSession,TimeSlot,Room + +def do_prompt(): + print "waiting:" + sys.stdin.readline() + +class PlacementException(Exception): + pass + +# ScheduleSlot really represents a single column of time. +# The TimeSlot object would work here, but it associates a room. +# There is a special Schedule slot (subclass) which corresponds to unscheduled items. +class ScheduleSlot(object): + def __init__(self, daytime): + self.daytime = daytime + self.badness = None + self.slotgroups = {} + + # this is a partial copy of ScheduledSession's methods. Prune later. + #def __unicode__(self): + # return u"%s [%s<->%s]" % (self.schedule, self.session, self.timeslot) + # + #def __str__(self): + # return self.__unicode__() + + def add_scheduledsession(self,fs): + self.slotgroups[fs] = fs + + def scheduled_session_pk(self, assignments): + things = [] + slot1 = assignments.slot1 + slot2 = assignments.slot2 + for fs in self.slotgroups.iterkeys(): + session = fs.session + if slot1 is not None and fs == slot1: + session = slot2.session + if slot2 is not None and fs == slot2: + session = slot1.session + if session is not None: + things.append((session.pk,fs)) + return things + + def recalc_badness1(self, assignments): + badness = 0 + for fs,fs2 in self.slotgroups.iteritems(): + if fs.session is not None: + num = fs.session.badness2(self) + #print "rc,,,,%s,%s,%u,recalc1" % (self.daytime, fs.session.short_name, num) + badness += num + self.badness = badness + + def recalc_badness(self, assignments): + badness = 0 + session_pk_list = self.scheduled_session_pk(assignments) + #print "rc,,,%u,slot_recalc" % (len(session_pk_list)) + for pk,fs in session_pk_list: + #print "rc,,,,%u,%s,list" % (pk,fs.session) + if fs.session is not None: + num = fs.session.badness_fast(fs.timeslot, self, session_pk_list) + #print "rc,,,,%s,%s,%u,recalc0" % (self.daytime, fs.session.short_name, num) + badness += num + self.badness = badness + + def calc_badness(self, assignments): + if self.badness is None: + self.recalc_badness(assignments) + return self.badness + +# +# this subclass does everything a ScheduleSlot does, in particular it knows how to +# maintain and recalculate badness, but it also maintains a list of slots which +# are unplaced so as to accelerate finding things to place at the beginning of automatic placement. +# +# XXX perhaps this should be in the form an iterator? +# +class UnplacedScheduleSlot(ScheduleSlot): + def __init__(self): + super(UnplacedScheduleSlot, self).__init__(None) + self.unplaced_slot_numbers = [] + self.unplaced_slots_finishcount = 0 + + def shuffle(self, generator): + generator.shuffle(self.unplaced_slot_numbers) + self.unplaced_slots_finishcount = self.count / 10 + + def finished(self): + if len(self.unplaced_slot_numbers) <= self.unplaced_slots_finishcount: + return True + else: + return False + + @property + def count(self): + return len(self.unplaced_slot_numbers) + + def add_scheduledsession(self,fs): + super(UnplacedScheduleSlot, self).add_scheduledsession(fs) + #print "unplaced add: %s" % (fs.available_slot) + self.unplaced_slot_numbers.append(fs.available_slot) + + def get_unplaced_slot_number(self): + #print "unplaced slots: %s" % (self.unplaced_slot_numbers) + return self.unplaced_slot_numbers[0] + + def delete_first(self): + del self.unplaced_slot_numbers[0] + + +class FakeScheduledSession(object): + """ + This model provides a fake (not-backed by database) N:M relationship between + Session and TimeSlot, but in this case TimeSlot is always None, because the + Session is not scheduled. + """ + faked = "fake" + + def __init__(self, schedule): + self.extendedfrom = None + self.modified = None + self.notes = None + self.badness = None + self.available_slot = None + self.origss = None + self.timeslot = None + self.session = None + self.schedule = schedule + self.pinned = False + self.scheduleslot = None + + def fromScheduledSession(self, ss): # or from another FakeScheduledSession + self.session = ss.session + self.schedule = ss.schedule + self.timeslot = ss.timeslot + self.modified = ss.modified + self.pinned = ss.pinned + self.origss = ss + + def save(self): + pass + + # this is a partial copy of ScheduledSession's methods. Prune later. + def __unicode__(self): + return u"%s [%s<->%s]" % (self.schedule, self.session, self.timeslot) + + def __str__(self): + return self.__unicode__() + + @property + def room_name(self): + return "noroom" + + @property + def special_agenda_note(self): + return self.session.agenda_note if self.session else "" + + @property + def acronym(self): + if self.session and self.session.group: + return self.session.group.acronym + + @property + def slot_to_the_right(self): + return None + + @property + def acronym_name(self): + if not self.session: + return self.notes + if hasattr(self, "interim"): + return self.session.group.name + " (interim)" + elif self.session.name: + return self.session.name + else: + return self.session.group.name + + @property + def session_name(self): + return self.session.name + + @property + def area(self): + if not self.session or not self.session.group: + return "" + if self.session.group.type_id == "irtf": + return "irtf" + if self.timeslot.type_id == "plenary": + return "1plenary" + if not self.session.group.parent or not self.session.group.parent.type_id in ["area","irtf"]: + return "" + return self.session.group.parent.acronym + + @property + def break_info(self): + return None + + @property + def area_name(self): + if self.session and self.session.group and self.session.group.acronym == "edu": + return "Training" + elif not self.session or not self.session.group or not self.session.group.parent or not self.session.group.parent.type_id == "area": + return "" + return self.session.group.parent.name + + @property + def isWG(self): + if not self.session or not self.session.group: + return False + if self.session.group.type_id == "wg" and self.session.group.state_id != "bof": + return True + + @property + def group_type_str(self): + if not self.session or not self.session.group: + return "" + if self.session.group and self.session.group.type_id == "wg": + if self.session.group.state_id == "bof": + return "BOF" + else: + return "WG" + + return "" + + @property + def slottype(self): + return "" + + @property + def empty_str(self): + # return JS happy value + if self.session: + return "False" + else: + return "True" + + def json_dict(self, selfurl): + ss = dict() + ss['scheduledsession_id'] = self.id + #ss['href'] = self.url(sitefqdn) + ss['empty'] = self.empty_str + ss['timeslot_id'] = self.timeslot.id + if self.session: + ss['session_id'] = self.session.id + ss['room'] = slugify(self.timeslot.location) + ss['roomtype'] = self.timeslot.type.slug + ss["time"] = date_format(self.timeslot.time, 'Hi') + ss["date"] = time_format(self.timeslot.time, 'Y-m-d') + ss["domid"] = self.timeslot.js_identifier + return ss + +# this object maintains the current state of the placement tool. +# the assignments hash says where the sessions would go. +class CurrentScheduleState: + def __getitem__(self, key): + if key in self.tempdict: + return self.tempdict[key] + return self.current_assignments[key] + + def __iter__(self): + return self.current_assignments.__iter__() + def iterkeys(self): + return self.current_assignments.__iter__() + + def add_to_available_slot(self, fs): + size = len(self.available_slots) + if fs.session is not None: + fs.session.setup_conflicts() + + time_column = None + needs_to_be_added = True + #print "adding fs for slot: %s" % (fs.timeslot) + if fs.timeslot is not None: + if fs.timeslot in self.fs_by_timeslot: + ofs = self.fs_by_timeslot[fs.timeslot] + #print " duplicate timeslot[%s], updating old one: %s" % (ofs.available_slot, fs.timeslot) + if ofs.session is None: + # keep the one with the assignment. + self.fs_by_timeslot[fs.timeslot] = fs + # get rid of old item + fs.available_slot = ofs.available_slot + self.available_slots[ofs.available_slot] = fs + needs_to_be_added = False + else: + self.fs_by_timeslot[fs.timeslot] = fs + + # add the slot to the list of vertical slices. + time_column = self.timeslots[fs.timeslot.time] + group_name = "empty" + if fs.session is not None: + group_name = fs.session.group.acronym + #print " inserting fs %s / %s to slot: %s" % (fs.timeslot.location.name, + # group_name, + # time_column.daytime) + fs.scheduleslot = time_column + if fs.session is None: + self.placed_scheduleslots.append(fs) + else: + time_column = self.unplaced_scheduledslots + fs.scheduleslot = self.unplaced_scheduledslots + + if needs_to_be_added: + self.total_slots = size + self.available_slots.append(fs) + fs.available_slot = size + + if time_column is not None: + # needs available_slot to be filled in + time_column.add_scheduledsession(fs) + #print "adding item: %u to unplaced slots (pinned: %s)" % (fs.available_slot, fs.pinned) + + def __init__(self, schedule, seed=None): + # initialize available_slots with the places that a session can go based upon the + # scheduledsession objects of the provided schedule. + # for each session which is not initially scheduled, also create a scheduledsession + # that has a session, but no timeslot. + + self.recordsteps = True + self.debug_badness = False + self.lastSaveTime = datetime.now() + self.lastSaveStep = 0 + self.verbose = False + + # this maps a *group* to a list of (session,location) pairs, using FakeScheduledSession + self.current_assignments = {} + self.tempdict = {} # used when calculating badness. + + # this contains an entry for each location, and each un-location in the form of + # (session,location) with the appropriate part None. + self.fs_by_timeslot = {} + self.available_slots = [] + self.unplaced_scheduledslots = UnplacedScheduleSlot() + self.placed_scheduleslots = [] + self.sessions = {} + self.total_slots = 0 + + self.schedule = schedule + self.meeting = schedule.meeting + self.seed = seed + self.badness = schedule.badness + self.random_generator=Random() + self.random_generator.seed(seed) + self.temperature = 10000000 + self.stepnum = 1 + self.timeslots = {} + self.slot1 = None + self.slot2 = None + + # setup up array of timeslots objects + for timeslot in schedule.meeting.timeslot_set.filter(type = "session").all(): + if not timeslot.time in self.timeslots: + self.timeslots[timeslot.time] = ScheduleSlot(timeslot.time) + fs = FakeScheduledSession(self.schedule) + fs.timeslot = timeslot + self.add_to_available_slot(fs) + self.timeslots[None] = self.unplaced_scheduledslots + + # make list of things that need placement. + for sess in self.meeting.sessions_that_can_be_placed().all(): + fs = FakeScheduledSession(self.schedule) + fs.session = sess + self.sessions[sess] = fs + self.current_assignments[sess.group] = [] + + #print "Then had %u" % (self.total_slots) + # now find slots that are not empty. + # loop here and the one for useableslots could be merged into one loop + allschedsessions = self.schedule.qs_scheduledsessions_with_assignments.filter(timeslot__type = "session").all() + for ss in allschedsessions: + # do not need to check for ss.session is not none, because filter above only returns those ones. + sess = ss.session + if not (sess in self.sessions): + #print "Had to create sess for %s" % (sess) + self.sessions[sess] = FakeScheduledSession(self.schedule) + fs = self.sessions[sess] + #print "Updating %s from %s(%s)" % (fs.session.group.acronym, ss.timeslot.location.name, ss.timeslot.time) + fs.fromScheduledSession(ss) + + # if pinned, then do not consider it when selecting, but it needs to be in + # current_assignments so that conflicts are calculated. + if not ss.pinned: + self.add_to_available_slot(fs) + else: + del self.sessions[sess] + self.current_assignments[ss.session.group].append(fs) + + # XXX can not deal with a session in two slots yet?! + + # need to remove any sessions that might have gotten through above, but are in non-session + # places, otherwise these could otherwise appear to be unplaced. + allspecialsessions = self.schedule.qs_scheduledsessions_with_assignments.exclude(timeslot__type = "session").all() + for ss in allspecialsessions: + sess = ss.session + if sess is None: + continue + if (sess in self.sessions): + del self.sessions[sess] + + # now need to add entries for those sessions which are currently unscheduled (and yet not pinned) + for sess,fs in self.sessions.iteritems(): + if fs.timeslot is None: + #print "Considering sess: %s, and loc: %s" % (sess, str(fs.timeslot)) + self.add_to_available_slot(fs) + + #import pdb; pdb.set_trace() + # do initial badness calculation for placement that has been done + for daytime,scheduleslot in self.timeslots.iteritems(): + scheduleslot.recalc_badness(self) + + def dump_available_slot_state(self): + for fs in self.available_slots: + shortname="unplaced" + sessid = 0 + if fs.session is not None: + shortname=fs.session.short_name + sessid = fs.session.id + pinned = "unplaced" + ssid=0 + if fs.origss is not None: + pinned = fs.origss.pinned + ssid = fs.origss.id + print "%s: %s[%u] pinned: %s ssid=%u" % (fs.available_slot, shortname, sessid, pinned, ssid) + + def pick_initial_slot(self): + if self.unplaced_scheduledslots.finished(): + self.initial_stage = False + if self.initial_stage: + item = self.unplaced_scheduledslots.get_unplaced_slot_number() + slot1 = self.available_slots[item] + #print "item: %u points to %s" % (item, slot1) + else: + slot1 = self.random_generator.choice(self.available_slots) + return slot1 + + def pick_second_slot(self): + if self.initial_stage and len(self.placed_scheduleslots)>0: + self.random_generator.shuffle(self.placed_scheduleslots) + slot2 = self.placed_scheduleslots[0] + del self.placed_scheduleslots[0] + else: + slot2 = self.random_generator.choice(self.available_slots) + return slot2 + + def pick_two_slots(self): + slot1 = self.pick_initial_slot() + slot2 = self.pick_second_slot() + tries = 100 + self.repicking = 0 + # 1) no point in picking two slots which are the same. + # 2) no point in picking two slots which have no session (already empty) + # 3) no point in picking two slots which are both unscheduled sessions + # 4) limit outselves to ten tries. + while (slot1 == slot2 or slot1 is None or slot2 is None or + (slot1.session is None and slot2.session is None) or + (slot1.timeslot is None and slot2.timeslot is None) + ) and tries > 0: + self.repicking += 1 + #print "%u: .. repicking slots, had: %s and %s" % (self.stepnum, slot1, slot2) + slot1 = self.pick_initial_slot() + slot2 = self.pick_second_slot() + tries -= 1 + if tries == 0: + raise PlacementException("How can it pick the same slot ten times in a row") + + if slot1.pinned: + raise PlacementException("Should never attempt to move pinned slot1") + + if slot2.pinned: + raise PlacementException("Should never attempt to move pinned slot2") + + return slot1, slot2 + + # this assigns a session to a particular slot. + def assign_session(self, session, fslot, doubleup=False): + import copy + if session is None: + # we need to unschedule the session + session = fslot.session + self.tempdict[session.group] = [] + return + + if not session in self.sessions: + raise PlacementException("Is there a legit case where session is not in sessions here?") + + oldfs = self.sessions[session] + # find the group mapping. + pairs = copy.copy(self.current_assignments[session.group]) + #print "pairs is: %s" % (pairs) + if oldfs in pairs: + which = pairs.index(oldfs) + del pairs[which] + #print "new pairs is: %s" % (pairs) + + self.sessions[session] = fslot + # now fix up the other things. + pairs.append(fslot) + self.tempdict[session.group] = pairs + + def commit_tempdict(self): + for key,value in self.tempdict.iteritems(): + self.current_assignments[key] = value + self.tempdict = dict() + + # calculate badness of the columns which have changed + def calc_badness(self, slot1, slot2): + badness = 0 + for daytime,scheduleslot in self.timeslots.iteritems(): + oldbadness = scheduleslot.badness + if oldbadness is None: + oldbadness = 0 + recalc="" + if slot1 is not None and slot1.scheduleslot == scheduleslot: + recalc="recalc slot1" + scheduleslot.recalc_badness(self) + if slot2 is not None and slot2.scheduleslot == scheduleslot: + recalc="recalc slot2" + scheduleslot.recalc_badness(self) + + newbadness = scheduleslot.calc_badness(self) + if self.debug_badness: + print " calc: %s %u %u %s" % (scheduleslot.daytime, oldbadness, newbadness, recalc) + badness += newbadness + return badness + + def try_swap(self): + badness = self.badness + slot1,slot2 = self.pick_two_slots() + if self.debug_badness: + print "start\n slot1: %s.\n slot2: %s.\n badness: %s" % (slot1, slot2,badness) + self.slot1 = slot1 + self.slot2 = slot2 + #import pdb; pdb.set_trace() + #self.assign_session(slot2.session, slot1, False) + #self.assign_session(slot1.session, slot2, False) + # self can substitute for current_assignments thanks to getitem() above. + newbadness = self.calc_badness(slot1, slot2) + if self.debug_badness: + print "end\n slot1: %s.\n slot2: %s.\n badness: %s" % (slot1, slot2, newbadness) + return newbadness + + def log_step(self, accepted_str, change, dice, prob): + acronym1 = "empty" + if self.slot1.session is not None: + acronym1 = self.slot1.session.group.acronym + place1 = "nowhere" + if self.slot1.timeslot is not None: + place1 = str(self.slot1.timeslot.location.name) + + acronym2= "empty" + if self.slot2.session is not None: + acronym2 = self.slot2.session.group.acronym + place2 = "nowhere" + if self.slot2.timeslot is not None: + place2 = str(self.slot2.timeslot.location.name) + initial = " " + if self.initial_stage: + initial = "init" + + # note in logging: the swap has already occured, but the values were set before + if self.verbose: + print "% 5u:%s %s temp=%9u delta=%+9d badness=%10d dice=%.4f <=> prob=%.4f (repicking=%u) %9s:[%8s->%8s], %9s:[%8s->%8s]" % (self.stepnum, initial, + accepted_str, self.temperature, + change, self.badness, dice, prob, + self.repicking, acronym1, place2, place1, acronym2, place1, place2) + + def do_step(self): + self.stepnum += 1 + newbadness = self.try_swap() + if self.badness is None: + self.commit_tempdict + self.badness = newbadness + return True, 0 + + change = newbadness - self.badness + prob = self.calc_probability(change) + dice = self.random_generator.random() + + #self.log_step("consider", change, dice, prob) + + if dice < prob: + accepted_str = "accepted" + accepted = True + # swap things as planned + self.commit_tempdict + + # actually do the swap in the FS + tmp = self.slot1.session + self.slot1.session = self.slot2.session + self.slot2.session = tmp + self.badness = newbadness + # save state object + else: + accepted_str = "rejected" + accepted = False + self.tempdict = dict() + + self.log_step(accepted_str, change, dice, prob) + + if accepted and not self.initial_stage: + self.temperature = self.temperature * 0.9995 + + return accepted, change + + def calc_probability(self, change): + import math + return 1/(1 + math.exp(float(change)/self.temperature)) + + def delete_available_slot(self, number): + # because the numbers matter, we just None things out, and let repicking + # work on things. + #last = len(self.available_slots)-1 + #if number < last: + # self.available_slots[number] = self.available_slots[last] + # self.available_slots[last].available_slot = number + # + #del self.available_slots[last] + self.available_slots[number] = None + + def do_steps(self, limit=None, monitorSchedule=None): + print "do_steps(%s,%s)" % (limit, monitorSchedule) + if self.badness is None or self.badness == 0: + self.badness = self.schedule.calc_badness1(self) + self.oldbadness = self.badness + while (limit is None or self.stepnum < limit) and self.temperature > 1000: + accepted,change = self.do_step() + #set_prompt_wait(True) + if not accepted and self.initial_stage: + # randomize again! + self.unplaced_scheduledslots.shuffle(self.random_generator) + + if accepted and self.initial_stage and self.unplaced_scheduledslots.count>0: + # delete it from available slots, so as not to leave unplaced slots + self.delete_available_slot(self.slot1.available_slot) + # remove initial slot from list. + self.unplaced_scheduledslots.delete_first() + + if False and accepted and self.recordsteps: + ass1 = AutomaticScheduleStep() + ass1.schedule = self.schedule + if self.slot1.session is not None: + ass1.session = self.slot1.session + if self.slot1.origss is not None: + ass1.moved_to = self.slot1.origss + ass1.stepnum = self.stepnum + ass1.save() + ass2 = AutomaticScheduleStep() + ass2.schedule = self.schedule + if self.slot2.session is not None: + ass2.session = self.slot2.session + if self.slot2.origss is not None: + ass2.moved_to = self.slot2.origss + ass2.stepnum = self.stepnum + ass2.save() + #print "%u: accepted: %s change %d temp: %d" % (self.stepnum, accepted, change, self.temperature) + if (self.stepnum % 1000) == 0 and monitorSchedule is not None: + self.saveToSchedule(monitorSchedule) + print "Finished after %u steps, badness = %u->%u" % (self.stepnum, self.oldbadness, self.badness) + + def saveToSchedule(self, targetSchedule): + when = datetime.now() + since = 0 + rate = 0 + if targetSchedule is None: + targetSchedule = self.schedule + else: + # XXX more stuff to do here, setup mapping, copy pinned items + pass + + if self.lastSaveTime is not None: + since = when - self.lastSaveTime + if since.microseconds > 0: + rate = 1000 * float(self.stepnum - self.lastSaveStep) / (1000*since.seconds + since.microseconds / 1000) + print "%u: saved to schedule: %s %s elapsed=%s rate=%.2f" % (self.stepnum, targetSchedule.name, when, since, rate) + self.lastSaveTime = datetime.now() + self.lastSaveStep = self.stepnum + + # first, remove all assignments in the schedule. + for ss in targetSchedule.scheduledsession_set.all(): + if ss.pinned: + continue + ss.delete() + + # then, add new items for new placements. + for fs in self.available_slots: + if fs is None: + continue + ss = ScheduledSession(timeslot = fs.timeslot, + schedule = targetSchedule, + session = fs.session) + ss.save() + + def do_placement(self, limit=None, targetSchedule=None): + self.badness = self.schedule.calc_badness1(self) + if limit is None: + limitstr = "unlimited " + else: + limitstr = "%u" % (limit) + print "Initial stage (limit=%s) starting with: %u items to place" % (limitstr, self.unplaced_scheduledslots.count) + + # permute the unplaced sessions + self.unplaced_scheduledslots.shuffle(self.random_generator) + + self.initial_stage = True + monitorSchedule = targetSchedule + if monitorSchedule is None: + monitorSchedule = self.schedule + self.do_steps(limit, monitorSchedule) + self.saveToSchedule(targetSchedule) + +# +# this does not clearly have value at this point. +# Not worth a migration/table yet. +# +if False: + class AutomaticScheduleStep(models.Model): + schedule = models.ForeignKey('Schedule', null=False, blank=False, help_text=u"Who made this agenda") + session = models.ForeignKey('Session', null=True, default=None, help_text=u"Scheduled session involved") + moved_from = models.ForeignKey('ScheduledSession', related_name="+", null=True, default=None, help_text=u"Where session was") + moved_to = models.ForeignKey('ScheduledSession', related_name="+", null=True, default=None, help_text=u"Where session went") + stepnum = models.IntegerField(default=0, blank=True, null=True) + diff --git a/ietf/meeting/test_data.py b/ietf/meeting/test_data.py index ff92707e7..85a204581 100644 --- a/ietf/meeting/test_data.py +++ b/ietf/meeting/test_data.py @@ -8,9 +8,11 @@ from ietf.group.models import Group def make_meeting_test_data(): - make_test_data() + if not Group.objects.filter(acronym='mars'): + make_test_data() system_person = Person.objects.get(name="(System)") plainman = Person.objects.get(user__username="plain") + secretary = Person.objects.get(user__username="secretary") meeting = Meeting.objects.get(number="42", type="ietf") schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="test-agenda", visible=True, public=True) diff --git a/ietf/meeting/tests_api.py b/ietf/meeting/tests_api.py index 01699928f..831db8da1 100644 --- a/ietf/meeting/tests_api.py +++ b/ietf/meeting/tests_api.py @@ -20,52 +20,67 @@ class ApiTests(TestCase): r = self.client.get("/dajaxice/dajaxice.core.js") self.assertEqual(r.status_code, 200) - def test_update_agenda_item(self): + def test_update_agenda(self): meeting = make_meeting_test_data() - session = Session.objects.filter(meeting=meeting, group__acronym="mars").first() - mars_scheduled = ScheduledSession.objects.get(session=session) + schedule = Schedule.objects.get(meeting__number=42,name="test-agenda") + mars_session = Session.objects.filter(meeting=meeting, group__acronym="mars").first() + ames_session = Session.objects.filter(meeting=meeting, group__acronym="ames").first() + + mars_scheduled = ScheduledSession.objects.get(session=mars_session) mars_slot = mars_scheduled.timeslot - ames_scheduled = ScheduledSession.objects.get(session__meeting=meeting, session__group__acronym="ames") + ames_scheduled = ScheduledSession.objects.get(session=ames_session) ames_slot = ames_scheduled.timeslot - def do_post(to): - # move this session from one timeslot to another - return self.client.post('/dajaxice/ietf.meeting.update_timeslot/', { - 'argv': json.dumps({ - "schedule_id": mars_scheduled.schedule.pk, - "session_id": session.pk, - "scheduledsession_id": to.pk if to else None, - })}) + def do_unschedule(scheduledsession): + url = urlreverse("ietf.meeting.ajax.scheduledsession_json", + kwargs=dict(num=scheduledsession.session.meeting.number, + name=scheduledsession.schedule.name, + scheduledsession_id=scheduledsession.pk,)) + return self.client.delete(url) - # faulty post - not logged in - r = do_post(to=ames_scheduled) - self.assertEqual(r.status_code, 200) - self.assertTrue("error" in json.loads(r.content)) - self.assertEqual(ScheduledSession.objects.get(pk=mars_scheduled.pk).session, session) + def do_schedule(schedule,session,timeslot): + url = urlreverse("ietf.meeting.ajax.scheduledsessions_json", + kwargs=dict(num=session.meeting.number, + name=schedule.name,)) + post_data = '{ "session_id": "%s", "timeslot_id": "%s" }'%(session.pk,timeslot.pk) + return self.client.post(url,post_data,content_type='application/x-www-form-urlencoded') - # faulty post - logged in as non-owner + # not logged in + # faulty delete + r = do_unschedule(mars_scheduled) + self.assertEqual(r.status_code, 403) + self.assertEqual(ScheduledSession.objects.get(pk=mars_scheduled.pk).session, mars_session) + # faulty post + r = do_schedule(schedule,ames_session,mars_slot) + self.assertEqual(r.status_code, 403) + + # logged in as non-owner + # faulty delete self.client.login(remote_user="ad") - r = do_post(to=ames_scheduled) - self.assertEqual(r.status_code, 200) + r = do_unschedule(mars_scheduled) + self.assertEqual(r.status_code, 403) self.assertTrue("error" in json.loads(r.content)) + # faulty post + r = do_schedule(schedule,ames_session,mars_slot) + self.assertEqual(r.status_code, 403) - # move to ames + # Put ames in the same timeslot as mars self.client.login(remote_user="plain") - r = do_post(to=ames_scheduled) + r = do_unschedule(ames_scheduled) self.assertEqual(r.status_code, 200) self.assertTrue("error" not in json.loads(r.content)) - self.assertEqual(ScheduledSession.objects.get(pk=mars_scheduled.pk).session, None) - self.assertEqual(ScheduledSession.objects.get(pk=ames_scheduled.pk).session, session) + r = do_schedule(schedule,ames_session,mars_slot) + self.assertEqual(r.status_code, 201) - # unschedule - self.client.login(remote_user="plain") - r = do_post(to=None) + # Unschedule mars + r = do_unschedule(mars_scheduled) self.assertEqual(r.status_code, 200) self.assertTrue("error" not in json.loads(r.content)) - self.assertEqual(ScheduledSession.objects.get(pk=ames_scheduled.pk).session, None) + self.assertEqual(ScheduledSession.objects.filter(session=mars_session).count(), 0) + self.assertEqual(ScheduledSession.objects.get(session=ames_session).timeslot, mars_slot) def test_constraints_json(self): @@ -106,7 +121,7 @@ class ApiTests(TestCase): timeslots_before = meeting.timeslot_set.count() url = urlreverse("ietf.meeting.ajax.timeslot_roomsurl", kwargs=dict(num=meeting.number)) - post_data = { "name": "new room", "capacity": "50" } + post_data = { "name": "new room", "capacity": "50" , "resources": []} # unauthorized post r = self.client.post(url, post_data) @@ -116,6 +131,7 @@ class ApiTests(TestCase): # create room self.client.login(remote_user="secretary") r = self.client.post(url, post_data) + self.assertEqual(r.status_code, 302) self.assertTrue(meeting.room_set.filter(name="new room")) timeslots_after = meeting.timeslot_set.count() @@ -213,7 +229,7 @@ class ApiTests(TestCase): meeting = make_meeting_test_data() url = urlreverse("ietf.meeting.ajax.agenda_infourl", - kwargs=dict(num=meeting.number, schedule_name=meeting.agenda.name)) + kwargs=dict(num=meeting.number, name=meeting.agenda.name)) r = self.client.get(url) info = json.loads(r.content) @@ -247,7 +263,7 @@ class ApiTests(TestCase): url = urlreverse("ietf.meeting.ajax.agenda_infourl", kwargs=dict(num=meeting.number, - schedule_name=meeting.agenda.name)) + name=meeting.agenda.name)) post_data = { 'visible': 'false', @@ -259,8 +275,14 @@ class ApiTests(TestCase): r = self.client.post(url, post_data) self.assertEqual(r.status_code, 403) + # TODO - permission protection on this function are not right + # Normal users are prevented from changing public/private on their own schedule + # The secretariat can't change normal user's agendas settings for them, and the + # page at /meeting//schedule//details behaves badly for the secretariat + # (pushing save seems to do nothing as the POST 401s in the background) + # change agenda - self.client.login(remote_user="ad") + self.client.login(remote_user="secretary") r = self.client.post(url, post_data) self.assertEqual(r.status_code, 302) changed_schedule = Schedule.objects.get(pk=meeting.agenda.pk) @@ -272,7 +294,7 @@ class ApiTests(TestCase): url = urlreverse("ietf.meeting.ajax.agenda_infourl", kwargs=dict(num=meeting.number, - schedule_name=meeting.agenda.name)) + name=meeting.agenda.name)) # unauthorized delete self.client.login(remote_user="plain") r = self.client.delete(url) diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index 6b78c4ae2..6f7948ddc 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -159,7 +159,7 @@ class EditTests(TestCase): self.client.login(remote_user="secretary") r = self.client.get(urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number))) self.assertEqual(r.status_code, 200) - self.assertTrue("session_obj" in r.content) + self.assertTrue("load_scheduledsessions" in r.content) def test_save_agenda_as_and_read_permissions(self): meeting = make_meeting_test_data() @@ -174,7 +174,7 @@ class EditTests(TestCase): self.assertEqual(r.status_code, 302) # get - url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, schedule_name="foo")) + url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, name="foo")) r = self.client.get(url) self.assertEqual(r.status_code, 200) diff --git a/ietf/meeting/urls.py b/ietf/meeting/urls.py index c3743bdd5..6152ab8b8 100644 --- a/ietf/meeting/urls.py +++ b/ietf/meeting/urls.py @@ -20,11 +20,13 @@ urlpatterns = patterns('', (r'^agenda/week-view.html$', views.week_view), (r'^week-view.html$', views.week_view), (r'^(?P\d+)/schedule/edit$', views.edit_agenda), - (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/edit$', views.edit_agenda), - (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/details$', views.edit_agenda_properties), + (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/edit$', views.edit_agenda), + (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/details$', views.edit_agenda_properties), (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)(?P.html)?/?$', views.agenda), (r'^(?P\d+)/agenda(?P.html)?/?$', views.agenda), (r'^(?P\d+)/(?Pagenda-utc)(?P.html)?/?$', views.agenda), + (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/sessions.json$', ajax.scheduledsessions_json), + (r'^(?P\d+)/schedule/(?P[A-Za-z0-9-:_]+)/session/(?P\d+).json$', ajax.scheduledsession_json), (r'^(?P\d+)/requests.html$', RedirectView.as_view(url='/meeting/%(num)s/requests', permanent=True)), (r'^(?P\d+)/requests$', views.meeting_requests), (r'^(?P\d+)/agenda(?P.txt)$', views.agenda), @@ -34,10 +36,11 @@ urlpatterns = patterns('', (r'^(?P\d+)/timeslots/edit$', views.edit_timeslots), (r'^(?P\d+)/rooms$', ajax.timeslot_roomsurl), (r'^(?P\d+)/room/(?P\d+).json$', ajax.timeslot_roomurl), + (r'^(?P\d+)/room/(?P\d+).html$', views.edit_roomurl), (r'^(?P\d+)/timeslots$', ajax.timeslot_slotsurl), (r'^(?P\d+)/timeslots.json$', ajax.timeslot_slotsurl), (r'^(?P\d+)/timeslot/(?P\d+).json$', ajax.timeslot_sloturl), - (r'^(?P\d+)/agendas/(?P[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl), + (r'^(?P\d+)/agendas/(?P[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl), (r'^(?P\d+)/agendas$', ajax.agenda_infosurl), (r'^(?P\d+)/agendas.json$', ajax.agenda_infosurl), (r'^(?P\d+)/week-view.html$', views.week_view), @@ -45,6 +48,7 @@ urlpatterns = patterns('', (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-]+)-drafts.pdf$', views.session_draft_pdf), (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-]+)-drafts.tgz$', views.session_draft_tarfile), (r'^(?P\d+)/agenda/(?P[A-Za-z0-9-]+)/?$', views.session_agenda), + (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+)/constraint/(?P\d+).json', ajax.constraint_json), diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index f2bd733d1..44a0a0d30 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -23,12 +23,13 @@ from django.middleware.gzip import GZipMiddleware from django.db.models import Max from django.forms.models import modelform_factory from django.views.decorators.csrf import ensure_csrf_cookie +from django.forms import ModelForm 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.meeting.models import Meeting, TimeSlot, Session, Schedule, Room from ietf.group.models import Group from ietf.meeting.helpers import get_areas @@ -97,13 +98,13 @@ class SaveAsForm(forms.Form): savename = forms.CharField(max_length=100) @role_required('Area Director','Secretariat') -def agenda_create(request, num=None, schedule_name=None): +def agenda_create(request, num=None, name=None): meeting = get_meeting(num) - schedule = get_schedule(meeting, schedule_name) + schedule = get_schedule(meeting, 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)) + raise Http404("No meeting information for meeting %s schedule %s available" % (num,name)) # authorization was enforced by the @group_require decorator above. @@ -160,6 +161,7 @@ def agenda_create(request, num=None, schedule_name=None): return redirect(edit_agenda, meeting.number, newschedule.name) +@role_required('Secretariat') @decorator_from_middleware(GZipMiddleware) @ensure_csrf_cookie def edit_timeslots(request, num=None): @@ -174,8 +176,8 @@ def edit_timeslots(request, num=None): rooms = meeting.room_set.order_by("capacity") + # this import locate here to break cyclic loop. 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]) @@ -194,15 +196,47 @@ def edit_timeslots(request, num=None): "meeting":meeting}, RequestContext(request)), content_type="text/html") +class RoomForm(ModelForm): + class Meta: + model = Room + exclude = ('meeting',) + +@role_required('Secretariat') +def edit_roomurl(request, num, roomid): + meeting = get_meeting(num) + + try: + room = meeting.room_set.get(pk=roomid) + except Room.DoesNotExist: + raise Http404("No room %u for meeting %s" % (roomid, meeting.name)) + + if request.POST: + roomform = RoomForm(request.POST, instance=room) + new_room = roomform.save(commit=False) + new_room.meeting = meeting + new_room.save() + roomform.save_m2m() + return HttpResponseRedirect( reverse(edit_timeslots, args=[meeting.number]) ) + + roomform = RoomForm(instance=room) + meeting_base_url = request.build_absolute_uri(meeting.base_url()) + site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash + return HttpResponse(render_to_string("meeting/room_edit.html", + {"meeting_base_url": meeting_base_url, + "site_base_url": site_base_url, + "editroom": roomform, + "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) @ensure_csrf_cookie -def edit_agenda(request, num=None, schedule_name=None): +def edit_agenda(request, num=None, name=None): if request.method == 'POST': - return agenda_create(request, num, schedule_name) + return agenda_create(request, num, name) user = request.user requestor = "AnonymousUser" @@ -215,8 +249,8 @@ def edit_agenda(request, num=None, schedule_name=None): 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("requestor: %s for sched_name: %s \n" % ( requestor, name )) + schedule = get_schedule(meeting, 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()) @@ -239,14 +273,8 @@ def edit_agenda(request, num=None, schedule_name=None): "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) @@ -275,7 +303,6 @@ def edit_agenda(request, num=None, schedule_name=None): "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") @@ -289,10 +316,10 @@ AgendaPropertiesForm = modelform_factory(Schedule, fields=('name','visible', 'pu @role_required('Area Director','Secretariat') @decorator_from_middleware(GZipMiddleware) @ensure_csrf_cookie -def edit_agenda_properties(request, num=None, schedule_name=None): +def edit_agenda_properties(request, num=None, name=None): meeting = get_meeting(num) - schedule = get_schedule(meeting, schedule_name) + schedule = get_schedule(meeting, name) form = AgendaPropertiesForm(instance=schedule) return HttpResponse(render_to_string("meeting/properties_edit.html", @@ -311,7 +338,7 @@ def edit_agenda_properties(request, num=None, schedule_name=None): def edit_agendas(request, num=None, order=None): #if request.method == 'POST': - # return agenda_create(request, num, schedule_name) + # return agenda_create(request, num, name) meeting = get_meeting(num) user = request.user @@ -546,7 +573,7 @@ def ical_agenda(request, num=None, name=None, ext=None): # Process the special flags. # "-wgname" will remove a working group from the output. - # "~Type" will add that type to 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) @@ -568,7 +595,7 @@ def ical_agenda(request, num=None, name=None, ext=None): 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__acronym__in = exclude) | #Q(session__group__parent__acronym__in = exclude)) return HttpResponse(render_to_string("meeting/agenda.ics", diff --git a/ietf/name/migrations/0020_add_default_penalties.py b/ietf/name/migrations/0020_add_default_penalties.py new file mode 100644 index 000000000..53ac9c0d8 --- /dev/null +++ b/ietf/name/migrations/0020_add_default_penalties.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from settings import BADNESS_TOOBIG, BADNESS_MUCHTOOBIG +from settings import BADNESS_CONFLICT_1, BADNESS_CONFLICT_2, BADNESS_CONFLICT_3, BADNESS_BETHERE + +class Migration(DataMigration): + + def forwards(self, orm): + bethere = orm.ConstraintName.objects.get(slug="bethere") + if bethere.penalty is None or bethere.penalty == 0: + bethere.penalty = BADNESS_BETHERE + bethere.save() + + # + conflict = orm.ConstraintName.objects.get(slug="conflict") + if conflict.penalty is None or conflict.penalty == 0: + conflict.penalty = BADNESS_CONFLICT_1 + conflict.save() + + # + conflic2 = orm.ConstraintName.objects.get(slug="conflic2") + if conflic2.penalty is None or conflic2.penalty == 0: + conflic2.penalty = BADNESS_CONFLICT_2 + conflic2.save() + + # + conflic3 = orm.ConstraintName.objects.get(slug="conflic3") + if conflic3.penalty is None or conflic3.penalty == 0: + conflic3.penalty = BADNESS_CONFLICT_3 + conflic3.save() + + def backwards(self, orm): + pass + + models = { + 'name.ballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'}, + 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.dbtemplatetypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docremindertypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.feedbacktype': { + 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupmilestonestatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.liaisonstatementpurposename': { + 'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.nomineepositionstate': { + 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.rolename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + } + } + + complete_apps = ['name'] + symmetrical = True diff --git a/ietf/name/migrations/0021_add_room.py b/ietf/name/migrations/0021_add_room.py new file mode 100644 index 000000000..f7189b53f --- /dev/null +++ b/ietf/name/migrations/0021_add_room.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'RoomResourceName' + db.create_table('name_roomresourcename', ( + ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('desc', self.gf('django.db.models.fields.TextField')(blank=True)), + ('used', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('order', self.gf('django.db.models.fields.IntegerField')(default=0)), + )) + db.send_create_signal('name', ['RoomResourceName']) + + def backwards(self, orm): + # Deleting model 'RoomResourceName' + db.delete_table('name_roomresourcename') + + + models = { + 'name.ballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'}, + 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.dbtemplatetypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docremindertypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.feedbacktype': { + 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupmilestonestatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.liaisonstatementpurposename': { + 'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.nomineepositionstate': { + 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.rolename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.roomresourcename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + } + } + + complete_apps = ['name'] diff --git a/ietf/name/migrations/0022_add_room_resources.py b/ietf/name/migrations/0022_add_room_resources.py new file mode 100644 index 000000000..d4db287d3 --- /dev/null +++ b/ietf/name/migrations/0022_add_room_resources.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + RoomResourceName = orm['name.RoomResourceName'] + RoomResourceName(slug='project', name='LCD projector', + desc='The room will have a computer projector', + used=True).save() + RoomResourceName(slug='proj2', name='second LCD projector', + desc='The room will have a second computer projector', + used=True).save() + RoomResourceName(slug='meetecho', name='Meetecho Remote Partition Support', + desc='The room will have a meetecho wrangler', + used=True).save() + + + def backwards(self, orm): + pass + + + models = { + 'name.ballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'}, + 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.dbtemplatetypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docremindertypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.feedbacktype': { + 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupmilestonestatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.liaisonstatementpurposename': { + 'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.nomineepositionstate': { + 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.rolename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.roomresourcename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + } + } + + complete_apps = ['name'] diff --git a/ietf/name/models.py b/ietf/name/models.py index 7403256d1..b18e9415b 100644 --- a/ietf/name/models.py +++ b/ietf/name/models.py @@ -31,7 +31,7 @@ class DocRelationshipName(NameModel): """Updates, Replaces, Obsoletes, Reviews, ... The relationship is always recorded in one direction.""" revname = models.CharField(max_length=255) - + class DocTypeName(NameModel): """Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email, Review, Issue, Wiki""" @@ -71,3 +71,5 @@ class DraftSubmissionStateName(NameModel): Previous Version Authors, Awaiting Initial Version Approval, Awaiting Manual Post, Cancelled, Posted""" next_states = models.ManyToManyField('DraftSubmissionStateName', related_name="previous_states", blank=True) +class RoomResourceName(NameModel): + "Room resources: Audio Stream, Meetecho, . . ." diff --git a/ietf/secr/meetings/views.py b/ietf/secr/meetings/views.py index 893a56999..eee4326f4 100644 --- a/ietf/secr/meetings/views.py +++ b/ietf/secr/meetings/views.py @@ -163,11 +163,16 @@ def send_notification(request, sessions): Room Name: {6} --------------------------------------------- ''' + + requestinguser = None + if request is not None: + requestinguser = request.user.person + group = sessions[0].group to_email = sessions[0].requested_by.role_email('chair').address - cc_list = get_cc_list(group, request.user.person) + cc_list = get_cc_list(group, requestinguser) from_email = ('"IETF Secretariat"','agenda@ietf.org') - if sessions.count() == 1: + if len(sessions) == 1: subject = '%s - Requested session has been scheduled for IETF %s' % (group.acronym, sessions[0].meeting.number) else: subject = '%s - Requested sessions have been scheduled for IETF %s' % (group.acronym, sessions[0].meeting.number) @@ -193,6 +198,8 @@ def send_notification(request, sessions): context['agenda_note'] = sessions[0].agenda_note context['session'] = get_initial_session(sessions) context['session_info'] = session_info + context['group'] = group + context['login'] = sessions[0].requested_by send_mail(request, to_email, diff --git a/ietf/secr/sreq/forms.py b/ietf/secr/sreq/forms.py index 95c4231d5..20c90aad2 100644 --- a/ietf/secr/sreq/forms.py +++ b/ietf/secr/sreq/forms.py @@ -1,6 +1,9 @@ from django import forms from ietf.group.models import Group +from ietf.meeting.models import ResourceAssociation +from django.forms.formsets import formset_factory +from ietf.person.forms import EmailsField import os # ------------------------------------------------- @@ -44,12 +47,25 @@ def join_conflicts(data): class GroupSelectForm(forms.Form): group = forms.ChoiceField() - + def __init__(self,*args,**kwargs): choices = kwargs.pop('choices') super(GroupSelectForm, self).__init__(*args,**kwargs) self.fields['group'].widget.choices = choices +BETHERE_CHOICES = ((False , 'No'), (True , 'Yes')) +# not using the ModelFormset, too complex. +class MustBePresentForm(forms.Form): + from ietf.person.models import Person + + #person = forms.ModelChoiceField(queryset= Person.objects.all(), required=False) + person = EmailsField(required=False) + bethere = forms.ChoiceField(required = False, choices = BETHERE_CHOICES) + # something like this is desired to make pre-existing items read-only + #self.fields['person'].widget.attrs['readonly'] = True + + +MustBePresentFormSet = formset_factory(MustBePresentForm, extra = 1) class SessionForm(forms.Form): num_session = forms.ChoiceField(choices=NUM_SESSION_CHOICES) @@ -65,6 +81,7 @@ class SessionForm(forms.Form): wg_selector2 = forms.ChoiceField(choices=WG_CHOICES,required=False) wg_selector3 = forms.ChoiceField(choices=WG_CHOICES,required=False) third_session = forms.BooleanField(required=False) + resources = forms.MultipleChoiceField(choices=[(x.pk,x.desc) for x in ResourceAssociation.objects.all()], widget=forms.CheckboxSelectMultiple) def __init__(self, *args, **kwargs): super(SessionForm, self).__init__(*args, **kwargs) @@ -85,7 +102,11 @@ class SessionForm(forms.Form): if self.initial and 'length_session3' in self.initial: if self.initial['length_session3'] != '0' and self.initial['length_session3'] != None: self.fields['third_session'].initial = True - + + resources = self.initial['resources'] + resource_choices = [r.pk for r in resources] + self.initial['resources'] = resource_choices + def clean_conflict1(self): conflict = self.cleaned_data['conflict1'] check_conflict(conflict) diff --git a/ietf/secr/sreq/tests.py b/ietf/secr/sreq/tests.py index 798bf03a7..bb7617a75 100644 --- a/ietf/secr/sreq/tests.py +++ b/ietf/secr/sreq/tests.py @@ -5,7 +5,8 @@ from django.core.urlresolvers import reverse from ietf.utils import TestCase from ietf.group.models import Group from ietf.ietfauth.utils import has_role -from ietf.utils.test_data import make_test_data +#from ietf.utils.test_data import make_test_data +from ietf.meeting.test_data import make_meeting_test_data as make_test_data #from pyquery import PyQuery @@ -13,7 +14,7 @@ SECR_USER='secretary' class SreqUrlTests(TestCase): def test_urls(self): - draft = make_test_data() + make_test_data() r = self.client.get("/secr/") self.assertEqual(r.status_code, 200) @@ -21,23 +22,24 @@ class SreqUrlTests(TestCase): r = self.client.get("/secr/sreq/") self.assertEqual(r.status_code, 200) - r = self.client.get("/secr/sreq/%s/new/" % draft.group.acronym) + testgroup=Group.objects.filter(type_id='wg').first() + r = self.client.get("/secr/sreq/%s/new/" % testgroup.acronym) self.assertEqual(r.status_code, 200) class MainTestCase(TestCase): def test_main(self): - draft = make_test_data() + make_test_data() url = reverse('sessions') r = self.client.get(url, REMOTE_USER=SECR_USER) self.assertEqual(r.status_code, 200) sched = r.context['scheduled_groups'] unsched = r.context['unscheduled_groups'] - self.failUnless(len(sched) == 0) - self.failUnless(len(unsched) > 0) + self.failUnless(len(unsched) == 0) + self.failUnless(len(sched) > 0) class SubmitRequestCase(TestCase): def test_submit_request(self): - draft = make_test_data() + make_test_data() acronym = Group.objects.all()[0].acronym url = reverse('sessions_new',kwargs={'acronym':acronym}) post_data = {'id_num_session':'1', diff --git a/ietf/secr/sreq/urls.py b/ietf/secr/sreq/urls.py index 88fe5c1f2..32bd3a719 100644 --- a/ietf/secr/sreq/urls.py +++ b/ietf/secr/sreq/urls.py @@ -4,10 +4,12 @@ urlpatterns = patterns('ietf.secr.sreq.views', url(r'^$', 'main', name='sessions'), url(r'^status/$', 'tool_status', name='sessions_tool_status'), url(r'^(?P[A-Za-z0-9_\-\+]+)/$', 'view', name='sessions_view'), + url(r'^(?P[A-Za-z0-9_\-\+]+)/(?P[A-Za-z0-9_\-\+]+)/$', 'view', name='sessions_view'), url(r'^(?P[A-Za-z0-9_\-\+]+)/approve/$', 'approve', name='sessions_approve'), url(r'^(?P[A-Za-z0-9_\-\+]+)/cancel/$', 'cancel', name='sessions_cancel'), url(r'^(?P[A-Za-z0-9_\-\+]+)/confirm/$', 'confirm', name='sessions_confirm'), url(r'^(?P[A-Za-z0-9_\-\+]+)/edit/$', 'edit', name='sessions_edit'), url(r'^(?P[A-Za-z0-9_\-\+]+)/new/$', 'new', name='sessions_new'), url(r'^(?P[A-Za-z0-9_\-\+]+)/no_session/$', 'no_session', name='sessions_no_session'), + url(r'^(?P[A-Za-z0-9_\-\+]+)/(?P[A-Za-z0-9_\-\+]+)/edit/$', 'edit_mtg', name='sessions_edit'), ) diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py index c70def36a..415df6191 100644 --- a/ietf/secr/sreq/views.py +++ b/ietf/secr/sreq/views.py @@ -14,6 +14,7 @@ from ietf.secr.utils.group import get_my_groups, groups_by_session from ietf.ietfauth.utils import has_role from ietf.utils.mail import send_mail from ietf.meeting.models import Meeting, Session, Constraint +from ietf.meeting.helpers import get_meeting from ietf.group.models import Group, Role from ietf.name.models import SessionStatusName, ConstraintName @@ -44,10 +45,13 @@ def get_initial_session(sessions): This function takes a queryset of sessions ordered by 'id' for consistency. It returns a dictionary to be used as the initial for a legacy session form ''' + initial = {} + if(len(sessions) == 0): + return initial + meeting = sessions[0].meeting group = sessions[0].group conflicts = group.constraint_source_set.filter(meeting=meeting) - initial = {} # even if there are three sessions requested, the old form has 2 in this field initial['num_session'] = sessions.count() if sessions.count() <= 2 else 2 @@ -64,6 +68,7 @@ def get_initial_session(sessions): initial['conflict2'] = ' '.join([ c.target.acronym for c in conflicts.filter(name__slug='conflic2') ]) initial['conflict3'] = ' '.join([ c.target.acronym for c in conflicts.filter(name__slug='conflic3') ]) initial['comments'] = sessions[0].comments + initial['resources'] = sessions[0].resources.all() return initial def get_lock_message(): @@ -78,12 +83,6 @@ def get_lock_message(): message = "This application is currently locked." return message -def get_meeting(): - ''' - Function to get the current IETF regular meeting. Simply returns the meeting with the most recent date - ''' - return Meeting.objects.filter(type='ietf').order_by('-date')[0] - def get_requester_text(person,group): ''' This function takes a Person object and a Group object and returns the text to use in the @@ -181,7 +180,7 @@ def approve(request, acronym): if has_role(request.user,'Secretariat') or group.parent.role_set.filter(name='ad',person=request.user.person): session.status = SessionStatusName.objects.get(slug='appr') - session.save() + session_save(session) messages.success(request, 'Third session approved') return redirect('sessions_view', acronym=acronym) @@ -212,7 +211,7 @@ def cancel(request, acronym): # mark sessions as deleted for session in sessions: session.status_id = 'deleted' - session.save() + session_save(session) # clear schedule assignments if already scheduled session.scheduledsession_set.all().delete() @@ -271,7 +270,7 @@ def confirm(request, acronym): requested_duration=datetime.timedelta(0,int(duration)), comments=form['comments'], status=SessionStatusName.objects.get(slug=slug)) - new_session.save() + session_save(new_session) # write constraint records save_conflicts(group,meeting,form['conflict1'],'conflict') @@ -302,12 +301,61 @@ def confirm(request, acronym): RequestContext(request, {}), ) +def make_essential_person(pk, person, required): + essential_person = dict() + essential_person["person"] = person.email_set.all()[0].pk + essential_person["bethere"] = required + return essential_person + +def make_bepresent_formset(group, session, default=True): + dict_of_essential_people = {} + + for x in session.people_constraints.all(): + #print "add db: %u %s" % (x.person.pk, x.person) + dict_of_essential_people[x.person.pk] = make_essential_person(x.person.pk, x.person, True) + + # now, add the co-chairs if they were not already present + chairs = group.role_set.filter(name='chair') + for chairrole in chairs: + chair = chairrole.person + if not chair.pk in dict_of_essential_people: + #print "add chair: %u" % (chair.pk) + dict_of_essential_people[chair.pk] = make_essential_person(chair.pk, chair, default) + + # add the responsible AD + if not group.ad.pk in dict_of_essential_people: + #print "add ad: %u" % (chair.pk) + dict_of_essential_people[group.ad.pk] = make_essential_person(group.ad.pk, group.ad, default) + + # make the form set of these people + list_of_essential_people = [] + for k,x in dict_of_essential_people.iteritems(): + #print "k: %s x: %s" % (k,x) + list_of_essential_people.append(x) + + list_of_essential_people.reverse() + #for t in list_of_essential_people: + # print "t: %s" % (t) + + formset = MustBePresentFormSet(initial=list_of_essential_people) + return formset + @check_permissions def edit(request, acronym): + return edit_mtg(request, None, acronym) + +def session_save(session): + session.save() + if session.status_id == "schedw" and session.meeting.agenda != None: + # send an email to iesg-secretariat to alert to change + pass + +@check_permissions +def edit_mtg(request, num, acronym): ''' This view allows the user to edit details of the session request ''' - meeting = get_meeting() + meeting = get_meeting(num) group = get_object_or_404(Group, acronym=acronym) sessions = Session.objects.filter(meeting=meeting,group=group).order_by('id') sessions_count = sessions.count() @@ -315,13 +363,18 @@ def edit(request, acronym): session_conflicts = session_conflicts_as_string(group, meeting) login = request.user.person + session = Session() + if(len(sessions) > 0): + session = sessions[0] + if request.method == 'POST': button_text = request.POST.get('submit', '') if button_text == 'Cancel': return redirect('sessions_view', acronym=acronym) form = SessionForm(request.POST,initial=initial) - if form.is_valid(): + bepresent_formset = MustBePresentFormSet(request.POST) + if form.is_valid() or bepresent_formset.is_valid(): if form.has_changed(): # might be cleaner to simply delete and rewrite all records (but maintain submitter?) # adjust duration or add sessions @@ -329,7 +382,7 @@ def edit(request, acronym): if 'length_session1' in form.changed_data: session = sessions[0] session.requested_duration = datetime.timedelta(0,int(form.cleaned_data['length_session1'])) - session.save() + session_save(session) # session 2 if 'length_session2' in form.changed_data: @@ -351,7 +404,7 @@ def edit(request, acronym): duration = datetime.timedelta(0,int(form.cleaned_data['length_session2'])) session = sessions[1] session.requested_duration = duration - session.save() + session_save(session) # session 3 if 'length_session3' in form.changed_data: @@ -373,7 +426,7 @@ def edit(request, acronym): duration = datetime.timedelta(0,int(form.cleaned_data['length_session3'])) session = sessions[2] session.requested_duration = duration - session.save() + session_save(session) if 'attendees' in form.changed_data: @@ -390,6 +443,12 @@ def edit(request, acronym): Constraint.objects.filter(meeting=meeting,source=group,name='conflic3').delete() save_conflicts(group,meeting,form.cleaned_data['conflict3'],'conflic3') + if 'resources' in form.changed_data: + new_resource_ids = form.cleaned_data['resources'] + new_resources = [ ResourceAssociation.objects.get(pk=a) + for a in new_resource_ids] + session.resources = new_resources + # deprecated # log activity #add_session_activity(group,'Session Request was updated',meeting,user) @@ -397,16 +456,46 @@ def edit(request, acronym): # send notification send_notification(group,meeting,login,form.cleaned_data,'update') + for bepresent in bepresent_formset.forms: + if bepresent.is_valid() and 'person' in bepresent.cleaned_data: + persons_cleaned = bepresent.cleaned_data['person'] + if(len(persons_cleaned) == 0): + continue + + person = bepresent.cleaned_data['person'][0].person + if 'bethere' in bepresent.changed_data and bepresent.cleaned_data['bethere']=='True': + #print "Maybe adding bethere constraint for %s" % (person) + if session.people_constraints.filter(person = person).count()==0: + # need to create new constraint. + #print " yes" + nc = session.people_constraints.create(person = person, + meeting = meeting, + name_id = 'bethere', + source = session.group) + nc.save() + else: + #print "Maybe deleting bethere constraint for %s" % (person) + if session.people_constraints.filter(person = person).count() > 0: + #print " yes" + session.people_constraints.filter(person = person).delete() + + # nuke any cache that might be lingering around. + from ietf.meeting.helpers import session_constraint_expire + session_constraint_expire(session) + messages.success(request, 'Session Request updated') return redirect('sessions_view', acronym=acronym) else: form = SessionForm(initial=initial) + bepresent_formset = make_bepresent_formset(group, session, False) + return render_to_response('sreq/edit.html', { 'meeting': meeting, 'form': form, 'group': group, + 'bepresent_formset' : bepresent_formset, 'session_conflicts': session_conflicts}, RequestContext(request, {}), ) @@ -550,7 +639,7 @@ def no_session(request, acronym): requested_by=login, requested_duration=0, status=SessionStatusName.objects.get(slug='notmeet')) - session.save() + session_save(session) # send notification to_email = SESSION_REQUEST_EMAIL @@ -614,11 +703,11 @@ def tool_status(request): RequestContext(request, {}), ) -def view(request, acronym): +def view(request, acronym, num = None): ''' This view displays the session request info ''' - meeting = get_meeting() + meeting = get_meeting(num) group = get_object_or_404(Group, acronym=acronym) sessions = Session.objects.filter(~Q(status__in=('canceled','notmeet','deleted')),meeting=meeting,group=group).order_by('id') diff --git a/ietf/secr/templates/includes/sessions_request_form.html b/ietf/secr/templates/includes/sessions_request_form.html index 629acc3e3..ab85655bb 100755 --- a/ietf/secr/templates/includes/sessions_request_form.html +++ b/ietf/secr/templates/includes/sessions_request_form.html @@ -1,4 +1,4 @@ - * Required Field + * Required Field
{% csrf_token %} {% if form.non_field_errors %}{{ form.non_field_errors }}{% endif %} @@ -50,12 +50,38 @@
- + People who need to be present: + + {{ bepresent_formset.management_form }} + + + + + + {% for essential_person_form in bepresent_formset.forms %} + + + + + {% endfor %} +
PersonMust be present?
{{ essential_person_form.person }}{{ essential_person_form.bethere }}
+ + + Resources requested: + + {{ form.resources }} + + + Special Requests:
 
i.e. Meetecho, WebEx (please state which, and the reason needed), restrictions on meeting times / days, etc. {{ form.comments.errors }}{{ form.comments }} - + {% include "includes/buttons_save_cancel.html" %} - +
+{% block content_end %} + + +{% endblock %} diff --git a/ietf/secr/templates/includes/sessions_request_view.html b/ietf/secr/templates/includes/sessions_request_view.html index 5c761cc34..44614564c 100644 --- a/ietf/secr/templates/includes/sessions_request_view.html +++ b/ietf/secr/templates/includes/sessions_request_view.html @@ -26,7 +26,11 @@ Other WGs that included {{ group }} in their conflict list: {% if session_conflicts %}{{ session_conflicts }}{% else %}None so far{% endif %} + + Resources requested: + {% if session.resources %}
    {% for resource in session.resources %}
  • {{ resource.desc }}{% endfor %}
{% else %}None so far{% endif %} + {% autoescape off %} - Special Requests:{{ session.comments }} + Special Requests:{{ session.comments }} {% endautoescape %} diff --git a/ietf/secr/templates/sreq/main.html b/ietf/secr/templates/sreq/main.html index 79e5a3206..e095c2cb8 100755 --- a/ietf/secr/templates/sreq/main.html +++ b/ietf/secr/templates/sreq/main.html @@ -14,7 +14,7 @@ {% endblock %} {% block content %} -

» View list of timeslot requests

+

» View list of timeslot requests

Sessions Request Tool: IETF {{ meeting.number }} diff --git a/ietf/secr/templates/sreq/view.html b/ietf/secr/templates/sreq/view.html index 56c783f02..4c4fdf97b 100644 --- a/ietf/secr/templates/sreq/view.html +++ b/ietf/secr/templates/sreq/view.html @@ -18,8 +18,8 @@ {% block content %}
-

Sessions - View

- +

Sessions - View (meeting: {{ meeting.number }})

+ {% include "includes/sessions_request_view.html" %}
diff --git a/ietf/settings.py b/ietf/settings.py index 8928fc9d8..f888afb94 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -19,6 +19,9 @@ LOG_DIR = '/var/log/datatracker' import sys sys.path.append(os.path.abspath(BASE_DIR + "/..")) +# dajaxice now in subdirectory +sys.path.append(os.path.abspath(BASE_DIR + "/../django-dajaxice")) + DEBUG = False TEMPLATE_DEBUG = DEBUG @@ -153,6 +156,7 @@ ROOT_URLCONF = 'ietf.urls' TEMPLATE_DIRS = ( BASE_DIR + "/templates", BASE_DIR + "/secr/templates", + BASE_DIR+"/../django-dajaxice/dajaxice/templates", ) TEMPLATE_CONTEXT_PROCESSORS = ( @@ -400,8 +404,33 @@ USE_ETAGS=True PRODUCTION_TIMEZONE = "America/Los_Angeles" +# Automatic Scheduling +# +# how much to login while running, bigger numbers make it more verbose. +BADNESS_CALC_LOG = 0 +# +# these penalties affect the calculation of how bad the assignments are. +BADNESS_UNPLACED = 1000000 + +# following four are used only during migrations to setup up ConstraintName +# and penalties are taken from the database afterwards. +BADNESS_BETHERE = 200000 +BADNESS_CONFLICT_1 = 100000 +BADNESS_CONFLICT_2 = 10000 +BADNESS_CONFLICT_3 = 1000 + +BADNESS_TOOSMALL_50 = 5000 +BADNESS_TOOSMALL_100 = 50000 +BADNESS_TOOBIG = 100 +BADNESS_MUCHTOOBIG = 500 + +# do not run SELENIUM tests by default +SELENIUM_TESTS = False +SELENIUM_TESTS_ONLY = False + # Put the production SECRET_KEY in settings_local.py, and also any other # sensitive or site-specific changes. DO NOT commit settings_local.py to svn. + from settings_local import * # We provide a secret key only for test and development modes. It's diff --git a/ietf/templates/meeting/agenda-utc.html b/ietf/templates/meeting/agenda-utc.html index 98c9295d0..7dd300b74 100644 --- a/ietf/templates/meeting/agenda-utc.html +++ b/ietf/templates/meeting/agenda-utc.html @@ -121,7 +121,7 @@ You can customize the agenda below to show only selected working group sessions. {% for wg in schedule.groups %}{% ifchanged wg.parent.acronym %}{% if forloop.counter > 1 %} {% endif %} {% endifchanged %} -
{% if wg.state.name = "BOF" %}{{wg.acronym}}{% else %}{{wg.acronym}}{% endif %}
{% endfor %} +
{% if wg.is_bof %}{{wg.acronym}}{% else %}{{wg.acronym}}{% endif %}
{% endfor %} @@ -182,7 +182,7 @@ You can customize the agenda below to show only selected working group sessions. {{item.timeslot.name}} - - + - {% if item.timeslot.show_location %}{{item.timeslot.get_location}}{% endif %} @@ -200,7 +200,7 @@ You can customize the agenda below to show only selected working group sessions. {% if item.session.agenda %}{{item.session.group.name}} {% else %}{{item.session.group.name}}{% endif %} - {% if item.session.group.state.name = "BOF" %} BOF {% endif %} + {% if item.session.is_bof %} BOF {% endif %} {% if item.session.agenda_note %}
{{item.session.agenda_note}}{% endif %} {% if item.session.agenda %}drafts: tar|pdf{%endif%} diff --git a/ietf/templates/meeting/landscape_edit.html b/ietf/templates/meeting/landscape_edit.html index e2c296c29..f8b4d3833 100644 --- a/ietf/templates/meeting/landscape_edit.html +++ b/ietf/templates/meeting/landscape_edit.html @@ -30,7 +30,6 @@ - {% dajaxice_js_import %} @@ -44,14 +43,17 @@ + + + + + + + + + + + + + + + +{% dajaxice_js_import %} + + + +{% endblock js %} + + +{% block start_content_table %}{% endblock %} +{% block end_content_table %}{% endblock %} + +{% block content %} +
+
+< +
+ +
+ +
+
{% csrf_token %} + + {{ editroom.as_table }} + +
+
+
+ +{% endblock %} diff --git a/ietf/templates/meeting/timeslot_edit.html b/ietf/templates/meeting/timeslot_edit.html index 2697cd672..591b3271c 100644 --- a/ietf/templates/meeting/timeslot_edit.html +++ b/ietf/templates/meeting/timeslot_edit.html @@ -20,17 +20,22 @@ + -{% if server_mode and server_mode != "production" %} - -{% endif %} + + + + + + + {% dajaxice_js_import %} - + @@ -41,29 +46,25 @@ function my_js_callback(data){ alert(data.message); } {% endcomment %} +var meeting_number = "{{ meeting.number }}"; var meeting_base_url = "{{ meeting_base_url }}"; var site_base_url = "{{ site_base_url }}"; total_days = {{time_slices|length}}; total_rooms = {{rooms|length}}; -function setup_slots(){ -{% for ts in timeslots %} - make_ss({ - "timeslot_id":"{{ts.id}}", - "room" :"{{ts.location|slugify}}", - "roomtype" :"{{ts.type.slug}}", - "time" :"{{ts.time|date:'Hi' }}", - "date" :"{{ts.time|date:'Y-m-d'}}", - "domid" :"{{ts.js_identifier}}"}); +first_day = new Date("{% with timeslots|first as day %} {{ day.time }} {% endwith %}"); /* needed for the datepicker */ + +function setup_slots(promiselist){ +var ts_promise = load_timeslots("{% url "ietf.meeting.ajax.timeslot_slotsurl" meeting.number %}"); +promiselist.push(ts_promise); + +{% for day in time_slices %} + days.push("{{day}}"); {% endfor %} - console.log("setup_slots run"); } - {% endblock js %} @@ -79,9 +80,10 @@ function setup_slots(){
- + {% for day in time_slices %} + {% for day in time_slices %} {% for slot in slot_slices|lookup:day %} + {% for day in time_slices %} {% for slot in date_slices|lookup:day %} - + {% endfor %} {% endfor %} @@ -148,7 +161,9 @@ function setup_slots(){
name: {{meeting.number}} +
@@ -99,6 +101,7 @@ function setup_slots(){
+ROOM
+DAY
@@ -117,18 +120,28 @@ function setup_slots(){ {% endfor %} {% for r in rooms %} -
X
-
{{r}} ({{r.capacity}})
+ - + +
+
+ {% for resource in r.resources.all %} + + {{resource.desc}} + + {% endfor %} +
+
{% csrf_token %} - {{ addday.as_table }} + {{ addday }} + +