Merged in [7561] from rjsparks@nostrum.com:

Updates and bugfixes to the agenda editing features
* Removes the pattern of using ScheduledSession m2m objects with one of the relationships left as None
* Improves scheduled session state handling
* Simplifies sending email to scheduled session requestors
* Improved timeslot purpose and room editing
* Improved access controls to editing forms
* Better test coverage for the meeting views
* Improvements to the javascript driven by prototyping automated tests
* Better initialization of a new meeting
On the session request form:
  - Allows specifiying room resources (projectors, meetecho)
  - Allows specifying what people must be present
On the schedule editing page:
  - correctly calculates conflict levels
  - displays conflicts in and out of each session separately
  - italicizes BoFs
  - shows resource and people conflicts
Adds automated placement as a management command
 - Legacy-Id: 7628
Note: SVN reference [7561] has been migrated to Git commit 1221f79af9
This commit is contained in:
Henrik Levkowetz 2014-04-22 21:34:23 +00:00
commit bf1e44864d
87 changed files with 12420 additions and 1453 deletions

View file

@ -62,6 +62,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
@ -135,10 +138,10 @@ class GroupMilestoneInfo(models.Model):
docs = models.ManyToManyField('doc.Document', blank=True)
def __unicode__(self):
return self.desc[:20] + "..."
return self.desc[:20] + "..."
class Meta:
abstract = True
ordering = ['due', 'id']
ordering = ['due', 'id']
class GroupMilestone(GroupMilestoneInfo):
time = models.DateTimeField(auto_now=True)
@ -155,7 +158,7 @@ class GroupStateTransitions(models.Model):
next_states = models.ManyToManyField('doc.State', related_name='previous_groupstatetransitions_states')
def __unicode__(self):
return u'%s "%s" -> %s' % (self.group.acronym, self.state.name, [s.name for s in self.next_states.all()])
return u'%s "%s" -> %s' % (self.group.acronym, self.state.name, [s.name for s in self.next_states.all()])
GROUP_EVENT_CHOICES = [
("changed_state", "Changed state"),
@ -163,6 +166,7 @@ GROUP_EVENT_CHOICES = [
("info_changed", "Changed metadata"),
("requested_close", "Requested closing group"),
("changed_milestone", "Changed milestone"),
("sent_notification", "Sent notification")
]
class GroupEvent(models.Model):

View file

@ -1,13 +1,15 @@
import json
import datetime
from django.shortcuts import get_object_or_404, redirect
from django.http import HttpResponse
from django.views.decorators.http import require_POST
from dajaxice.decorators import dajaxice_register
from ietf.ietfauth.utils import role_required, has_role, user_is_person
from ietf.meeting.helpers import get_meeting, get_schedule, get_schedule_by_id, agenda_permissions
from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint
from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint, ScheduledSession, ResourceAssociation
from ietf.meeting.views import edit_timeslots, edit_agenda
from ietf.name.models import TimeSlotTypeName
@ -66,89 +68,50 @@ 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'})
@dajaxice_register
def update_timeslot(request, schedule_id, session_id, scheduledsession_id=None, extended_from_id=None, duplicate=False):
if not has_role(request.user,('Area Director','Secretariat')):
return json.dumps({'error':'no permission'})
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'})
@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):
if not has_role(request.user,'Secretariat'):
return json.dumps({'error':'no permission'})
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)
@ -157,9 +120,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
@ -200,6 +171,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)
@ -218,14 +208,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'))
@ -235,7 +225,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')
@ -325,36 +315,34 @@ def agenda_add(request, meeting):
else:
return redirect(edit_agenda, meeting.number, newagenda.name)
@role_required('Area Director','Secretariat')
@require_POST
def agenda_update(request, meeting, schedule):
# forms are completely useless for update actions that want to
# accept a subset of values. (huh? we could use required=False)
#debug.log("99 meeting.agenda: %s / %s / %s" %
# (schedule, update_dict, request.body))
user = request.user
if has_role(user, "Secretariat"):
if "public" in request.POST:
value1 = True
value = request.POST["public"]
if value == "0" or value == 0 or value=="false":
value1 = False
#debug.log("setting public for %s to %s" % (schedule, value1))
schedule.public = value1
if not user.is_authenticated():
return HttpResponse({'error':'no permission'}, status=403)
cansee,canedit = agenda_permissions(meeting, schedule, request.user)
#read_only = not canedit ## not used
def is_truthy_enough(value):
return not (value == "0" or value == 0 or value=="false")
# TODO: Secretariat should always get canedit
if not (canedit or has_role(user, "Secretariat")):
return HttpResponse({'error':'no permission'}, status=403)
if "public" in request.POST:
schedule.public = is_truthy_enough(request.POST["public"])
if "visible" in request.POST:
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
schedule.visible = is_truthy_enough(request.POST["visible"])
if "name" in request.POST:
value = request.POST["name"]
#debug.log("setting name for %s to %s" % (schedule, value))
schedule.name = value
schedule.name = request.POST["name"]
schedule.save()
@ -390,11 +378,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':
@ -425,13 +413,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()
@ -449,7 +437,7 @@ def meeting_json(request, num):
#############################################################################
## Agenda Editing API functions
## Session details API functions
#############################################################################
def session_json(request, num, sessionid):
@ -467,6 +455,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.scheduledsession_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):

View file

@ -61,26 +61,24 @@ def get_area_list_from_sessions(scheduledsessions, num):
'session__group__parent__acronym').distinct().values_list(
'session__group__parent__acronym',flat=True)
def build_all_agenda_slices(scheduledsessions, all = False):
def build_all_agenda_slices(meeting):
time_slices = []
date_slices = {}
for ss in scheduledsessions:
if(all or ss.session != None):# and len(ss.timeslot.session.agenda_note)>1):
ymd = ss.timeslot.time.date()
for ts in meeting.timeslot_set.exclude(type__in=['reg','break']).order_by('time','name'):
ymd = ts.time.date()
if ymd not in date_slices and ss.timeslot.location != None:
if ymd not in date_slices and ts.location != None:
date_slices[ymd] = []
time_slices.append(ymd)
if ymd in date_slices:
if [ss.timeslot.time, ss.timeslot.time+ss.timeslot.duration] not in date_slices[ymd]: # only keep unique entries
date_slices[ymd].append([ss.timeslot.time, ss.timeslot.time+ss.timeslot.duration])
if [ts.time, ts.time+ts.duration] not in date_slices[ymd]: # only keep unique entries
date_slices[ymd].append([ts.time, ts.time+ts.duration])
time_slices.sort()
return time_slices,date_slices
def get_scheduledsessions_from_schedule(schedule):
ss = schedule.scheduledsession_set.filter(timeslot__location__isnull = False).exclude(session__isnull = True).order_by('timeslot__time','timeslot__name','session__group__group')

View file

View file

@ -0,0 +1,118 @@
"""
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
from optparse import make_option
#import cProfile, pstats, io
import sys
from ietf.meeting.models import Schedule, Meeting
class Command(BaseCommand):
args = '<meeting> <schedulename>'
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
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
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)

View file

@ -0,0 +1,349 @@
# -*- coding: utf-8 -*-
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']

View file

@ -0,0 +1,336 @@
# -*- coding: utf-8 -*-
from south.v2 import DataMigration
class Migration(DataMigration):
def forwards(self, orm):
orm.ResourceAssociation(name_id = "project",
icon = "projector.png",
desc = "Projector in room").save()
orm.ResourceAssociation(name_id = "proj2",
icon = "projector2.png",
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

View file

@ -0,0 +1,340 @@
# -*- coding: utf-8 -*-
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']

View file

@ -0,0 +1,332 @@
# -*- coding: utf-8 -*-
from south.v2 import DataMigration
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

View file

@ -5,6 +5,7 @@ import datetime
from urlparse import urljoin
import copy
import os
import sys
import re
import debug # pyflakes:ignore
@ -12,13 +13,14 @@ import debug # pyflakes:ignore
from django.db import models
from django.conf import settings
# mostly used by json_dict()
from django.template.defaultfilters import slugify, date as date_format, time as time_format
#from django.template.defaultfilters import slugify, date as date_format, time as time_format
from django.template.defaultfilters import date as date_format
from timedeltafield import TimedeltaField
from ietf.doc.models import Document
from ietf.group.models import Group
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName
from ietf.person.models import Person
countries = pytz.country_names.items()
@ -112,6 +114,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, )
@ -158,8 +169,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()
@ -170,16 +181,16 @@ class Meeting(models.Model):
# this functions makes a list of timeslices and rooms, and
# makes sure that all schedules have all of them.
def create_all_timeslots(self):
alltimeslots = self.timeslot_set.all()
for sched in self.schedule_set.all():
ts_hash = {}
for ss in sched.scheduledsession_set.all():
ts_hash[ss.timeslot] = ss
for ts in alltimeslots:
if not (ts in ts_hash):
ScheduledSession.objects.create(schedule = sched,
timeslot = ts)
# def create_all_timeslots(self):
# alltimeslots = self.timeslot_set.all()
# for sched in self.schedule_set.all():
# ts_hash = {}
# for ss in sched.scheduledsession_set.all():
# ts_hash[ss.timeslot] = ss
# for ts in alltimeslots:
# if not (ts in ts_hash):
# ScheduledSession.objects.create(schedule = sched,
# timeslot = ts)
def vtimezone(self):
if self.time_zone:
@ -196,13 +207,36 @@ class Meeting(models.Model):
pass
return ''
def set_official_agenda(self, agenda):
if self.agenda != agenda:
self.agenda = agenda
self.save()
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)
@ -222,7 +256,10 @@ class Room(models.Model):
time=ts.time,
location=self,
duration=ts.duration)
self.meeting.create_all_timeslots()
#self.meeting.create_all_timeslots()
def dom_id(self):
return "room%u" % (self.pk)
def json_url(self):
return "/meeting/%s/room/%s.json" % (self.meeting.number, self.id)
@ -326,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'))
dom_id="ts%u" % (self.pk)
if self.location is not None:
dom_id = self.location.dom_id()
return "%s_%s_%s" % (dom_id, 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):
@ -356,7 +402,7 @@ class TimeSlot(models.Model):
ts.location = room
ts.save()
self.meeting.create_all_timeslots()
#self.meeting.create_all_timeslots()
"""
This routine deletes all timeslots which are in the same time as this slot.
@ -419,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:
@ -513,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'],
@ -530,6 +582,25 @@ 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')
# 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()
# to be renamed ScheduleTimeslotSessionAssignments (stsa)
class ScheduledSession(models.Model):
"""
This model provides an N:M relationship between Session and TimeSlot.
@ -602,27 +673,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
@ -671,9 +746,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)
@ -716,6 +790,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
@ -748,6 +823,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:
@ -818,18 +896,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)
@ -844,17 +924,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)
@ -879,7 +999,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]
@ -947,9 +1067,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
@ -1036,51 +1156,9 @@ 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

739
ietf/meeting/placement.py Normal file
View file

@ -0,0 +1,739 @@
# 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
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
from ietf.meeting.models import ScheduledSession
from django.template.defaultfilters import slugify, date as date_format, time as time_format
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)

View file

@ -2,27 +2,34 @@ import datetime
from ietf.doc.models import Document, State
from ietf.group.models import Group
from ietf.meeting.models import Meeting, Room, TimeSlot, Session, Schedule, ScheduledSession
from ietf.meeting.models import Meeting, Room, TimeSlot, Session, Schedule, ScheduledSession, ResourceAssociation
from ietf.name.models import RoomResourceName
from ietf.person.models import Person
from ietf.utils.test_data import make_test_data
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") ## not used
meeting = Meeting.objects.get(number="42", type="ietf")
schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="test-agenda", visible=True, public=True)
pname = RoomResourceName.objects.create(name='projector',slug='proj')
projector = ResourceAssociation.objects.create(name=pname,icon="notfound.png",desc="Basic projector")
room = Room.objects.create(meeting=meeting, name="Test Room", capacity=123)
room.resources = [projector]
# mars WG
slot = TimeSlot.objects.create(meeting=meeting, type_id="session", duration=30 * 60, location=room,
time=datetime.datetime.combine(datetime.date.today(), datetime.time(9, 30)))
mars_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="mars"),
attendees=10, requested_by=system_person,
requested_duration=20, status_id="sched",
requested_duration=20, status_id="schedw",
scheduled=datetime.datetime.now())
mars_session.resources = [projector]
ScheduledSession.objects.create(timeslot=slot, session=mars_session, schedule=schedule)
# ames WG
@ -30,7 +37,7 @@ def make_meeting_test_data():
time=datetime.datetime.combine(datetime.date.today(), datetime.time(10, 30)))
ames_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="ames"),
attendees=10, requested_by=system_person,
requested_duration=20, status_id="sched",
requested_duration=20, status_id="schedw",
scheduled=datetime.datetime.now())
ScheduledSession.objects.create(timeslot=slot, session=ames_session, schedule=schedule)

View file

@ -9,6 +9,7 @@ from ietf.meeting.models import Schedule, TimeSlot, Session, ScheduledSession, M
from ietf.meeting.test_data import make_meeting_test_data
from ietf.person.models import Person
from ietf.utils.test_utils import TestCase
from ietf.utils.mail import outbox
class ApiTests(TestCase):
@ -19,60 +20,86 @@ 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)
#mars_slot = mars_scheduled.timeslot ## never used
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_slot = ames_scheduled.timeslot ## never used
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
def do_extend(schedule,scheduledsession):
session = scheduledsession.session
url = urlreverse("ietf.meeting.ajax.scheduledsessions_json",
kwargs=dict(num=session.meeting.number,
name=schedule.name,))
post_data = '{ "session_id": "%s", "timeslot_id": "%s", "extendedfrom_id": "%s" }'%(session.pk,scheduledsession.timeslot.slot_to_the_right.pk,scheduledsession.timeslot.pk)
return self.client.post(url,post_data,content_type='application/x-www-form-urlencoded')
# 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(username="ad", password="ad+password")
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)
# Until the next agenda merge, the access permissions on the function under
# test only allow the secretariat to make changes.
# Tweaking the test data here instead of in make_meeting_test_data to simplify
# returning to the intended test scenario after that merge
test_schedule = mars_scheduled.schedule
test_schedule.owner=Person.objects.get(user__username='secretary')
test_schedule.save()
# move to ames
self.client.login(username="secretary", password="secretary+password")
r = do_post(to=ames_scheduled)
# Put ames in the same timeslot as mars
self.client.login(username="plain", password='plain+password')
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(username="secretary", password="secretary+password")
r = do_post(to=None)
# Move the two timeslots close enough together for extension to work
ames_slot_qs=TimeSlot.objects.filter(id=ames_slot.id)
ames_slot_qs.update(time=mars_slot.time+mars_slot.duration+datetime.timedelta(minutes=10))
# Extend the mars session
r = do_extend(schedule,mars_scheduled)
self.assertEqual(r.status_code, 201)
self.assertTrue("error" not in json.loads(r.content))
self.assertEqual(mars_session.scheduledsession_set.count(),2)
# Unschedule mars
r = do_unschedule(mars_scheduled)
self.assertEqual(r.status_code, 200)
self.assertTrue("error" not in json.loads(r.content))
# Make sure it got both the original and extended session
self.assertEqual(mars_session.scheduledsession_set.count(),0)
self.assertEqual(ScheduledSession.objects.get(pk=ames_scheduled.pk).session, None)
self.assertEqual(ScheduledSession.objects.get(session=ames_session).timeslot, mars_slot)
def test_constraints_json(self):
@ -113,7 +140,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)
@ -123,6 +150,7 @@ class ApiTests(TestCase):
# create room
self.client.login(username="secretary", password="secretary+password")
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()
@ -146,6 +174,7 @@ class ApiTests(TestCase):
self.assertTrue(not meeting.room_set.filter(pk=room.pk))
self.assertTrue(not TimeSlot.objects.filter(pk__in=timeslots_before))
# This really belongs in group tests
def test_group_json(self):
make_meeting_test_data()
group = Group.objects.get(acronym="mars")
@ -156,15 +185,34 @@ class ApiTests(TestCase):
info = json.loads(r.content)
self.assertEqual(info["name"], group.name)
# This really belongs in person tests
def test_person_json(self):
make_meeting_test_data()
person = Person.objects.get(user__username="ad")
url = urlreverse("ietf.person.ajax.person_json", kwargs=dict(personid=person.pk))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
info = json.loads(r.content)
self.assertEqual(info["name"], person.name)
def test_sessions_json(self):
meeting = make_meeting_test_data()
url = urlreverse("ietf.meeting.ajax.sessions_json",kwargs=dict(num=meeting.number))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
info = json.loads(r.content)
self.assertEqual(set([x['short_name'] for x in info]),set(['mars','ames']))
schedule = meeting.agenda
url = urlreverse("ietf.meeting.ajax.scheduledsessions_json",kwargs=dict(num=meeting.number,name=schedule.name))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
info = json.loads(r.content)
self.assertEqual(len(info),2)
def test_slot_json(self):
meeting = make_meeting_test_data()
slot = meeting.timeslot_set.all()[0]
@ -172,6 +220,7 @@ class ApiTests(TestCase):
url = urlreverse("ietf.meeting.ajax.timeslot_sloturl",
kwargs=dict(num=meeting.number, slotid=slot.pk))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
info = json.loads(r.content)
self.assertEqual(info["timeslot_id"], slot.pk)
@ -189,15 +238,18 @@ class ApiTests(TestCase):
}
# unauthorized post
prior_slotcount = meeting.timeslot_set.count()
self.client.login(username="ad", password="ad+password")
r = self.client.post(url, post_data)
self.assertEqual(r.status_code, 403)
self.assertEqual(meeting.timeslot_set.count(),prior_slotcount)
# create room
# create slot
self.client.login(username="secretary", password="secretary+password")
r = self.client.post(url, post_data)
self.assertEqual(r.status_code, 302)
self.assertTrue(meeting.timeslot_set.filter(time=slot_time))
self.assertEqual(meeting.timeslot_set.count(),prior_slotcount+1)
def test_delete_slot(self):
meeting = make_meeting_test_data()
@ -220,7 +272,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)
@ -254,20 +306,23 @@ 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',
'name': 'new-test-name',
}
# unauthorized post
self.client.login(username="plain", password="plain+password")
# unauthorized posts
self.client.logout()
r = self.client.post(url, post_data)
self.assertEqual(r.status_code, 403)
self.client.login(username="ad", password="ad+password")
r = self.client.post(url, post_data)
self.assertEqual(r.status_code, 403)
# change agenda
self.client.login(username="ad", password="ad+password")
self.client.login(username="secretary", password="secretary+password")
r = self.client.post(url, post_data)
self.assertEqual(r.status_code, 302)
changed_schedule = Schedule.objects.get(pk=meeting.agenda.pk)
@ -279,7 +334,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(username="plain", password="plain+password")
r = self.client.delete(url)
@ -326,9 +381,12 @@ class ApiTests(TestCase):
schedule.public = True
schedule.save()
# Setting a meeting as official no longer sends mail immediately
prior_length= len(outbox)
r = self.client.post(url, post_data)
self.assertEqual(r.status_code, 200)
self.assertEqual(Meeting.objects.get(pk=meeting.pk).agenda, schedule)
self.assertEqual(len(outbox),prior_length)
def test_read_only(self):
meeting = make_meeting_test_data()
@ -388,3 +446,60 @@ class ApiTests(TestCase):
r = self.client.post(url, post_data)
self.assertEqual(r.status_code, 200)
self.assertTrue(ScheduledSession.objects.get(pk=scheduled.pk).pinned)
class UnusedButExposedApiTests(TestCase):
def test_manipulate_timeslot_via_dajaxice(self):
meeting = make_meeting_test_data()
slot_time = datetime.date.today()
url = '/dajaxice/ietf.meeting.update_timeslot_purpose/'
create_post_data = {
'argv' : json.dumps({
"meeting_num" : meeting.number,
"timeslot_id" : 0,
"purpose" : "plenary",
"room_id" : meeting.room_set.first().id,
"time" : slot_time.strftime("%Y-%m-%d %H:%M:%S"),
"duration" : 3600
})}
prior_timeslot_count = meeting.timeslot_set.count()
# Create as nobody should fail
r = self.client.post(url, create_post_data)
self.assertEqual(r.status_code, 200)
info = json.loads(r.content)
self.assertTrue('error' in info and info['error']=='no permission')
self.assertEqual(meeting.timeslot_set.count(),prior_timeslot_count)
# Successful create
self.client.login(username="secretary", password="secretary+password")
r = self.client.post(url, create_post_data)
self.assertEqual(r.status_code, 200)
info = json.loads(r.content)
self.assertFalse('error' in info)
self.assertTrue('roomtype' in info)
self.assertEqual(info['roomtype'],'plenary')
self.assertEqual(meeting.timeslot_set.count(),prior_timeslot_count+1)
modify_post_data = {
'argv' : json.dumps({
"meeting_num" : meeting.number,
"timeslot_id" : meeting.timeslot_set.get(time=slot_time).id,
"purpose" : "session"
})}
# Fail as non-secretariat
self.client.login(username="plain", password="plain+password")
r = self.client.post(url, modify_post_data)
self.assertEqual(r.status_code, 200)
info = json.loads(r.content)
self.assertTrue('error' in info and info['error']=='no permission')
self.assertEqual(meeting.timeslot_set.get(time=slot_time).type.name,'Plenary')
# Successful change of purpose
self.client.login(username="secretary", password="secretary+password")
r = self.client.post(url, modify_post_data)
self.assertEqual(r.status_code, 200)
self.assertEqual(meeting.timeslot_set.get(time=slot_time).type.name,'Session')

View file

@ -157,13 +157,18 @@ class EditTests(TestCase):
self.client.login(username="secretary", password="secretary+password")
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()
url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number))
# try to get non-existing agenda
url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, name="foo"))
r = self.client.get(url)
self.assertEqual(r.status_code, 404)
# save as
url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number))
self.client.login(username="ad", password="ad+password")
r = self.client.post(url, {
'savename': "foo",
@ -172,7 +177,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)

View file

@ -21,11 +21,13 @@ urlpatterns = patterns('',
(r'^agenda/week-view.html$', views.week_view),
(r'^week-view.html$', views.week_view),
(r'^(?P<num>\d+)/schedule/edit$', views.edit_agenda),
(r'^(?P<num>\d+)/schedule/(?P<schedule_name>[A-Za-z0-9-:_]+)/edit$', views.edit_agenda),
(r'^(?P<num>\d+)/schedule/(?P<schedule_name>[A-Za-z0-9-:_]+)/details$', views.edit_agenda_properties),
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)/edit$', views.edit_agenda),
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)/details$', views.edit_agenda_properties),
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)(?P<ext>.html)?/?$', views.agenda),
(r'^(?P<num>\d+)/agenda(?P<ext>.html)?/?$', views.agenda),
(r'^(?P<num>\d+)/(?P<base>agenda-utc)(?P<ext>.html)?/?$', views.agenda),
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)/sessions.json$', ajax.scheduledsessions_json),
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)/session/(?P<scheduledsession_id>\d+).json$', ajax.scheduledsession_json),
(r'^(?P<num>\d+)/requests.html$', RedirectView.as_view(url='/meeting/%(num)s/requests', permanent=True)),
(r'^(?P<num>\d+)/requests$', views.meeting_requests),
(r'^(?P<num>\d+)/agenda(?P<ext>.txt)$', views.agenda),
@ -35,10 +37,11 @@ urlpatterns = patterns('',
(r'^(?P<num>\d+)/timeslots/edit$', views.edit_timeslots),
(r'^(?P<num>\d+)/rooms$', ajax.timeslot_roomsurl),
(r'^(?P<num>\d+)/room/(?P<roomid>\d+).json$', ajax.timeslot_roomurl),
(r'^(?P<num>\d+)/room/(?P<roomid>\d+).html$', views.edit_roomurl),
(r'^(?P<num>\d+)/timeslots$', ajax.timeslot_slotsurl),
(r'^(?P<num>\d+)/timeslots.json$', ajax.timeslot_slotsurl),
(r'^(?P<num>\d+)/timeslot/(?P<slotid>\d+).json$', ajax.timeslot_sloturl),
(r'^(?P<num>\d+)/agendas/(?P<schedule_name>[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl),
(r'^(?P<num>\d+)/agendas/(?P<name>[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl),
(r'^(?P<num>\d+)/agendas$', ajax.agenda_infosurl),
(r'^(?P<num>\d+)/agendas.json$', ajax.agenda_infosurl),
(r'^(?P<num>\d+)/week-view.html$', views.week_view),
@ -46,6 +49,7 @@ urlpatterns = patterns('',
(r'^(?P<num>\d+)/agenda/(?P<session>[A-Za-z0-9-]+)-drafts.pdf$', views.session_draft_pdf),
(r'^(?P<num>\d+)/agenda/(?P<session>[A-Za-z0-9-]+)-drafts.tgz$', views.session_draft_tarfile),
(r'^(?P<num>\d+)/agenda/(?P<session>[A-Za-z0-9-]+)/?$', views.session_agenda),
(r'^(?P<num>\d+)/sessions.json', ajax.sessions_json),
(r'^(?P<num>\d+)/session/(?P<sessionid>\d+).json', ajax.session_json),
(r'^(?P<num>\d+)/session/(?P<sessionid>\d+)/constraints.json', ajax.session_constraints),
(r'^(?P<num>\d+)/constraint/(?P<constraintid>\d+).json', ajax.constraint_json),

View file

@ -5,14 +5,13 @@ import os
import re
import tarfile
import urllib
import json
from tempfile import mkstemp
import debug # pyflakes:ignore
from django import forms
from django.shortcuts import render_to_response, redirect
from django.http import HttpResponse, Http404
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.template import RequestContext
@ -21,11 +20,12 @@ from django.conf import settings
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.doc.models import Document, State
from ietf.group.models import Group
from ietf.ietfauth.utils import role_required, has_role
from ietf.meeting.models import Meeting, TimeSlot, Session, Schedule
from ietf.meeting.models import Meeting, TimeSlot, Session, Schedule, Room
from ietf.meeting.helpers import get_areas
from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list
from ietf.meeting.helpers import get_all_scheduledsessions_from_schedule
@ -92,13 +92,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.
@ -155,6 +155,7 @@ def agenda_create(request, num=None, schedule_name=None):
return redirect(edit_agenda, meeting.number, newschedule.name)
@role_required('Secretariat')
@ensure_csrf_cookie
def edit_timeslots(request, num=None):
@ -168,8 +169,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])
@ -188,18 +189,50 @@ 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.
@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
meeting = get_meeting(num)
schedule = get_schedule(meeting, schedule_name)
schedule = get_schedule(meeting, name)
meeting_base_url = request.build_absolute_uri(meeting.base_url())
site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash
@ -219,14 +252,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)
@ -239,7 +266,7 @@ def edit_agenda(request, num=None, schedule_name=None):
# django 1.3+
ad.default_hostscheme = site_base_url
time_slices,date_slices = build_all_agenda_slices(scheduledsessions, True)
time_slices,date_slices = build_all_agenda_slices(meeting)
return HttpResponse(render_to_string("meeting/landscape_edit.html",
{"schedule":schedule,
@ -255,7 +282,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")
@ -268,17 +294,22 @@ AgendaPropertiesForm = modelform_factory(Schedule, fields=('name','visible', 'pu
@role_required('Area Director','Secretariat')
@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",
{"schedule":schedule,
"form":form,
"meeting":meeting},
RequestContext(request)), content_type="text/html")
cansee, canedit = agenda_permissions(meeting, schedule, request.user)
if not (canedit or has_role(request.user,'Secretariat')):
return HttpResponseForbidden("You may not edit this agenda")
else:
return HttpResponse(render_to_string("meeting/properties_edit.html",
{"schedule":schedule,
"form":form,
"meeting":meeting},
RequestContext(request)), content_type="text/html")
##############################################################################
# show list of agendas.
@ -289,7 +320,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
@ -524,7 +555,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)
@ -546,7 +577,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",

View file

@ -0,0 +1,201 @@
# -*- coding: utf-8 -*-
from south.v2 import DataMigration
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

View file

@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
from south.db import db
from south.v2 import SchemaMigration
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']

View file

@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
from south.v2 import SchemaMigration
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']

View file

@ -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, . . ."

View file

@ -90,7 +90,7 @@ class TimeChoiceField(forms.ChoiceField):
class MeetingModelForm(forms.ModelForm):
class Meta:
model = Meeting
exclude = ('type',)
exclude = ('type', 'agenda')
def clean_number(self):
number = self.cleaned_data['number']
@ -110,9 +110,6 @@ class MeetingRoomForm(forms.ModelForm):
model = Room
fields = '__all__'
class ExtraSessionForm(forms.Form):
no_notify = forms.BooleanField(required=False, label="Do NOT notify this action")
class NewSessionForm(forms.Form):
day = forms.ChoiceField(choices=SESSION_DAYS)
time = TimeChoiceField()

View file

@ -1,13 +1,29 @@
from django.conf import settings
from django.core.urlresolvers import reverse
from ietf.utils.test_utils import TestCase
from ietf.meeting.models import Meeting
from ietf.utils.test_data import make_test_data
from ietf.person.models import Person
from ietf.group.models import Group, GroupEvent
from ietf.meeting.models import Meeting, Schedule, Room, TimeSlot, ScheduledSession
from ietf.utils.mail import outbox
from ietf.meeting.test_data import make_meeting_test_data
from pyquery import PyQuery
SECR_USER='secretary'
import datetime
import os
import shutil
class MainTestCase(TestCase):
def setUp(self):
self.bluesheet_dir = os.path.abspath("tmp-bluesheet-dir")
self.bluesheet_path = os.path.join(self.bluesheet_dir,'blue_sheet.rtf')
os.mkdir(self.bluesheet_dir)
settings.SECR_BLUE_SHEET_PATH = self.bluesheet_path
def tearDown(self):
shutil.rmtree(self.bluesheet_dir)
def test_main(self):
"Main Test"
url = reverse('meetings')
@ -17,9 +33,175 @@ class MainTestCase(TestCase):
def test_view(self):
"View Test"
make_test_data()
meeting = Meeting.objects.all()[0]
meeting = make_meeting_test_data()
url = reverse('meetings_view', kwargs={'meeting_id':meeting.number})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
q = PyQuery(response.content)
self.assertEqual(len(q('#id_schedule_selector option')),2)
def test_add_meeting(self):
"Add Meeting"
url = reverse('meetings_add')
post_data = dict(number=1,city='Toronto',date='2014-07-20',country='CA',
time_zone='America/New_York',venue_name='Hilton',
venue_addr='100 First Ave')
self.client.login(username='secretary', password='secretary+password')
response = self.client.post(url, post_data, follow=True)
self.assertEqual(response.status_code, 200)
self.assertEqual(Meeting.objects.count(),1)
self.assertEqual(Schedule.objects.count(),1)
def test_edit_meeting(self):
"Edit Meeting"
Meeting.objects.create(number=1,
type_id='ietf',
date=datetime.datetime(2014,7,20))
url = reverse('meetings_edit_meeting',kwargs={'meeting_id':1})
post_data = dict(number='1',date='2014-07-20',city='Toronto')
self.client.login(username="secretary", password="secretary+password")
response = self.client.post(url, post_data,follow=True)
self.assertEqual(response.status_code, 200)
meeting = Meeting.objects.get(number=1)
self.assertEqual(meeting.city,'Toronto')
def test_blue_sheets(self):
"Test Bluesheets"
meeting = make_meeting_test_data()
url = reverse('meetings_blue_sheet',kwargs={'meeting_id':meeting.number})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
url = reverse('meetings_blue_sheet_generate',kwargs={'meeting_id':meeting.number})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
self.assertTrue(os.path.exists(self.bluesheet_path))
def test_notifications(self):
"Test Notifications"
meeting = make_meeting_test_data()
url = reverse('meetings_notifications',kwargs={'meeting_id':42})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
q = PyQuery(response.content)
self.assertEqual(q('#id_notification_list').html(),'ames,mars')
# test that only changes since last notification show up
mars_group = Group.objects.get(acronym='mars')
ames_group = Group.objects.get(acronym='ames')
now = datetime.datetime.now()
then = datetime.datetime.now()+datetime.timedelta(hours=1)
person = Person.objects.get(name="(System)")
GroupEvent.objects.create(group=mars_group,time=now,type='sent_notification',
by=person,desc='sent scheduled notification for %s' % meeting)
ss = meeting.agenda.scheduledsession_set.get(session__group=ames_group)
ss.modified = then
ss.save()
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
q = PyQuery(response.content)
self.assertEqual(q('#id_notification_list').html(),'ames')
# test that email goes out
mailbox_before = len(outbox)
self.client.login(username="secretary", password="secretary+password")
response = self.client.post(url)
self.assertEqual(response.status_code, 302)
self.assertEqual(len(outbox), mailbox_before + 1)
def test_meetings_select(self):
make_meeting_test_data()
url = reverse('meetings_select',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_meetings_rooms(self):
meeting = make_meeting_test_data()
url = reverse('meetings_rooms',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
q = PyQuery(response.content)
self.assertEqual(len(q("#id_rooms_table tr")),2)
# test delete
# first unschedule sessions so we can delete
ScheduledSession.objects.filter(schedule=meeting.agenda).delete()
self.client.login(username="secretary", password="secretary+password")
response = self.client.post(url, {
'room-TOTAL_FORMS': q('input[name="room-TOTAL_FORMS"]').val(),
'room-INITIAL_FORMS': q('input[name="room-INITIAL_FORMS"]').val(),
'room-0-meeting': q('input[name="room-0-meeting"]').val(),
'room-0-id': q('input[name="room-0-id"]').val(),
'room-0-name': q('input[name="room-0-name"]').val(),
'room-0-capacity': q('input[name="room-0-capacity"]').val(),
'room-0-DELETE': 'on'
})
self.assertEqual(response.status_code, 302)
self.assertEqual(Room.objects.filter(meeting=meeting).count(),0)
def test_meetings_times(self):
make_meeting_test_data()
url = reverse('meetings_times',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_meetings_times_delete(self):
meeting = make_meeting_test_data()
qs = TimeSlot.objects.filter(meeting=meeting,type='session')
before = qs.count()
url = reverse('meetings_times_delete',kwargs={
'meeting_id':42,
'schedule_name':'test-agenda',
'time':qs.first().time.strftime("%Y:%m:%d:%H:%M")
})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
after = TimeSlot.objects.filter(meeting=meeting,type='session').count()
self.assertEqual(after,before - (Room.objects.filter(meeting=meeting).count()))
def test_meetings_times_edit(self):
meeting = make_meeting_test_data()
timeslot = TimeSlot.objects.filter(meeting=meeting,type='session').first()
url = reverse('meetings_times_edit',kwargs={
'meeting_id':42,
'schedule_name':'test-agenda',
'time':timeslot.time.strftime("%Y:%m:%d:%H:%M")
})
self.client.login(username="secretary", password="secretary+password")
response = self.client.post(url, {
'day':'1',
'time':'08:00',
'duration_hours':'1',
'duration_minutes':'0',
'name':'Testing'
})
self.assertEqual(response.status_code, 302)
self.assertTrue(TimeSlot.objects.filter(meeting=meeting,name='Testing'))
def test_meetings_nonsession(self):
make_meeting_test_data()
url = reverse('meetings_non_session',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_meetings_select_group(self):
make_meeting_test_data()
url = reverse('meetings_select_group',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
self.client.login(username="secretary", password="secretary+password")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
q = PyQuery(response.content)
self.assertEqual(len(q("#id_scheduled_sessions")),1)
# def test_meetings_schedule():

View file

@ -9,14 +9,17 @@ urlpatterns = patterns('ietf.secr.meetings.views',
url(r'^(?P<meeting_id>\d{1,6})/blue_sheet/$', 'blue_sheet', name='meetings_blue_sheet'),
url(r'^(?P<meeting_id>\d{1,6})/blue_sheet/generate/$', 'blue_sheet_generate', name='meetings_blue_sheet_generate'),
url(r'^(?P<meeting_id>\d{1,6})/edit/$', 'edit_meeting', name='meetings_edit_meeting'),
url(r'^(?P<meeting_id>\d{1,6})/rooms/$', 'rooms', name='meetings_rooms'),
url(r'^(?P<meeting_id>\d{1,6})/times/$', 'times', name='meetings_times'),
url(r'^(?P<meeting_id>\d{1,6})/times/delete/(?P<time>[0-9\:]+)/$', 'times_delete', name='meetings_times_delete'),
url(r'^(?P<meeting_id>\d{1,6})/non_session/$', 'non_session', name='meetings_non_session'),
url(r'^(?P<meeting_id>\d{1,6})/non_session/edit/(?P<slot_id>\d{1,6})/$', 'non_session_edit', name='meetings_non_session_edit'),
url(r'^(?P<meeting_id>\d{1,6})/non_session/delete/(?P<slot_id>\d{1,6})/$', 'non_session_delete', name='meetings_non_session_delete'),
url(r'^(?P<meeting_id>\d{1,6})/select/$', 'select_group',
name='meetings_select_group'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<acronym>[A-Za-z0-9_\-\+]+)/schedule/$', 'schedule', name='meetings_schedule'),
url(r'^(?P<meeting_id>\d{1,6})/notifications/$', 'notifications', name='meetings_notifications'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/$', 'select', name='meetings_select'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/$', 'non_session', name='meetings_non_session'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/edit/(?P<slot_id>\d{1,6})/$', 'non_session_edit', name='meetings_non_session_edit'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/delete/(?P<slot_id>\d{1,6})/$', 'non_session_delete', name='meetings_non_session_delete'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/rooms/$', 'rooms', name='meetings_rooms'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/select/$', 'select_group', name='meetings_select_group'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/times/$', 'times', name='meetings_times'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/times/delete/(?P<time>[0-9\:]+)/$', 'times_delete', name='meetings_times_delete'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/times/edit/(?P<time>[0-9\:]+)/$', 'times_edit', name='meetings_times_edit'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/unschedule/(?P<session_id>\d{1,6})/$', 'unschedule', name='meetings_unschedule'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/(?P<acronym>[A-Za-z0-9_\-\+]+)/schedule/$', 'schedule', name='meetings_schedule'),
url(r'^(?P<meeting_id>\d{1,6})/(?P<acronym>[A-Za-z0-9_\-\+]+)/remove/$', 'remove_session', name='meetings_remove_session'),
)

View file

@ -1,4 +1,3 @@
import os
import datetime
import json
@ -14,19 +13,19 @@ from django.template import RequestContext
from django.utils.functional import curry
from ietf.utils.mail import send_mail
from ietf.meeting.models import Meeting, Session, Room, TimeSlot, ScheduledSession
from ietf.meeting.models import Meeting, Session, Room, TimeSlot, ScheduledSession, Schedule
from ietf.meeting.helpers import get_schedule
from ietf.group.models import Group
from ietf.group.models import Group, GroupEvent
from ietf.person.models import Person
from ietf.secr.meetings.blue_sheets import create_blue_sheets
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, ExtraSessionForm, MeetingModelForm,
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm,
MeetingRoomForm, NewSessionForm, NonSessionEditForm, NonSessionForm, TimeSlotForm,
UploadBlueSheetForm, get_next_slot )
from ietf.secr.proceedings.views import build_choices, handle_upload_file
from ietf.secr.proceedings.views import build_choices, handle_upload_file, make_directories
from ietf.secr.sreq.forms import GroupSelectForm
from ietf.secr.sreq.views import get_initial_session
from ietf.secr.utils.mail import get_cc_list
from ietf.secr.utils.meeting import get_upload_root, get_session, get_timeslot
from ietf.secr.utils.meeting import get_session, get_timeslot
@ -35,27 +34,15 @@ from ietf.secr.utils.meeting import get_upload_root, get_session, get_timeslot
# --------------------------------------------------
# Helper Functions
# --------------------------------------------------
def assign(session,timeslot,meeting):
def assign(session,timeslot,meeting,schedule=None):
'''
Robust function to assign a session to a timeslot
Robust function to assign a session to a timeslot. Much simplyfied 2014-03-26.
'''
qs = timeslot.scheduledsession_set.filter(schedule=meeting.agenda)
# this should never happen, but just in case
if not qs:
ScheduledSession.objects.create(schedule=meeting.agenda,
session=session,
timeslot=timeslot)
else:
# find the first unassigned scheduled session or create a new one
for ss in qs:
if not ss.session:
ss.session = session
ss.save()
break
else:
ScheduledSession.objects.create(schedule=meeting.agenda,
session=session,
timeslot=timeslot)
if schedule == None:
schedule = meeting.agenda
ScheduledSession.objects.create(schedule=schedule,
session=session,
timeslot=timeslot)
session.status_id = 'sched'
session.save()
@ -66,7 +53,6 @@ def build_timeslots(meeting,room=None):
If room is passed pre-create timeslots for the new room. Call this after saving new rooms
or adding a room.
'''
schedule = get_schedule(meeting)
slots = meeting.timeslot_set.filter(type='session')
if room:
rooms = [room]
@ -96,8 +82,6 @@ def build_timeslots(meeting,room=None):
time=new_time,
location=room,
duration=t.duration)
ScheduledSession.objects.create(schedule=schedule,timeslot=t)
meeting.create_all_timeslots();
def build_nonsession(meeting):
'''
@ -105,6 +89,9 @@ def build_nonsession(meeting):
for a new meeting, based on the last meeting
'''
last_meeting = get_last_meeting(meeting)
if not last_meeting:
return None
delta = meeting.date - last_meeting.date
system = Person.objects.get(name='(system)')
schedule = get_schedule(meeting)
@ -128,86 +115,92 @@ def build_nonsession(meeting):
time=new_time,
duration=slot.duration,
show_location=slot.show_location)
ScheduledSession.objects.create(schedule=schedule,session=session,timeslot=ts)
if session:
ScheduledSession.objects.create(schedule=schedule,session=session,timeslot=ts)
def get_last_meeting(meeting):
last_number = int(meeting.number) - 1
return Meeting.objects.get(number=last_number)
def is_combined(session, meeting):
try:
return Meeting.objects.get(number=last_number)
except Meeting.DoesNotExist:
return None
def is_combined(session,meeting,schedule=None):
'''
Check to see if this session is using two combined timeslots
'''
if session.scheduledsession_set.filter(schedule=meeting.agenda).count() > 1:
if schedule == None:
schedule = meeting.agenda
if session.scheduledsession_set.filter(schedule=schedule).count() > 1:
return True
else:
return False
def make_directories(meeting):
def send_notifications(meeting, groups, person):
'''
This function takes a meeting object and creates the appropriate materials directories
'''
path = get_upload_root(meeting)
os.umask(0)
if not os.path.exists(path):
os.makedirs(path)
for d in ('slides','agenda','minutes','id','rfc','bluesheets'):
if not os.path.exists(os.path.join(path,d)):
os.mkdir(os.path.join(path,d))
def send_notification(request, sessions):
'''
This view generates notifications for schedule sessions
Send session scheduled email notifications for each group in groups. Person is the
user who initiated this action, request.uesr.get_profile().
'''
session_info_template = '''{0} Session {1} ({2})
{3}, {4} {5}
Room Name: {6}
---------------------------------------------
'''
group = sessions[0].group
to_email = sessions[0].requested_by.role_email('chair').address
cc_list = get_cc_list(group, request.user.person)
from_email = ('"IETF Secretariat"','agenda@ietf.org')
if sessions.count() == 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)
template = 'meetings/session_schedule_notification.txt'
now = datetime.datetime.now()
for group in groups:
sessions = group.session_set.filter(meeting=meeting)
to_email = sessions[0].requested_by.role_email('chair').address
# TODO confirm list, remove requested_by from cc, add session-request@ietf.org?
cc_list = get_cc_list(group)
from_email = ('"IETF Secretariat"','agenda@ietf.org')
if len(sessions) == 1:
subject = '%s - Requested session has been scheduled for IETF %s' % (group.acronym, meeting.number)
else:
subject = '%s - Requested sessions have been scheduled for IETF %s' % (group.acronym, meeting.number)
template = 'meetings/session_schedule_notification.txt'
# easier to populate template from timeslot perspective. assuming one-to-one timeslot-session
count = 0
session_info = ''
data = [ (s,get_timeslot(s)) for s in sessions ]
for s,t in data:
count += 1
session_info += session_info_template.format(group.acronym,
count,
s.requested_duration,
t.time.strftime('%A'),
t.name,
'%s-%s' % (t.time.strftime('%H%M'),(t.time + t.duration).strftime('%H%M')),
t.location)
# easier to populate template from timeslot perspective. assuming one-to-one timeslot-session
count = 0
session_info = ''
data = [ (s,get_timeslot(s)) for s in sessions ]
for s,t in data:
count += 1
session_info += session_info_template.format(group.acronym,
count,
s.requested_duration,
t.time.strftime('%A'),
t.name,
'%s-%s' % (t.time.strftime('%H%M'),(t.time + t.duration).strftime('%H%M')),
t.location)
# send email
context = {}
context['to_name'] = sessions[0].requested_by
context['agenda_note'] = sessions[0].agenda_note
context['session'] = get_initial_session(sessions)
context['session_info'] = session_info
# send email
context = {}
context['to_name'] = sessions[0].requested_by
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,
from_email,
subject,
template,
context,
cc=cc_list)
send_mail(None,
to_email,
from_email,
subject,
template,
context,
cc=cc_list)
# create sent_notification event
GroupEvent.objects.create(group=group,time=now,type='sent_notification',
by=person,desc='sent scheduled notification for %s' % meeting)
def sort_groups(meeting):
def sort_groups(meeting,schedule=None):
'''
Similar to sreq.views.sort_groups
Takes a Meeting object and returns a tuple scheduled_groups, unscheduled groups.
'''
if not schedule:
schedule = meeting.agenda
scheduled_groups = []
unscheduled_groups = []
#sessions = Session.objects.filter(meeting=meeting,status__in=('schedw','apprw','appr','sched','notmeet','canceled'))
@ -215,7 +208,7 @@ def sort_groups(meeting):
groups_with_sessions = [ s.group for s in sessions ]
gset = set(groups_with_sessions)
sorted_groups_with_sessions = sorted(gset, key = lambda instance: instance.acronym)
scheduled_sessions = ScheduledSession.objects.filter(schedule=meeting.agenda,session__isnull=False)
scheduled_sessions = ScheduledSession.objects.filter(schedule=schedule,session__isnull=False)
groups_with_timeslots = [ x.session.group for x in scheduled_sessions ]
for group in sorted_groups_with_sessions:
if group in groups_with_timeslots:
@ -267,6 +260,14 @@ def add(request):
if form.is_valid():
meeting = form.save()
schedule = Schedule.objects.create(meeting = meeting,
name = 'Empty-Schedule',
owner = Person.objects.get(name='(System)'),
visible = True,
public = True)
meeting.agenda = schedule
meeting.save()
#Create Physical new meeting directory and subdirectories
make_directories(meeting)
@ -349,15 +350,15 @@ def edit_meeting(request, meeting_id):
if request.method == 'POST':
button_text = request.POST.get('submit','')
if button_text == 'Save':
form = MeetingModelForm(request.POST, instance=meeting)
if form.is_valid():
form.save()
messages.success(request,'The meeting entry was changed successfully')
return redirect('meetings_view', meeting_id=meeting_id)
else:
if button_text == 'Cancel':
return redirect('meetings_view', meeting_id=meeting_id)
form = MeetingModelForm(request.POST, instance=meeting)
if form.is_valid():
form.save()
messages.success(request,'The meeting entry was changed successfully')
return redirect('meetings_view', meeting_id=meeting_id)
else:
form = MeetingModelForm(instance=meeting)
@ -385,12 +386,13 @@ def main(request):
RequestContext(request, {}),
)
def non_session(request, meeting_id):
def non_session(request, meeting_id, schedule_name):
'''
Display and add "non-session" time slots, ie. registration, beverage and snack breaks
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
# if the Break/Registration records don't exist yet (new meeting) create them
if not TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other')):
build_nonsession(meeting)
@ -437,7 +439,7 @@ def non_session(request, meeting_id):
schedule=meeting.agenda)
messages.success(request, 'Non-Sessions updated successfully')
return redirect('meetings_non_session', meeting_id=meeting_id)
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule.name)
else:
form = NonSessionForm(initial={'show_location':True})
@ -447,11 +449,12 @@ def non_session(request, meeting_id):
return render_to_response('meetings/non_session.html', {
'slots': slots,
'form': form,
'meeting': meeting},
'meeting': meeting,
'schedule': schedule},
RequestContext(request, {}),
)
def non_session_delete(request, meeting_id, slot_id):
def non_session_delete(request, meeting_id, schedule_name, slot_id):
'''
This function deletes the non-session TimeSlot. For "other" and "plenary" timeslot types
we need to delete the corresponding Session object as well. Check for uploaded material
@ -459,30 +462,30 @@ def non_session_delete(request, meeting_id, slot_id):
'''
slot = get_object_or_404(TimeSlot, id=slot_id)
if slot.type_id in ('other','plenary'):
session = get_session(slot)
session = get_session(slot,schedule=schedule)
if session and session.materials.exclude(states__slug='deleted'):
messages.error(request, 'Materials have already been uploaded for "%s". You must delete those before deleting the timeslot.' % slot.name)
return redirect('meetings_non_session', meeting_id=meeting_id)
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
else:
slot.sessions.all().delete()
slot.delete()
messages.success(request, 'Non-Session timeslot deleted successfully')
return redirect('meetings_non_session', meeting_id=meeting_id)
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
def non_session_edit(request, meeting_id, slot_id):
def non_session_edit(request, meeting_id, schedule_name, slot_id):
'''
Allows the user to assign a location to this non-session timeslot
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
slot = get_object_or_404(TimeSlot, id=slot_id)
session = get_session(slot)
session = get_session(slot,schedule=schedule)
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return redirect('meetings_non_session', meeting_id=meeting_id)
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
form = NonSessionEditForm(request.POST,meeting=meeting, session=session)
if form.is_valid():
@ -500,7 +503,7 @@ def non_session_edit(request, meeting_id, slot_id):
session.save()
messages.success(request, 'Location saved')
return redirect('meetings_non_session', meeting_id=meeting_id)
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
else:
# we need to pass the session to the form in order to disallow changing
@ -518,6 +521,41 @@ def non_session_edit(request, meeting_id, slot_id):
RequestContext(request, {}),
)
def notifications(request, meeting_id):
'''
Send scheduled session email notifications. Finds all groups with
schedule changes since the last time notifications were sent.
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
last_notice = GroupEvent.objects.filter(type='sent_notification').first()
groups = set()
for ss in meeting.agenda.scheduledsession_set.filter(timeslot__type='session'):
last_notice = ss.session.group.latest_event(type='sent_notification')
if last_notice and ss.modified > last_notice.time:
groups.add(ss.session.group)
elif not last_notice:
groups.add(ss.session.group)
if request.method == "POST":
# ensure session state is scheduled
for ss in meeting.agenda.scheduledsession_set.all():
session = ss.session
if session.status.slug == "schedw":
session.status_id = "sched"
session.scheduled = datetime.datetime.now()
session.save()
send_notifications(meeting,groups,request.user.person)
messages.success(request, "Notifications Sent")
return redirect('meetings_view', meeting_id=meeting.number)
return render_to_response('meetings/notifications.html', {
'meeting': meeting,
'groups': sorted(groups, key=lambda a: a.acronym),
'last_notice': last_notice },
RequestContext(request, {}),
)
def remove_session(request, meeting_id, acronym):
'''
Remove session from agenda. Disassociate session from timeslot and set status.
@ -541,12 +579,13 @@ def remove_session(request, meeting_id, acronym):
messages.success(request, '%s Session removed from agenda' % (group.acronym))
return redirect('meetings_select_group', meeting_id=meeting.number)
def rooms(request, meeting_id):
def rooms(request, meeting_id, schedule_name):
'''
Display and edit MeetingRoom records for the specified meeting
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
# if no rooms exist yet (new meeting) formset extra=10
first_time = not bool(meeting.room_set.all())
extra = 10 if first_time else 0
@ -555,7 +594,7 @@ def rooms(request, meeting_id):
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return redirect('meetings', meeting_id=meeting_id)
return redirect('meetings', meeting_id=meeting_id,schedule_name=schedule_name)
formset = RoomFormset(request.POST, instance=meeting, prefix='room')
if formset.is_valid():
@ -573,22 +612,25 @@ def rooms(request, meeting_id):
build_timeslots(meeting,room=form.instance)
messages.success(request, 'Meeting Rooms changed successfully')
return redirect('meetings_rooms', meeting_id=meeting_id)
return redirect('meetings_rooms', meeting_id=meeting_id, schedule_name=schedule_name)
else:
formset = RoomFormset(instance=meeting, prefix='room')
return render_to_response('meetings/rooms.html', {
'meeting': meeting,
'schedule': schedule,
'formset': formset},
RequestContext(request, {}),
)
def schedule(request, meeting_id, acronym):
def schedule(request, meeting_id, schedule_name, acronym):
'''
This view handles scheduling session requests to TimeSlots
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
group = get_object_or_404(Group, acronym=acronym)
sessions = Session.objects.filter(meeting=meeting,group=group,status__in=('schedw','apprw','appr','sched','canceled'))
legacy_session = get_initial_session(sessions)
now = datetime.datetime.now()
@ -598,7 +640,7 @@ def schedule(request, meeting_id, acronym):
for s in sessions:
d = {'session':s.id,
'note':s.agenda_note}
timeslot = get_timeslot(s)
timeslot = get_timeslot(s, schedule=schedule)
if timeslot:
d['room'] = timeslot.location.id
@ -606,7 +648,7 @@ def schedule(request, meeting_id, acronym):
d['time'] = timeslot.time.strftime('%H%M')
else:
d['day'] = 2 # default
if is_combined(s,meeting):
if is_combined(s,meeting,schedule=schedule):
d['combine'] = True
initial.append(d)
@ -617,12 +659,11 @@ def schedule(request, meeting_id, acronym):
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return redirect('meetings_select_group', meeting_id=meeting_id)
return redirect('meetings_select_group', meeting_id=meeting_id,schedule_name=schedule_name)
formset = NewSessionFormset(request.POST,initial=initial)
extra_form = ExtraSessionForm(request.POST)
if formset.is_valid() and extra_form.is_valid():
if formset.is_valid():
# TODO formsets don't have has_changed until Django 1.3
has_changed = False
for form in formset.forms:
@ -635,7 +676,7 @@ def schedule(request, meeting_id, acronym):
day = form.cleaned_data['day']
combine = form.cleaned_data.get('combine',None)
session = Session.objects.get(id=id)
initial_timeslot = get_timeslot(session)
initial_timeslot = get_timeslot(session,schedule=schedule)
# find new timeslot
new_day = meeting.date + datetime.timedelta(days=int(day)-1)
@ -646,20 +687,18 @@ def schedule(request, meeting_id, acronym):
# COMBINE SECTION - BEFORE --------------
if 'combine' in form.changed_data and not combine:
next_slot = get_next_slot(initial_timeslot)
for ss in next_slot.scheduledsession_set.filter(schedule=meeting.agenda,session=session):
for ss in next_slot.scheduledsession_set.filter(schedule=schedule,session=session):
ss.session = None
ss.save()
# ---------------------------------------
if any(x in form.changed_data for x in ('day','time','room')):
# clear the old association
if initial_timeslot:
# get SS record(s) and unschedule by removing the session reference
for ss in session.scheduledsession_set.filter(schedule=meeting.agenda):
ss.session = None
ss.save()
# delete scheduledsession records to unschedule
session.scheduledsession_set.filter(schedule=schedule).delete()
if timeslot:
assign(session,timeslot,meeting)
assign(session,timeslot,meeting,schedule=schedule)
if timeslot.sessions.all().count() > 1:
messages.warning(request, 'WARNING: There are now multiple sessions scheduled for the timeslot: %s' % timeslot)
else:
@ -676,40 +715,41 @@ def schedule(request, meeting_id, acronym):
# COMBINE SECTION - AFTER ---------------
if 'combine' in form.changed_data and combine:
next_slot = get_next_slot(timeslot)
assign(session,next_slot,meeting)
assign(session,next_slot,meeting,schedule=schedule)
# ---------------------------------------
# notify. dont send if Tutorial, BOF or indicated on form
notification_message = "No notification has been sent to anyone for this session."
if (has_changed
and not extra_form.cleaned_data.get('no_notify',False)
and group.state.slug != 'bof'
and get_timeslot(session)): # and the session is scheduled, else skip
send_notification(request, sessions)
notification_message = "Notification sent."
if has_changed:
messages.success(request, 'Session(s) Scheduled for %s. %s' % (group.acronym, notification_message))
return redirect('meetings_select_group', meeting_id=meeting_id)
messages.success(request, 'Session(s) Scheduled for %s.' % group.acronym )
return redirect('meetings_select_group', meeting_id=meeting_id,schedule_name=schedule_name)
else:
formset = NewSessionFormset(initial=initial)
extra_form = ExtraSessionForm()
return render_to_response('meetings/schedule.html', {
'extra_form': extra_form,
'group': group,
'meeting': meeting,
'schedule': schedule,
'show_request': True,
'session': legacy_session,
'formset': formset},
RequestContext(request, {}),
)
def select_group(request, meeting_id):
def select(request, meeting_id, schedule_name):
'''
Options to edit Rooms & Times or schedule a session
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
return render_to_response('meetings/select.html', {
'meeting': meeting,
'schedule': schedule},
RequestContext(request, {}),
)
def select_group(request, meeting_id, schedule_name):
'''
In this view the user can select the group to schedule. Only those groups that have
submitted session requests appear in the dropdowns.
@ -717,19 +757,20 @@ def select_group(request, meeting_id):
NOTE: BOF list includes Proposed Working Group type, per Wanda
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
if request.method == 'POST':
group = request.POST.get('group',None)
if group:
redirect_url = reverse('meetings_schedule', kwargs={'meeting_id':meeting_id,'acronym':group})
redirect_url = reverse('meetings_schedule', kwargs={'meeting_id':meeting_id,'acronym':group,'schedule_name':schedule_name})
else:
redirect_url = reverse('meetings_select_group',kwargs={'meeting_id':meeting_id})
redirect_url = reverse('meetings_select_group',kwargs={'meeting_id':meeting_id,'schedule_name':schedule_name})
messages.error(request, 'No group selected')
return HttpResponseRedirect(redirect_url)
# split groups into scheduled / unscheduled
scheduled_groups, unscheduled_groups = sort_groups(meeting)
scheduled_groups, unscheduled_groups = sort_groups(meeting,schedule)
# prep group form
wgs = filter(lambda a: a.type_id in ('wg','ag') and a.state_id=='active', unscheduled_groups)
@ -748,11 +789,12 @@ def select_group(request, meeting_id):
'bof_form': bof_form,
'irtf_form': irtf_form,
'scheduled_groups': scheduled_groups,
'meeting': meeting},
'meeting': meeting,
'schedule': schedule},
RequestContext(request, {}),
)
def times(request, meeting_id):
def times(request, meeting_id, schedule_name):
'''
Display and edit time slots (TimeSlots). It doesn't display every TimeSlot
object for the meeting because there is one timeslot per time per room,
@ -761,6 +803,7 @@ def times(request, meeting_id):
prepopulated from the last meeting
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
# build list of timeslots
slots = []
@ -791,19 +834,18 @@ def times(request, meeting_id):
# assert False, (new_time, time_seen)
if new_time in time_seen:
messages.error(request, 'There is already a timeslot for %s. To change you must delete the old one first.' % new_time.strftime('%a %H:%M'))
return redirect('meetings_times', meeting_id=meeting_id)
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
for room in meeting.room_set.all():
ts = TimeSlot.objects.create(type_id='session',
meeting=meeting,
name=name,
time=new_time,
location=room,
duration=duration)
ScheduledSession.objects.create(schedule=meeting.agenda,timeslot=ts)
TimeSlot.objects.create(type_id='session',
meeting=meeting,
name=name,
time=new_time,
location=room,
duration=duration)
messages.success(request, 'Timeslots created')
return redirect('meetings_times', meeting_id=meeting_id)
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
else:
form = TimeSlotForm()
@ -811,31 +853,93 @@ def times(request, meeting_id):
return render_to_response('meetings/times.html', {
'form': form,
'meeting': meeting,
'schedule': schedule,
'times': times},
RequestContext(request, {}),
)
def times_delete(request, meeting_id, time):
def times_edit(request, meeting_id, schedule_name, time):
'''
This view handles bulk edit of timeslot details.
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
parts = [ int(x) for x in time.split(':') ]
dtime = datetime.datetime(*parts)
timeslots = TimeSlot.objects.filter(meeting=meeting,time=dtime)
if request.method == 'POST':
button_text = request.POST.get('submit', '')
if button_text == 'Cancel':
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
form = TimeSlotForm(request.POST)
if form.is_valid():
day = form.cleaned_data['day']
time = form.cleaned_data['time']
duration = form.cleaned_data['duration']
name = form.cleaned_data['name']
t = meeting.date + datetime.timedelta(days=int(day))
new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute)
for timeslot in timeslots:
timeslot.time = new_time
timeslot.duration = duration
timeslot.name = name
timeslot.save()
messages.success(request, 'TimeSlot saved')
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
else:
# we need to pass the session to the form in order to disallow changing
# of group after materials have been uploaded
day = dtime.strftime('%w')
if day == 6:
day = -1
initial = {'day':day,
'time':dtime.strftime('%H:%M'),
'duration':timeslots.first().duration,
'name':timeslots.first().name}
form = TimeSlotForm(initial=initial)
return render_to_response('meetings/times_edit.html', {
'meeting': meeting,
'schedule': schedule,
'form': form},
RequestContext(request, {}),
)
def times_delete(request, meeting_id, schedule_name, time):
'''
This view handles bulk delete of all timeslots matching time (datetime) for the given
meeting. There is one timeslot for each room.
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
parts = [ int(x) for x in time.split(':') ]
dtime = datetime.datetime(*parts)
qs = meeting.agenda.scheduledsession_set.filter(timeslot__time=dtime,
session__isnull=False)
if qs:
messages.error(request, 'ERROR deleting timeslot. There is one or more sessions scheduled for this timeslot.')
return redirect('meetings_times', meeting_id=meeting_id)
TimeSlot.objects.filter(meeting=meeting,time=dtime).delete()
messages.success(request, 'Timeslot deleted')
return redirect('meetings_times', meeting_id=meeting_id)
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
def unschedule(request, meeting_id, schedule_name, session_id):
'''
Unschedule given session object
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
session = get_object_or_404(Session, id=session_id)
session.scheduledsession_set.filter(schedule=meeting.agenda).delete()
# TODO: change session state?
messages.success(request, 'Session unscheduled')
return redirect('meetings_select_group', meeting_id=meeting_id, schedule_name=schedule_name)
def view(request, meeting_id):
'''
@ -851,7 +955,7 @@ def view(request, meeting_id):
'''
meeting = get_object_or_404(Meeting, number=meeting_id)
return render_to_response('meetings/view.html', {
'meeting': meeting},
RequestContext(request, {}),

View file

@ -32,7 +32,11 @@ def display_duration(value):
'5400':'1.5 Hours',
'7200':'2 Hours',
'9000':'2.5 Hours'}
return map[value]
if value in map:
return map[value]
else:
x=int(value)
return "%d Hours %d Minutes %d Seconds"%(x//3600,(x%3600)//60,x%60)
@register.filter
def get_published_date(doc):

View file

@ -191,7 +191,7 @@ def make_directories(meeting):
'''
path = get_upload_root(meeting)
os.umask(0)
for leaf in ('slides','agenda','minutes','id','rfc'):
for leaf in ('slides','agenda','minutes','id','rfc','bluesheets'):
target = os.path.join(path,leaf)
if not os.path.exists(target):
os.makedirs(target)

View file

@ -1,6 +1,8 @@
from django import forms
from ietf.group.models import Group
from ietf.meeting.models import ResourceAssociation
from ietf.person.forms import EmailsField
# -------------------------------------------------
@ -44,13 +46,12 @@ 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
class SessionForm(forms.Form):
num_session = forms.ChoiceField(choices=NUM_SESSION_CHOICES)
length_session1 = forms.ChoiceField(choices=LENGTH_SESSION_CHOICES)
@ -65,6 +66,8 @@ 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,required=False)
bethere = EmailsField(label="Must be present", required=False)
def __init__(self, *args, **kwargs):
super(SessionForm, self).__init__(*args, **kwargs)
@ -85,7 +88,7 @@ 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
def clean_conflict1(self):
conflict = self.cleaned_data['conflict1']
check_conflict(conflict)

View file

@ -2,7 +2,8 @@ from django.core.urlresolvers import reverse
from ietf.utils.test_utils import TestCase
from ietf.group.models import Group
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
@ -10,7 +11,7 @@ SECR_USER='secretary'
class SreqUrlTests(TestCase):
def test_urls(self):
draft = make_test_data()
make_test_data()
self.client.login(username="secretary", password="secretary+password")
@ -20,7 +21,8 @@ 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):
@ -32,8 +34,8 @@ class MainTestCase(TestCase):
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):

View file

@ -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<acronym>[A-Za-z0-9_\-\+]+)/$', 'view', name='sessions_view'),
url(r'^(?P<num>[A-Za-z0-9_\-\+]+)/(?P<acronym>[A-Za-z0-9_\-\+]+)/view/$', 'view', name='sessions_view'),
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/approve/$', 'approve', name='sessions_approve'),
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/cancel/$', 'cancel', name='sessions_cancel'),
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/confirm/$', 'confirm', name='sessions_confirm'),
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/edit/$', 'edit', name='sessions_edit'),
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/new/$', 'new', name='sessions_new'),
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/no_session/$', 'no_session', name='sessions_no_session'),
url(r'^(?P<num>[A-Za-z0-9_\-\+]+)/(?P<acronym>[A-Za-z0-9_\-\+]+)/edit/$', 'edit_mtg', name='sessions_edit'),
)

View file

@ -10,13 +10,15 @@ from django.template import RequestContext
from ietf.group.models import Group
from ietf.ietfauth.utils import has_role
from ietf.meeting.models import Meeting, Session, Constraint
from ietf.meeting.models import Meeting, Session, Constraint, ResourceAssociation
from ietf.meeting.helpers import get_meeting
from ietf.name.models import SessionStatusName, ConstraintName
from ietf.secr.sreq.forms import SessionForm, GroupSelectForm, ToolStatusForm
from ietf.secr.utils.decorators import check_permissions, sec_only
from ietf.secr.utils.group import groups_by_session
from ietf.secr.utils.mail import get_ad_email_list, get_chair_email_list, get_cc_list
from ietf.utils.mail import send_mail
from ietf.person.models import Email
# -------------------------------------------------
# Globals
@ -38,10 +40,21 @@ 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 = {}
bethere_people = [x.person for x in sessions[0].constraints().filter(name='bethere')]
bethere_email = []
for person in bethere_people:
e = person.email_set.order_by("-active","-time").first()
if e:
bethere_email.append(e)
# 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
@ -58,6 +71,8 @@ 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()
initial['bethere'] = bethere_email
return initial
def get_lock_message():
@ -72,12 +87,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
@ -175,7 +184,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)
@ -206,7 +215,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()
@ -233,6 +242,10 @@ def confirm(request, acronym):
if not querydict:
raise Http404
form = querydict.copy()
if 'resources' in form:
form['resources'] = [ ResourceAssociation.objects.get(pk=pk) for pk in form['resources'].split(',')]
if 'bethere' in form:
form['bethere'] = [Email.objects.get(address=addr) for addr in form['bethere'].split(',')]
meeting = get_meeting()
group = get_object_or_404(Group,acronym=acronym)
login = request.user.person
@ -265,13 +278,20 @@ 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)
if 'resources' in form:
new_session.resources = form['resources']
# write constraint records
save_conflicts(group,meeting,form['conflict1'],'conflict')
save_conflicts(group,meeting,form['conflict2'],'conflic2')
save_conflicts(group,meeting,form['conflict3'],'conflic3')
if 'bethere' in form:
bethere_cn = ConstraintName.objects.get(slug='bethere')
for email in form['bethere']:
Constraint.objects.create(name=bethere_cn,source=group,person=email.person,meeting=new_session.meeting)
# deprecated in new schema
# log activity
#add_session_activity(group,'New session was requested',meeting,user)
@ -296,19 +316,52 @@ def confirm(request, acronym):
RequestContext(request, {}),
)
#Move this into make_initial
def add_essential_people(group,initial):
# This will be easier when the form uses Person instead of Email
people = set()
if 'bethere' in initial:
people.update(initial['bethere'])
for role in group.role_set.filter(name='chair'):
e = role.person.email_set.order_by("-active","-time").first()
if e:
people.add(e)
e = group.ad.email_set.order_by("-active","-time").first()
if e:
people.add(e)
initial['bethere'] = list(people)
@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()
initial = get_initial_session(sessions)
if 'resources' in initial:
initial['resources'] = [x.pk for x in initial['resources']]
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':
@ -323,7 +376,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:
@ -345,7 +398,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:
@ -367,7 +420,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:
@ -384,6 +437,18 @@ 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
if 'bethere' in form.changed_data and set(form.cleaned_data['bethere'])!=set(initial['bethere']):
session.constraints().filter(name='bethere').delete()
bethere_cn = ConstraintName.objects.get(slug='bethere')
for email in form.cleaned_data['bethere']:
Constraint.objects.create(name=bethere_cn,source=group,person=email.person,meeting=session.meeting)
# deprecated
# log activity
#add_session_activity(group,'Session Request was updated',meeting,user)
@ -391,6 +456,10 @@ def edit(request, acronym):
# send notification
send_notification(group,meeting,login,form.cleaned_data,'update')
# 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)
@ -504,10 +573,15 @@ def new(request, acronym):
return redirect('sessions_new', acronym=acronym)
initial = get_initial_session(previous_sessions)
add_essential_people(group,initial)
if 'resources' in initial:
initial['resources'] = [x.pk for x in initial['resources']]
form = SessionForm(initial=initial)
else:
form = SessionForm()
initial={}
add_essential_people(group,initial)
form = SessionForm(initial=initial)
return render_to_response('sreq/new.html', {
'meeting': meeting,
@ -543,7 +617,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
@ -607,11 +681,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')

View file

@ -1,4 +1,4 @@
<span class="required">*</span> Required Field
<span class="required">*</span> Required Field
<form id="session-request-form" action="." method="post" name="form_post">{% csrf_token %}
{% if form.non_field_errors %}{{ form.non_field_errors }}{% endif %}
<table id="sessions-new-table" cellspacing="1" cellpadding="1" border="0">
@ -14,7 +14,8 @@
Length of Third Session: {{ form.length_session3.errors }}{{ form.length_session3 }}</td></tr>
{% endif %}
<tr class="bg1"><td>Number of Attendees:<span class="required">*</span></td><td>{{ form.attendees.errors }}{{ form.attendees }}</td></tr>
<tr class="bg2"><td>Conflicts to Avoid:</td>
<tr class="bg2"><td>People who must be present:</td><td>{{ form.bethere.errors }}{{ form.bethere }}</td></tr>
<tr class="bg1"><td>Conflicts to Avoid:</td>
<td>
<table>
<tr>
@ -50,12 +51,17 @@
</table>
</td>
</tr>
<tr bgcolor="#cccccc">
<tr class="bg2"><td>Resources requested:</td>
<td>
{{ form.resources }}
</td>
</tr>
<tr class="bg1">
<td valign="top">Special Requests:<br />&nbsp;<br />i.e. Meetecho, WebEx (please state which, and the reason needed), restrictions on meeting times / days, etc.</td>
<td>{{ form.comments.errors }}{{ form.comments }}</td>
</tr>
</table>
{% include "includes/buttons_save_cancel.html" %}
</form>

View file

@ -26,6 +26,13 @@
<td>Other WGs that included {{ group }} in their conflict list:</td>
<td>{% if session_conflicts %}{{ session_conflicts }}{% else %}<i>None so far</i>{% endif %}</td>
</tr>
<tr class="row2">
<td>Resources requested:</td>
<td>{% if session.resources %}<ul>{% for resource in session.resources %}<li>{{ resource.desc }}</li>{% endfor %}</ul>{% else %}<i>None so far</i>{% endif %}</td>
</tr>
<tr class="row1">
<td>People who must be present:</td>
<td>{% if session.bethere %}<ul>{% for email in session.bethere %}<li>{{ email.person }}</li>{% endfor %}</ul>{% else %}<i>None</i>{% endif %}</td>
{% autoescape off %}
<tr class="row2"><td>Special Requests:</td><td>{{ session.comments }}</td></tr>
{% endautoescape %}

View file

@ -11,8 +11,9 @@
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
&raquo; <a href="../../">Meetings</a>
&raquo; <a href="../">{{ meeting.number }}</a>
&raquo; <a href="{% url "meetings" %}">Meetings</a>
&raquo; <a href="{% url "meetings_view" meeting_id=meeting.number %}">{{ meeting.number }}</a>
&raquo; <a href="{% url "meetings_select" meeting_id=meeting.number schedule_name=schedule.name %}">{{ schedule.name }}</a>
&raquo; Rooms and Times
{% endblock %}
@ -21,9 +22,9 @@
<div id="nav" class="rooms-times-nav">
<ul id="list-nav">
<li id="nav-room" class="leftmost"><a href="{% url "meetings_rooms" meeting_id=meeting.number %}">Rooms</a></li>
<li id="nav-time"><a href="{% url "meetings_times" meeting_id=meeting.number %}">Times</a></li>
<li id="nav-non-session"><a href="{% url "meetings_non_session" meeting_id=meeting.number %}">Non-Session</a></li>
<li id="nav-room" class="leftmost"><a href="{% url "meetings_rooms" meeting_id=meeting.number schedule_name=schedule.name %}">Rooms</a></li>
<li id="nav-time"><a href="{% url "meetings_times" meeting_id=meeting.number schedule_name=schedule.name %}">Times</a></li>
<li id="nav-non-session"><a href="{% url "meetings_non_session" meeting_id=meeting.number schedule_name=schedule.name %}">Non-Session</a></li>
</ul>
</div>

View file

@ -8,8 +8,9 @@
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
&raquo; <a href="../">Meetings</a>
&raquo; Blue Sheet
&raquo; <a href="../../">Meetings</a>
&raquo; <a href="../">{{ meeting.number }}</a>
&raquo; Blue Sheets
{% endblock %}
{% block content %}
@ -36,7 +37,7 @@
<div class="button-group">
<ul>
<li><button onclick="window.location='../../'">Back</button></li>
<li><button onclick="window.location='../'">Back</button></li>
</ul>
</div> <!-- button-group -->

View file

@ -29,13 +29,13 @@
<td>{{ item.session.short }}</td>
<td>{{ item.session.group.acronym }}</td>
{% if item.type.slug == 'other' or item.type.slug == 'plenary' %}
<td><a href="{% url "meetings_non_session_edit" meeting_id=meeting.number slot_id=item.id %}">{{ item.location }}</a></td>
<td><a href="{% url "meetings_non_session_edit" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">{{ item.location }}</a></td>
{% else %}
<td>{{ item.location }}</td>
{% endif %}
<td>{{ item.show_location }}</td>
<td>{{ item.type }}</td>
<td><a href="{% url "meetings_non_session_delete" meeting_id=meeting.number slot_id=item.id %}">Delete</a></td>
<td><a href="{% url "meetings_non_session_delete" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">Delete</a></td>
</tr>
{% endfor %}
</tbody>

View file

@ -0,0 +1,53 @@
{% extends "base_site.html" %}
{% block title %}Meetings{% endblock %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
&raquo; <a href="../../">Meetings</a>
&raquo; <a href="../">{{ meeting.number }}</a>
&raquo; Notifications
{% endblock %}
{% block content %}
<div class="module">
<h2>IETF {{ meeting.number }} - Send Notifications</h2>
<form id="id_notification_form" action="." method="post">{% csrf_token %}
<p>Send email notifications to all groups that have been scheduled since the last
notification went out on {{ last_notice.time|date:"Y-m-d" }}:</p>
<p id="id_notification_list">{% if not groups %}(none){% endif %}{% for group in groups %}{{ group.acronym }}{% if not forloop.last %},{% endif %}{% endfor %}<p>
<input type="submit" value="Send Now" name="submit" onclick="return window.confirm('Are you sure you want to send notifications?');">
</form>
{% include "includes/buttons_back.html" %}
</div> <!-- module -->
{% endblock %}

View file

@ -10,7 +10,7 @@
<form id="meetings-meta-rooms" action="" method="post">{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors }}
<table class="full-width">
<table id="id_rooms_table" class="full-width">
<thead>
<tr>
{% for field in formset.forms.0.visible_fields %}

View file

@ -35,9 +35,10 @@
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
&raquo; <a href="../../">Meetings</a>
&raquo; <a href="{% url "meetings_view" meeting_id=meeting.number %}">{{ meeting }}</a>
&raquo; <a href="{% url "meetings_select_group" meeting_id=meeting.number %}">Select</a>
&raquo; <a href="{% url "meetings" %}">Meetings</a>
&raquo; <a href="{% url "meetings_view" meeting_id=meeting.number %}">{{ meeting.number }}</a>
&raquo; <a href="{% url "meetings_select" meeting_id=meeting.number schedule_name=schedule.name %}">{{ schedule.name }}</a>
&raquo; <a href="{% url "meetings_select_group" meeting_id=meeting.number schedule_name=schedule.name %}">Select</a>
&raquo; {{ group.acronym }}
{% endblock %}
@ -66,16 +67,11 @@
{% endfor %}
</div> <!-- inline-group -->
<table class="full-width amstable">
<col width="200">
{{ extra_form }}
</table>
<div class="button-group">
<ul>
<li><button type="submit" name="submit" value="Save">Save</button></li>
<li><button type="button" onclick="if (window.confirm('This group will be permanently removed from the agenda and scheduling tool')) { window.location='{% url "meetings_remove_session" meeting_id=meeting.number acronym=group.acronym %}' };">Remove this group from agenda</button></li>
<li><button type="button" onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number %}'">Back</button></li>
<li><button type="button" onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number schedule_name=schedule.name %}'">Back</button></li>
</ul>
</div> <!-- button-group -->

View file

@ -0,0 +1,51 @@
{% extends "base_site.html" %}
{% block title %}Meetings{% endblock %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
&raquo; <a href="../../">Meetings</a>
&raquo; <a href="../">{{ meeting.number }}</a>
&raquo; {{ schedule.name }}
{% endblock %}
{% block content %}
<div class="module">
<h2>IETF {{ meeting.number }}</h2>
<div class="button-group">
<ul>
<li><button onclick="window.location='{% url "meetings_rooms" meeting_id=meeting.number schedule_name=schedule.name %}'">Rooms and Times</button></li>
<li><button onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number schedule_name=schedule.name %}'">Schedule WG Sessions</button></li>
<li><button type="button" onclick="window.location='../'">Back</button></li>
</ul>
</div> <!-- button-group -->
</div> <!-- module -->
{% endblock %}

View file

@ -7,8 +7,9 @@
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
&raquo; <a href="../../">Meetings</a>
&raquo; <a href="../">{{ meeting.number }}</a>
&raquo; <a href="{% url "meetings" %}">Meetings</a>
&raquo; <a href="{% url "meetings_view" meeting_id=meeting.number %}">{{ meeting.number }}</a>
&raquo; <a href="{% url "meetings_select" meeting_id=meeting.number schedule_name=schedule.name %}">{{ schedule.name }}</a>
&raquo; Select Group
{% endblock %}
@ -47,9 +48,9 @@
<div class="inline-related">
<h2>Scheduled Sessions</h2>
<ul>
<ul id="id_scheduled_sessions">
{% for group in scheduled_groups %}
<li><a href="{% url "meetings_schedule" meeting_id=meeting.number acronym=group.acronym %}">{{ group.acronym }}</a></li>
<li><a href="{% url "meetings_schedule" meeting_id=meeting.number schedule_name=schedule.name acronym=group.acronym %}">{{ group.acronym }}</a></li>
{% endfor %}
</ul>
</div><!-- inline-related-->

View file

@ -13,6 +13,7 @@
<th>Time</th>
<th>Name</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
@ -21,7 +22,8 @@
<td>{{ item.time|date:"D" }}</td>
<td>{{ item.time|date:"H:i" }} - {{ item.end_time|date:"H:i" }}</td>
<td>{{ item.name }}</td>
<td><a href="{% url "meetings_times_delete" meeting_id=meeting.number time=item.time|date:"Y:m:d:H:i" %}">Delete</a></td>
<td><a href="{% url "meetings_times_edit" meeting_id=meeting.number schedule_name=schedule.name time=item.time|date:"Y:m:d:H:i" %}">Edit</a></td>
<td><a href="{% url "meetings_times_delete" meeting_id=meeting.number schedule_name=schedule.name time=item.time|date:"Y:m:d:H:i" %}" onclick="return window.confirm('Are you sure you want to delete this timeslot? Any sessions assigned to this timeslot will be unscheduled.');">Delete</a></td>
</tr>
{% endfor %}
</tbody>

View file

@ -0,0 +1,18 @@
{% extends "meetings/base_rooms_times.html" %}
{% block subsection %}
<div class="module interim-container">
<h2>Meeting - {{ meeting }}</h2>
<p><h3>TimeSlot:</h3></p>
<form id="times-edit-form" enctype="multipart/form-data" action="." method="post">{% csrf_token %}
<table class="full-width amstable">
{{ form.as_table }}
</table>
{% include "includes/buttons_save_cancel.html" %}
</form>
</div> <!-- module -->
{% endblock %}

View file

@ -34,8 +34,16 @@
<div class="button-group">
<ul>
<li><button onclick="window.location='{% url "meetings_edit_meeting" meeting_id=meeting.number %}'">Edit</button></li>
<li><button onclick="window.location='{% url "meetings_rooms" meeting_id=meeting.number %}'">Rooms and Times</button></li>
<li><button onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number %}'">Schedule WG Sessions</button></li>
<li><button onclick="window.location='{% url "meetings_notifications" meeting_id=meeting.number %}'">Notifications</button></li>
<li><button onclick="window.location='{% url "meetings_blue_sheet" meeting_id=meeting.number %}'">Blue Sheets</button></li>
<li><form id="id_schedule_selector">
<select name="forma" onchange="location = this.options[this.selectedIndex].value;">
<option value="">Select a schedule...</option>
{% for sched in meeting.schedule_set.all %}
<option value="{{ sched.name }}">{{ sched.name }}</option>
{% endfor %}
</select>
</form></li>
</ul>
</div> <!-- button-group -->
@ -44,7 +52,8 @@
{% endblock %}
<li><button onclick="window.location='{% url "meetings_rooms" meeting_id=meeting.number %}'">Rooms and Times</button></li>
<li><button onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number %}'">Schedule WG Sessions</button></li>

View file

@ -5,6 +5,9 @@
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/sessions.js"></script>
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
<script type="text/javascript" src="/js/tokenized-field.js"></script>
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}
@ -17,7 +20,6 @@
<a href="http://www.ietf.org/wg/request-tool-instructions.html" target="_blank">Instructions</a>
{% endblock %}
{% block content %}
<div class="module interim-container">

View file

@ -14,7 +14,7 @@
{% endblock %}
{% block content %}
<p>&raquo; <a href="http://datatracker.ietf.org/meeting/requests">View list of timeslot requests</a></p>
<p>&raquo; <a href="/meeting/requests">View list of timeslot requests</a></p>
<div class="module interim-container">
<h2>
Sessions Request Tool: IETF {{ meeting.number }}

View file

@ -5,6 +5,9 @@
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/sessions.js"></script>
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
<script type="text/javascript" src="/js/tokenized-field.js"></script>
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
{% endblock %}
{% block breadcrumbs %}{{ block.super }}

View file

@ -18,8 +18,8 @@
{% block content %}
<div class="module interim-container">
<h2>Sessions - View</h2>
<h2>Sessions - View (meeting: {{ meeting.number }})</h2>
{% include "includes/sessions_request_view.html" %}
<br>

View file

@ -157,6 +157,7 @@ ROOT_URLCONF = 'ietf.urls'
TEMPLATE_DIRS = (
BASE_DIR + "/templates",
BASE_DIR + "/secr/templates",
BASE_DIR+"/../django-dajaxice/dajaxice/templates",
)
TEMPLATE_CONTEXT_PROCESSORS = (
@ -266,9 +267,6 @@ CONFLICT_REVIEW_TXT_URL = 'http://www.ietf.org/cr/'
STATUS_CHANGE_PATH = '/a/www/ietf-ftp/status-changes'
STATUS_CHANGE_TXT_URL = 'http://www.ietf.org/sc/'
AGENDA_PATH = '/a/www/www6s/proceedings/'
AGENDA_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/agenda/%(wg)s.%(ext)s'
MINUTES_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/minutes/%(wg)s.%(ext)s'
SLIDES_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/slides/%(wg)s-*'
IPR_DOCUMENT_PATH = '/a/www/ietf-ftp/ietf/IPR/'
IETFWG_DESCRIPTIONS_PATH = '/a/www/www6s/wg-descriptions/'
IESG_TASK_FILE = '/a/www/www6/iesg/internal/task.txt'
@ -414,6 +412,30 @@ PRODUCTION_TIMEZONE = "America/Los_Angeles"
PYFLAKES_DEFAULT_ARGS= ["ietf", ]
VULTURE_DEFAULT_ARGS= ["ietf", ]
# 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 * # pyflakes:ignore

View file

@ -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 %}
</td>{% endif %}
<td valign="top" id="{{wg.parent.acronym|upper}}-groups">{% endifchanged %}
<div id='selector-{{wg.acronym}}' class="unselected" onclick="toggle(this)">{% if wg.state.name = "BOF" %}<i>{{wg.acronym}}</i>{% else %}{{wg.acronym}}{% endif %}</div>{% endfor %}
<div id='selector-{{wg.acronym}}' class="unselected" onclick="toggle(this)">{% if wg.is_bof %}<i>{{wg.acronym}}</i>{% else %}{{wg.acronym}}{% endif %}</div>{% endfor %}
</td>
</tr>
<tr><td align="center" colspan="{{schedule.area_list|length}}">
@ -182,7 +182,7 @@ You can customize the agenda below to show only selected working group sessions.
</td>
<td colspan="5">
{{item.timeslot.name}}
-
-
{% if item.timeslot.show_location %}<a href="http://tools.ietf.org/agenda/{{schedule.meeting.number}}/venue/?room={{ item.timeslot.get_location|slugify }}">{{item.timeslot.get_location}}</a>{% endif %}
</td>
</tr>
@ -200,7 +200,7 @@ You can customize the agenda below to show only selected working group sessions.
<img src="/images/color-palette-4x4.gif" alt="" onclick="pickAgendaColor('{{schedule.meeting.number}}-{{item.timeslot.time|date:"D-Hi"|lower}}-{{item.session.group.parent.acronym|upper}}-{{item.session.group.acronym|lower}}',this);" title="color tag this line"/ class="noprint">
{% if item.session.agenda %}<a href="/meeting/{{ schedule.meeting.number }}/agenda/{{ item.session.group.acronym }}/">{{item.session.group.name}}</a>
{% 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 %}
<br/><span class="note">{{item.session.agenda_note}}</span>{% endif %}</td>
<td class="materials">{% if item.session.agenda %}drafts:&nbsp;<a href="/meeting/{{schedule.meeting.number}}/agenda/{{item.session.group.acronym}}-drafts.tgz">tar</a>|<a href="/meeting/{{ schedule.meeting.number }}/agenda/{{item.session.group.acronym}}-drafts.pdf">pdf</a>{%endif%}</td>

View file

@ -30,7 +30,6 @@
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.sortable.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.accordion.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.draggable.min.js'></script>
<!-- <script type='text/javascript' src='/js/jquery-ui-1.8.11.custom.min.js'></script> -->
{% dajaxice_js_import %}
@ -44,14 +43,17 @@
<script type='text/javascript'>
meeting_number = {{ meeting.number }};
schedule_id = {{ schedule.id }};
var meeting_number = "{{ meeting.number }}";
var schedule_id = {{ schedule.id }};
var schedule_owner_href = "{{ schedule.owner_email }}";
var schedule_name = "{{ schedule.name }}";
var meeting_base_url = "{{ meeting_base_url }}";
var site_base_url = "{{ site_base_url }}";
total_days = {{time_slices|length}};
total_rooms = {{rooms|length}};
var scheduledsession_post_href = "{% url "ietf.meeting.ajax.scheduledsessions_json" meeting.number schedule.name %}";
var total_days = {{time_slices|length}};
var total_rooms = {{rooms|length}};
function setup_slots(){
function setup_slots(promiselist){
{% for day in time_slices %}
days.push("{{day}}");
{% endfor %}
@ -63,40 +65,16 @@ area_directors["{{ad.group.acronym}}"] = [];
area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.defurl}}"));
{% endfor %}
{% autoescape off %}
{% for s in session_jsons %}
session_obj({{s}});
{% endfor %}
{% endautoescape %}
var ts_promise = load_timeslots("{% url "ietf.meeting.ajax.timeslot_slotsurl" meeting.number %}");
var sess_promise = load_sessions("{% url "ietf.meeting.ajax.sessions_json" meeting.number %}");
promiselist.push(ts_promise);
promiselist.push(sess_promise);
{% for s in scheduledsessions %}
make_ss({ "scheduledsession_id": "{{s.id}}",
"empty": "{{s.empty_str}}",
"timeslot_id":"{{s.timeslot.id}}",
"session_id" :"{{s.session.id}}",
"pinned" :"{{s.pinned}}",
{% if s.session %}{% if s.extendedfrom %}"extendedfrom_id":"{{s.extendedfrom.id}}",
{% endif %}{% endif %}"room" :"{{s.timeslot.location.name|slugify}}",
{% if s.slot_to_the_right %}"following_timeslot_id":"{{s.slot_to_the_right.id}}",
{% endif %}"roomtype" :"{{s.slottype}}",
"time" :"{{s.timeslot.time|date:'Hi' }}",
"date" :"{{s.timeslot.time|date:'Y-m-d'}}",
"domid" :"{{s.timeslot.js_identifier}}"});
{% endfor %}
make_ss({ "scheduledsession_id": 0,
"empty": true,
"timeslot_id":"Unassigned",
"session_id" :null,
"room" :"unassigned",
"time" :null,
"date" :null,
"domid" :"sortable-list"});
var ss_promise = load_scheduledsessions(ts_promise, sess_promise, scheduledsession_post_href);
promiselist.push(ss_promise);
console.log("setup_slots run");
calculate_room_select_box();
{% for area in area_list %}
legend_status["{{area.upcase_acronym}}"] = true;
{% endfor %}
@ -165,7 +143,6 @@ area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.def
<div class="agenda_div">
<div id="dialog-confirm" title="" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
@ -191,135 +168,6 @@ area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.def
</p>
</div>
<table id="meetings" class="ietf-navbar" style="width:100%">
<tr>
<th class="schedule_title"><div id="spinner"><!-- spinney goes here --></div></th>
{% for day in time_slices %}
<th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title agenda_slot_unavailable">
<div id="close_{{day|date:'Y-m-d'}}" class="close top_left very_small close_day">x</div>
{{day|date:'D'}}&nbsp;({{day}})
</th>
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer" id="">
<div class="ui-widget-content ui-resizable" id="resize-{{day|date:'Y-m-d'}}-spacer">
<div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
</div>
</th>
{% endfor %}
</tr>
<tr>
<th class="th_column"><button id="show_all_button" class="styled_button">show all</button></th>
{% for day in time_slices %}
{% for slot in date_slices|lookup:day %}
<th class="day_{{day}}-{{slot.0|date:'Hi'}} day_{{day}} room_title ">{{slot.0|date:'Hi'}}-{{slot.1|date:'Hi'}} </th>
{% endfor %}
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></th>
{% endfor %}
{% for r in rooms %}
<tr id="{{r.name|to_acceptable_id}}" class="{% cycle 'agenda_row_alt' '' %} agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_{{r.name|to_acceptable_id}}">X</div>
<div class="right room_name">{{r.name}} <span class="capacity">({{r.capacity}})</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">{{r.name}}</span>-->
</th>
{% for day in time_slices %}
{% for slot in date_slices|lookup:day %}
<td id="{{r.name|slugify}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot agenda_slot_unavailable" capacity="{{r.capacity}}" ></td>
{% endfor %}
<td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
<div id="session-info" class="ui-droppable bucket-list room_title">
<div class="agenda_slot_title"><b>Session Information:</b></div>
<div class="ss_info_box">
<div class="ss_info ss_info_left">
<table>
<tr><td class="ss_info_name_short">Group:</td><td><span id="info_grp"></span>
<!-- <button id="agenda_sreq_button" class="right">Edit Request</button> --></tr>
<tr><td class="ss_info_name_short">Name:</td> <td d="info_name"></td></tr>
<tr><td class="ss_info_name_short">Area:</td> <td><span id="info_area"></span><button id="show_all_area" class="right">Show All</button></td></tr>
</table>
</div>
<div class="ss_info ss_info_right">
<table>
<tr><td class="ss_info_name_long">Duration/Capacity:</td><td class="info_split" id="info_duration"></td> <td class="info_split" id="info_capacity"></td></tr>
<tr><td class="ss_info_name_long">Location:</td><td colspan=2 id="info_location"></td></tr>
<tr><td class="ss_info_name_long">Responsible AD:</td><td colspan=2 id="info_responsible"></td></tr>
<tr><td class="ss_info_name_long">Requested By:</td><td colspan=2 id="info_requestedby"></td></tr>
</table>
</div>
<div id="conflict_table">
<div id="special_requests">Special Requests</div>
<table>
<tbody id="conflict_table_body">
<tr class="conflict_list_row">
<td class="conflict_list_title">
Group conflicts
</td>
<td id="conflict_group_list">
<ul>
</ul>
</td>
</tr>
<tr class="conflict_list_row">
<td class="conflict_list_title">
<b>be present</b>
</td>
<td id="conflict_people_list">
<ul>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<div class="agenda_find_free"><button class="agenda_selected_buttons small_button" id="find_free">Find Free</button></div>
<div class="agenda_double_slot button_disabled">
<button class="agenda_selected_buttons small_button" disabled
id="double_slot">Extend</button>
</div>
<div id="agenda_pin_slot" class="button_disabled">
<button class="agenda_selected_buttons small_button" disabled
id="pin_slot">Pin</button>
</div>
<div class="color_legend">
{% for area in area_list %}
<span class="{{area.upcase_acronym}}-scheme"><input class='color_checkboxes' type="checkbox" id="{{area.upcase_acronym}}" value="{{area.upcase_acronym}}-value" checked>{{area.upcase_acronym}}</span>
{% endfor %}
</div>
</div>
<div class="agenda_save_box">
<div id="agenda_title"><b>Agenda name: </b><span>{{schedule.name}}</span></div>
<div id="agenda_saveas">
<form action="{{saveasurl}}" method="post">{% csrf_token %}
{{ saveas.as_p }}
<input type="submit" name="saveas" value="saveas">
</form>
</div>
</div>
</div>
<!-- some boxes for dialogues -->
<div id="dialog-confirm-two" title="" style="display:none">
<p>
@ -349,14 +197,171 @@ area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.def
</p>
</div>
<div id="can-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
<table id="meetings" class="ietf-navbar" style="width:100%">
<tr>
<th class="schedule_title"><div id="pageloaded" style="display:none">loaded</div><div id="spinner"><!-- spinney goes here --></div></th>
<th></th>
{% for day in time_slices %}
<th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title agenda_slot_unavailable">
<div id="close_{{day|date:'Y-m-d'}}" class="close top_left very_small close_day">x</div>
{{day|date:'D'}}&nbsp;({{day}})
</th>
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer" id="">
<div class="ui-widget-content ui-resizable" id="resize-{{day|date:'Y-m-d'}}-spacer">
<div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
</div>
</th>
{% endfor %}
</tr>
<tr>
<th class="th_column"><button id="show_all_button" class="styled_button">show all</button></th>
<th><!-- resources --></th>
{% for day in time_slices %}
{% for slot in date_slices|lookup:day %}
<th class="day_{{day}}-{{slot.0|date:'Hi'}} day_{{day}} room_title ">{{slot.0|date:'Hi'}}-{{slot.1|date:'Hi'}} </th>
{% endfor %}
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></th>
{% endfor %}
</tr>
{% for r in rooms %}
<tr id="{{r.name|to_acceptable_id}}" class="{% cycle 'agenda_row_alt' '' %} agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_{{r.name|to_acceptable_id}}">X</div>
<div class="right room_name">{{r.name}} <span class="capacity">({{r.capacity}})</span></div>
</th>
<th class="room_features">
<div class="resource_list">
{% for resource in r.resources.all %}
<span class="resource_image">
<img src="/images/{{ resource.icon }}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>
</span>
{% endfor %}
</div>
</th>
{% for day in time_slices %}
{% for slot in date_slices|lookup:day %}
<td id="{{r.domid}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot agenda_slot_unavailable" capacity="{{r.capacity}}" ></td>
{% endfor %}
<td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
<div id="can-not-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
You can not extend this session. The slot is not available.
</p>
<div id="session-info" class="ui-droppable bucket-list room_title">
<div class="agenda_slot_title"><b>Session Information:</b></div>
<div class="ss_info_box">
<div class="ss_info ss_info_left">
<table>
<tr><td class="ss_info_name_short">Group:</td><td><span id="info_grp"></span>
<!-- <button id="agenda_sreq_button" class="right">Edit Request</button> --></tr>
<tr><td class="ss_info_name_short">Name:</td> <td id="info_name"></td></tr>
<tr><td class="ss_info_name_short">Area:</td> <td><span id="info_area"></span>
<button id="show_all_area" class="right">Show All</button></td></tr>
<tr>
<td colspan=2>
<div class="agenda_nice_button" id="agenda_find_free">
<button class="agenda_selected_buttons small_button" id="find_free">Find Free</button>
</div>
<div class="agenda_nice_button button_disabled" id="agenda_double_slot">
<button class="agenda_selected_buttons small_button" disabled id="double_slot">Extend</button>
</div>
<div id="agenda_pin_slot" class="agenda_nice_button button_disabled">
<button class="agenda_selected_buttons small_button" disabled id="pin_slot">Pin</button>
</div>
</td>
</tr>
</table>
</div>
<div class="ss_info ss_info_right">
<table>
<tr><td class="ss_info_name_long">Duration/Capacity:</td>
<td class="info_split"><span id="info_duration"></span>
<span style="right" id="grp_type"></span></td>
<td class="info_split" id="info_capacity"></td></tr>
<tr><td class="ss_info_name_long">Location:</td><td colspan=2 id="info_location"></td></tr>
<tr><td class="ss_info_name_long">Responsible AD:</td><td colspan=2 id="info_responsible"></td></tr>
<tr><td class="ss_info_name_long">Requested By:</td><td colspan=2 id="info_requestedby"></td></tr>
<tr>
<td colspan=3>
<div class="agenda_nice_button button_disabled" id="agenda_prev_session">
<button class="agenda_selected_buttons small_button" disabled id="prev_session">Prev</button>
</div>
<div class="agenda_nice_button button_disabled" id="agenda_show">
<button class="agenda_selected_buttons small_button" disabled id="show_session">Show</button>
</div>
<div class="agenda_nice_button button_disabled" id="agenda_next_session">
<button class="agenda_selected_buttons small_button" disabled id="next_session">Next</button>
</div>
<div class="request_features" id="agenda_requested_features">
</div>
</td>
</tr>
</table>
</div>
<div id="conflict_table">
<div id="special_requests">Special Requests</div>
<table>
<tbody id="conflict_table_body">
<tr class="conflict_list_row">
<td class="conflict_list_title">
Group conflicts
</td>
<td id="conflict_group_list">
<ul>
</ul>
</td>
</tr>
<tr class="conflict_list_row">
<td class="conflict_list_title">
<b>be present</b>
</td>
<td id="conflict_people_list">
<ul>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<div class="color_legend">
{% for area in area_list %}
<span class="{{area.upcase_acronym}}-scheme"><input class='color_checkboxes' type="checkbox" id="{{area.upcase_acronym}}" value="{{area.upcase_acronym}}-value" checked>{{area.upcase_acronym}}</span>
{% endfor %}
</div>
</div>
<div class="agenda_save_box">
<div id="agenda_title"><b>Agenda name: </b><span>{{schedule.name}}</span></div>
<div id="agenda_saveas">
<form action="{{saveasurl}}" method="post">{% csrf_token %}
{{ saveas.as_p }}
<input id="saveasbutton" type="submit" name="saveas" value="saveas">
</form>
</div>
</div>
</div>
{% endblock %}

View file

@ -21,7 +21,7 @@
<div id="read_only">
<p>
You can not have access this agenda. It belongs to {{ schedule.owner }}.
You do not have access this agenda. It belongs to {{ schedule.owner }}.
</p>
<p>
<a href="{% url "ietf.meeting.views.edit_agendas" meeting.number %}">List your meetings</a>.

View file

@ -46,7 +46,7 @@ th { text-align: right; vertical-align: text-top; }
<tr><td class="status" colspan="7"><!-- {{session.group.parent.id}} -->{{session.status}}</td></tr>
{%endifchanged%}
<tr class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}">
<th valign="top">{{session.group.acronym|upper}}</th>
<th valign="top"><a href="{% url "ietf.secr.sreq.views.edit_mtg" num=meeting.number acronym=session.group.acronym %}">{{session.group.acronym|upper}}</a></th>
<td valign="top" align="center">{% if not session.requested_duration %}<i>{{session.status}}</i>{%else%} {{session.requested_duration|stringformat:"s"|slice:"0:4"}} {% endif %}</td>
<td valign="top" align="center">{{session.attendees}}</td>
<td valign="top">

View file

@ -0,0 +1,60 @@
{% extends "base.html" %}
{% load ietf_filters %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load humanize %}
{% load dajaxice_templatetags %}
{% block title %}IETF {{ meeting.number }} Meeting Agenda: Timeslot/Room Availability{% endblock %}
{% load agenda_custom_tags %}
{% block pagehead %}
<link rel='stylesheet' type='text/css' href='/css/jquery-ui-themes/jquery-ui-1.8.11.custom.css' />
<link rel='stylesheet' type='text/css' href='/css/base2.css' />
<link rel='stylesheet' type='text/css' href='/css/agenda.css' />
{% endblock pagehead %}
{% block js %}
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery-ui.custom.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.widget.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.droppable.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.sortable.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.accordion.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.draggable.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.datetime.js'></script>
<!-- source (MIT License) http://momentjs.com/ https://github.com/moment/moment/ -->
<script type='text/javascript' src='/js/moment.min.js'></script>
<!-- source (MIT License) : https://github.com/trentrichardson/jQuery-Timepicker-Addon -->
<script type='text/javascript' src='/js/jquery-ui-timepicker/jquery-ui-timepicker-addon.js'></script>
<script type='text/javascript' src='/js/jquery-ui-timepicker/jquery-ui-sliderAccess.js.js'></script>
<link rel='stylesheet' type='text/css' href='/css/jquery-ui-timepicker-addon.css' />
{% dajaxice_js_import %}
<script type='text/javascript' src='/js/spin/dist/spin.min.js'></script>
{% endblock js %}
{% block start_content_table %}{% endblock %}
{% block end_content_table %}{% endblock %}
{% block content %}
<div class="wrapper custom_text_stuff">
<div style="ui-icon ui-icon-arrow-1-w" id="close_ietf_menubar">
&lt;
</div>
<div class="room_div">
<div id="edit_room_dialog">
<form action="{{roomsurl}}" method="post">{% csrf_token %}
<table>
{{ editroom.as_table }}
<tr><td><input type="submit" name="editroom" value="editroom"></td></tr>
</table>
</form>
</div>
{% endblock %}

View file

@ -20,17 +20,22 @@
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.sortable.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.accordion.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.draggable.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.datetime.js'></script>
{% if server_mode and server_mode != "production" %}
<script src="https://towtruck.mozillalabs.com/towtruck.js"></script>
{% endif %}
<!-- source (MIT License) http://momentjs.com/ https://github.com/moment/moment/ -->
<script type='text/javascript' src='/js/moment.min.js'></script>
<!-- source (MIT License) : https://github.com/trentrichardson/jQuery-Timepicker-Addon -->
<script type='text/javascript' src='/js/jquery-ui-timepicker/jquery-ui-timepicker-addon.js'></script>
<script type='text/javascript' src='/js/jquery-ui-timepicker/jquery-ui-sliderAccess.js.js'></script>
<link rel='stylesheet' type='text/css' href='/css/jquery-ui-timepicker-addon.css' />
{% dajaxice_js_import %}
<script type='text/javascript' src='/js/spin/dist/spin.min.js'></script>
<script type='text/javascript' src='/js/agenda/timeslot_edit.js'></script>
<script type='text/javascript' src='/js/agenda/agenda_helpers.js'></script>
<script type='text/javascript' src='/js/agenda/agenda_objects.js'></script>
<script type='text/javascript' src='/js/agenda/agenda_helpers.js'></script>
<script type='text/javascript' src='/js/agenda/agenda_listeners.js'></script>
@ -41,29 +46,25 @@ function my_js_callback(data){
alert(data.message);
}<!-- dajaxice example --> {% 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");
}
</script>
<style type='text/css'>
</style>
{% endblock js %}
@ -79,9 +80,10 @@ function setup_slots(){
<div class="agenda_div">
<table id="meetings" class="ietf-navbar" style="width:100%">
<th class="schedule_title"><span id="schedule_name">name: {{meeting.number}}</span>
<th class="schedule_title"><div id="pageloaded" style="display:none"><span id="schedule_name">name: {{meeting.number}}</span></div>
<div id="spinner"><!-- spinney goes here --></div>
</th>
<th><!-- resources --></th>
{% for day in time_slices %}
<th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title">
<div style="display: none;" class="delete delete_day bottom_left" id="delete_{{day|date:'Y-m-d'}}">X</div>
@ -99,6 +101,7 @@ function setup_slots(){
<div class="addbutton" id="add_room">+ROOM</div>
<div class="addbutton" id="add_day">+DAY</div>
</th>
<th><!-- resources --></th>
{% for day in time_slices %}
{% for slot in slot_slices|lookup:day %}
<th class="day_{{day}} room_title">
@ -117,18 +120,28 @@ function setup_slots(){
{% endfor %}
{% for r in rooms %}
<tr id="{{r|to_acceptable_id}}" class="agenda_slot">
<tr id="{{r.name|to_acceptable_id}}" class="agenda_slot_row">
<th class="vert_time">
<div class="delete delete_room bottom_left"
id="delete_{{r|to_acceptable_id}}"
id="delete_{{r.name|to_acceptable_id}}"
href="{{r.json_url}}"
roomid="{{r.pk}}">X</div>
<div class="right room_name">{{r}} <span class="capacity">({{r.capacity}})</span></div>
<div class="right room_name"><a class="edit_room editlink"
href="/meeting/{{ meeting.number }}/room/{{r.pk}}.html" >{{r.name}} <span class="capacity">({{r.capacity}})</span></a></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">{{r}}</span></th> -->
<!-- <span class="hide_room light_blue_border">X</span><span class="left">{{r.name}}</span></th> -->
<th class="room_features">
<div class="resource_list">
{% for resource in r.resources.all %}
<span class="resource_image">
<img src="/images/{{ resource.icon }}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>
</span>
{% endfor %}
</div>
</th>
{% for day in time_slices %}
{% for slot in date_slices|lookup:day %}
<td id="{{r|slugify}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot {% cycle 'agenda_slot_alt' '' %} agenda_slot_unavailable" ></td>
<td slot_time="{{day}} {{slot.0|date:'H:i:s'}}" slot_duration="{{slot.2}}" slot_room="{{r.pk}}" id="{{r.domid}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot {% cycle 'agenda_slot_alt' '' %} agenda_slot_unavailable" ></td>
{% endfor %}
<td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
{% endfor %}
@ -148,7 +161,9 @@ function setup_slots(){
<div class="dialog" id="add_day_dialog">
<table>
<form action="{{adddayurl}}" method="post">{% csrf_token %}
{{ addday.as_table }}
{{ addday }}
<tr><th><label>Duration</label></th><td><input type="text" id="duration_time"></td></tr>
<tr><th></th><td><div id="timespan"></div></td></tr>
<tr><td><input type="submit" name="addday" value="addday"></td></tr>
</form>
</table>

View file

@ -69,7 +69,7 @@ urlpatterns = patterns('',
if settings.SERVER_MODE in ('development', 'test'):
urlpatterns += patterns('',
(r'^(?P<path>(?:images|css|js)/.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),
(r'^(?P<path>(?:images|css|js|test)/.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),
(r'^(?P<path>admin/(?:img|css|js)/.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),
(r'^(?P<path>secretariat/(img|css|js)/.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),
(r'^(?P<path>robots\.txt)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT+"dev/"}),

View file

@ -251,8 +251,9 @@ div.agenda_slot{
#session-info{
min-height: 150px;
min-height: 160px;
position: relative;
width:100%;
}
.ss_info {
@ -315,6 +316,18 @@ div.agenda_slot{
}
.wg_style {
font-style:normal;
}
.bof_style {
font-style:italic;
}
#grp_type {
display: inline;
float: right;
}
.ss_info tr{
/* border-right: 1px solid #89D; */
/* border-top: 1px solid #89D; */
@ -354,36 +367,36 @@ td.ss_info_name_short{
min-width:50px;
}
.agenda_find_free{
.agenda_nice_button {
border: 1px solid #89D;
background-color: #EDF5FF;
padding: 5px;
max-width: 150px;
float:left;
margin-top:70px;
margin-left:3px;
margin-top: 5px;
margin-bottom: 10px;
margin-left:10px;
}
.agenda_find_free{
}
.agenda_double_slot {
border: 1px solid #89D;
background-color: #EDF5FF;
padding: 5px;
max-width: 150px;
float:left;
margin-top:70px;
margin-left:3px;
}
#agenda_pin_slot {
border: 1px solid #89D;
background-color: #EDF5FF;
padding: 5px;
max-width: 150px;
float:left;
margin-top:70px;
margin-left:3px;
}
#agenda_prev_session {
}
#agenda_show {
}
#agenda_next_session {
}
.button_disabled {
color: #EDF4FF;
text-color: #EDF4FF;
@ -489,6 +502,11 @@ div#conflict_table {
background-color: #89D;
}
div.conflictlevel {
display: inline-block;
min-width: 1.5em;
}
div.our-conflict {
padding-left: 20px;
background-image: url('/images/conflict-boxes/narow3.png');
@ -834,11 +852,13 @@ table.actual_conflic3 {
border: solid 1px #203E5F;
}
/*
.agenda_row_alt td {
/* for now, use no alternating theme */
border-bottom: 1px solid orange;
margin-bottom: 5px;
}
*/
.agenda_slot_plenary {
/* background-color: #d38f2d; */ /* dark orange */
@ -1045,3 +1065,47 @@ div.line{
padding-top: 0px;
padding-bottom: 0px;
}
.ui-resizable-s {
height:6px;
/* background-color:aqua; */
/* background: #99beff; /\* Old browsers *\/ */
/* background: -moz-linear-gradient(top, #99beff 0%, #1e5799 100%); /\* FF3.6+ *\/ */
/* expect error on firefox */
background: -webkit-gradient(linear, top, bottom, color-stop(0%,#99beff), color-stop(100%,#1e5799)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #99beff 0%,#1e5799 100%); /* Chrome10+,Safari5.1+ */
/* background: -o-linear-gradient(left, #99beff 0%,#1e5799 100%); /\* Opera 11.10+ *\/ */
/* background: -ms-linear-gradient(left, #99beff 0%,#1e5799 100%); /\* IE10+ *\/ */
/* background: linear-gradient(to right, #99beff 0%,#1e5799 100%); /\* W3C *\/ */
/* expect error on firefox */
/* filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#99beff', endColorstr='#1e5799',GradientType=1 ); /\* IE6-9 *\/ */
}
.resource_image {
float: left;
}
.resource_list {
width: 75px;
}
.room_features {
background-color:#2647A0;
}
.agenda_requested_feature {
float: right;
}
/* copied from doc.css, maybe with some modifications. */
a.editlink {
background-image: url("/images/pencil.png");
background-size: 10px;
background-position: right top;
background-attachment: scroll;
background-repeat: no-repeat;
padding-right: 12px;
}
a.editlink:link {text-decoration:none; color:inherit;}
a.editlink:visited {text-decoration:none; color:inherit;}

View file

@ -0,0 +1,11 @@
.ui-timepicker-div .ui-widget-header { margin-bottom: 8px; }
.ui-timepicker-div dl { text-align: left; }
.ui-timepicker-div dl dt { float: left; clear:left; padding: 0 0 0 5px; }
.ui-timepicker-div dl dd { margin: 0 10px 10px 40%; }
.ui-timepicker-div td { font-size: 90%; }
.ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; }
.ui-timepicker-rtl{ direction: rtl; }
.ui-timepicker-rtl dl { text-align: right; padding: 0 5px 0 0; }
.ui-timepicker-rtl dl dt{ float: right; clear: right; }
.ui-timepicker-rtl dl dd { margin: 0 40% 10px 10px; }

View file

@ -0,0 +1,244 @@
/**
* QUnit v1.12.0 - A JavaScript Unit Testing Framework
*
* http://qunitjs.com
*
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
-webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-testrunner-toolbar label {
display: inline-block;
padding: 0 .5em 0 .1em;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
overflow: hidden;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
#qunit-modulefilter-container {
float: right;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
.qunit-collapsed {
display: none;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #3c510c;
background-color: #fff;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
#qunit-testresult .module-name {
font-weight: bold;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}

BIN
static/images/blue-pin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created with Inkscape (http://www.inkscape.org/) --><svg height="80.000000pt" id="svg1" inkscape:version="0.40" sodipodi:docbase="/home/nicu/Desktop/pins" sodipodi:docname="bluepin.svg" sodipodi:version="0.32" width="80.000000pt" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink">
<metadata>
<rdf:RDF xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<cc:Work rdf:about="">
<dc:title>pin - blue</dc:title>
<dc:description></dc:description>
<dc:subject>
<rdf:Bag>
<rdf:li>office</rdf:li>
<rdf:li></rdf:li>
</rdf:Bag>
</dc:subject>
<dc:publisher>
<cc:Agent rdf:about="http://www.openclipart.org">
<dc:title>Nicu Buculei</dc:title>
</cc:Agent>
</dc:publisher>
<dc:creator>
<cc:Agent>
<dc:title>Nicu Buculei</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title>Nicu Buculei</dc:title>
</cc:Agent>
</dc:rights>
<dc:date></dc:date>
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<cc:license rdf:resource="http://web.resource.org/cc/PublicDomain"/>
<dc:language>en</dc:language>
</cc:Work>
<cc:License rdf:about="http://web.resource.org/cc/PublicDomain">
<cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
<cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/>
<cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
</cc:License>
</rdf:RDF>
</metadata>
<defs id="defs3">
<linearGradient id="linearGradient4810">
<stop id="stop4811" offset="0.0000000" style="stop-color:#ffffff;stop-opacity:0.0000000;"/>
<stop id="stop4812" offset="1.0000000" style="stop-color:#ffffff;stop-opacity:0.68041235;"/>
</linearGradient>
<linearGradient id="linearGradient2931">
<stop id="stop2932" offset="0.0000000" style="stop-color:#ffffff;stop-opacity:1.0000000;"/>
<stop id="stop2933" offset="1.0000000" style="stop-color:#ffffff;stop-opacity:0.0000000;"/>
</linearGradient>
<linearGradient gradientTransform="scale(1.806421,0.553581)" gradientUnits="userSpaceOnUse" id="linearGradient5443" inkscape:collect="always" x1="145.63139" x2="145.63139" xlink:href="#linearGradient2931" y1="712.78033" y2="772.31848"/>
<radialGradient cx="124.36100" cy="1082.2069" fx="125.14262" fy="1084.0195" gradientTransform="scale(2.175919,0.459576)" gradientUnits="userSpaceOnUse" id="radialGradient5444" inkscape:collect="always" r="54.577564" xlink:href="#linearGradient4810"/>
</defs>
<sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="40.000000" inkscape:cy="40.000000" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="753" inkscape:window-width="958" inkscape:window-x="26" inkscape:window-y="25" inkscape:zoom="7.1625000" pagecolor="#ffffff"/>
<g id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1">
<g id="g8582">
<path d="M 263.75000,501.11218 L 267.50000,707.36218 L 278.75000,761.11218 L 286.25000,706.11218 L 282.50000,502.36218 L 263.75000,501.11218 z " id="path1684" sodipodi:nodetypes="cccccc" style="fill:#9e9d9b;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:4.8927894;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;" transform="matrix(0.228884,-0.113495,0.113495,0.228884,-68.50043,-44.07814)"/>
<path d="M 265.37352,538.01031 L 268.16112,707.31019 L 278.15032,752.49956 L 270.68998,537.71983 L 265.37352,538.01031 z " id="path4813" sodipodi:nodetypes="ccccc" style="fill:#ffffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;" transform="matrix(0.228884,-0.113495,0.113495,0.228884,-68.50043,-44.07814)"/>
<path d="M 47.853127,40.988541 C 56.075476,56.812966 64.270603,72.652676 72.495918,88.474884 C 75.387683,92.150116 83.137425,100.52810 82.532330,99.736721 C 81.066331,95.446317 79.849393,89.204867 78.337002,84.933957 C 70.273127,69.392997 62.377631,54.378767 54.317596,38.835824 C 52.452933,39.609814 49.713423,40.204129 47.853127,40.988541 z M 49.593395,41.622707 C 50.683882,41.168118 51.774370,40.713528 52.864857,40.258938 C 60.765236,55.502991 68.882678,69.332768 76.756478,84.588139 C 77.866557,87.881796 78.976635,92.222575 80.086713,95.516233 C 77.928546,92.728974 75.501521,90.351573 73.402278,87.526326 C 65.465984,72.225120 57.529690,56.923913 49.593395,41.622707 z " id="path6692" sodipodi:nodetypes="cccccccccccc" style="fill:#000000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:4.8927894;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"/>
<path d="M 266.25000,361.26166 C 215.88000,361.26166 175.00000,378.34505 175.00000,399.39423 C 175.00000,403.53049 176.74525,407.46554 179.68750,411.18066 C 181.43767,420.67385 183.55201,434.39840 183.31250,446.85194 C 164.43916,456.32637 152.90625,468.84725 152.90625,482.59255 C 152.90625,512.12465 205.92967,536.08211 271.25000,536.08214 C 336.57032,536.08214 389.59376,512.12466 389.59375,482.59255 C 389.59375,467.81002 376.14852,454.57147 354.62500,444.94531 C 351.71542,434.28130 350.02631,422.82075 351.87500,412.25530 C 355.39117,408.23677 357.50000,403.93193 357.50000,399.39423 C 357.50000,398.81798 357.34188,398.26562 357.28125,397.69559 C 357.36116,397.57026 357.41834,397.43882 357.50000,397.31427 C 357.44366,397.13929 357.27081,396.99722 357.18750,396.82895 C 354.00151,376.98464 314.53966,361.26166 266.25000,361.26166 z " id="path1061" style="fill:#0000d3;fill-opacity:1.0000000;stroke:none;stroke-width:4.8927894;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;" transform="matrix(0.228884,-0.113495,0.113495,0.228884,-68.50043,-44.07814)"/>
<path d="M 336.25000 417.36218 A 77.500000 23.750000 0 1 1 181.25000,417.36218 A 77.500000 23.750000 0 1 1 336.25000 417.36218 z" id="path2309" sodipodi:cx="258.75000" sodipodi:cy="417.36218" sodipodi:rx="77.500000" sodipodi:ry="23.750000" sodipodi:type="arc" style="fill:url(#linearGradient5443);fill-opacity:1.0000000;stroke:none;stroke-width:2.0521364;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;" transform="matrix(0.256132,-0.127006,0.153507,0.309576,-92.66068,-79.75977)"/>
<path d="M 348.54157,411.25942 C 326.15065,427.59874 289.69574,431.77062 257.71875,430.84375 C 241.73026,430.38032 226.55820,428.15413 214.25000,424.59375 C 201.94180,421.03337 190.72405,418.18039 186.29912,412.92579 L 184.37500,413.93750 C 189.95007,420.55790 200.09726,425.57601 212.90625,429.28125 C 225.71524,432.98649 241.23849,435.27593 257.59375,435.75000 C 290.30426,436.69813 326.32842,430.50443 350.18750,413.09375 L 348.54157,411.25942 z " id="path2935" sodipodi:nodetypes="ccccccccc" style="fill:#000000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:4.8927894;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;" transform="matrix(0.228884,-0.113495,0.113495,0.228884,-68.50043,-44.07814)"/>
<path d="M 351.45088,447.13671 C 332.11482,464.05577 298.58377,475.22314 267.15625,475.53125 C 235.72873,475.83936 204.38333,466.48027 186.34772,449.64703 L 185.16457,450.48423 C 204.62896,468.65099 234.89627,480.75439 267.21875,480.43750 C 299.54123,480.12061 331.89240,467.07926 352.55634,448.99832 L 351.45088,447.13671 z " id="path3557" sodipodi:nodetypes="ccccccc" style="fill:#000000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:4.8927894;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;" transform="matrix(0.228884,-0.113495,0.113495,0.228884,-68.50043,-44.07814)"/>
<path d="M 157.50000,487.36218 C 204.01668,522.54648 331.42487,527.56909 386.25000,482.36218 C 378.33176,545.59131 185.36558,554.84125 157.50000,487.36218 z " id="path4179" sodipodi:nodetypes="ccc" style="fill:url(#radialGradient5444);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;" transform="matrix(0.228884,-0.113495,0.113495,0.228884,-68.50043,-44.07814)"/>
<path d="M 33.318230,7.9252710 C 27.215884,11.044142 21.205920,15.038942 17.459858,20.916861 C 15.909772,23.341527 14.735120,28.452360 19.100117,29.562094 C 21.103833,30.646438 20.541915,32.411847 21.795175,34.234322 C 22.939480,35.958635 24.866038,37.092702 22.791698,38.750178 C 20.825671,41.934310 19.044130,47.092568 21.465680,50.387341 C 24.305049,54.044199 29.350987,54.417249 33.674482,54.296096 C 43.510136,53.782469 52.915078,50.093273 61.188863,44.917575 C 67.017920,41.080696 72.737539,36.400227 75.792011,29.971363 C 77.375191,26.756307 77.463894,22.323064 74.512347,19.846313 C 71.528442,17.299724 67.393066,16.903866 63.619015,16.987519 C 61.976677,14.783236 58.759800,12.489943 59.104992,9.5691842 C 59.411426,7.4278307 58.985334,5.3889748 57.202566,4.3823833 C 53.135243,2.2487783 48.275091,3.0623370 43.984172,3.9816103 C 40.290194,4.8714406 36.720836,6.2420975 33.318230,7.9252710 z M 33.875065,9.0482330 C 39.754580,6.2254347 46.216107,3.9323746 52.827216,4.3959927 C 55.250918,4.5884597 58.492905,5.7149230 58.598104,8.5748892 C 58.028039,10.364457 58.765097,12.064180 59.652578,13.610363 C 60.617852,15.220044 61.833115,16.576598 63.261167,17.777540 C 65.134419,17.993188 66.958374,18.117125 68.836337,18.602079 C 71.983508,19.092749 75.452803,21.186266 75.630017,24.692122 C 75.778639,29.281869 72.721638,33.694705 69.762569,36.904845 C 62.726075,44.072040 53.616500,48.844901 44.055597,51.752455 C 38.282749,53.350356 31.978353,54.163016 26.137532,52.246241 C 23.306342,51.377078 21.003388,48.628354 21.396869,45.566280 C 21.612112,42.644524 23.270903,40.083437 24.838068,37.736859 C 24.837824,36.153761 23.443240,34.843731 22.784323,33.444992 C 21.811946,31.989412 21.065589,30.284732 19.886800,28.996132 C 17.485785,28.162991 16.655344,25.408810 17.688898,23.282068 C 19.887216,18.285800 24.409437,14.813081 28.851983,11.877822 C 30.473423,10.844502 32.152697,9.9025167 33.875065,9.0482330 z " id="path6693" sodipodi:nodetypes="cccccccccccccccccccccccccccccccc" style="fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:4.8927894;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/fire.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
static/images/projector.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

158
static/images/projector.svg Normal file
View file

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30"
height="46"
id="svg7594"
sodipodi:version="0.32"
inkscape:version="0.46"
version="1.0"
sodipodi:docname="hw_board.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs7596" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="14.818182"
inkscape:cx="15.982682"
inkscape:cy="20.30673"
inkscape:document-units="px"
inkscape:current-layer="g11262"
showgrid="false"
inkscape:window-width="1680"
inkscape:window-height="1003"
inkscape:window-x="-4"
inkscape:window-y="-4"
showguides="true"
inkscape:guide-bbox="true" />
<metadata
id="metadata7599">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>hw_board</dc:title>
<dc:date>2008-05-15</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Jean Cartier</dc:title>
</cc:Agent>
</dc:creator>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
<dc:language>fr-FR</dc:language>
<dc:subject>
<rdf:Bag>
<rdf:li>hardware</rdf:li>
<rdf:li>computer</rdf:li>
<rdf:li>desk</rdf:li>
<rdf:li>business</rdf:li>
</rdf:Bag>
</dc:subject>
<dc:contributor>
<cc:Agent>
<dc:title>Jean-Victor Balin (jean.victor.balin@gmail.com)</dc:title>
</cc:Agent>
</dc:contributor>
<dc:description>http://www.jcartier.net</dc:description>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="opacity:1;fill:#888888;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d=""
id="path8460" />
<path
style="opacity:1;fill:#888888;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d=""
id="path8462" />
<path
style="opacity:1;fill:#888888;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d=""
id="path8464" />
<g
id="g11262"
transform="translate(-279.1135,-276.31506)">
<g
id="g9079">
<path
style="fill:#edd400;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4277"
sodipodi:nodetypes="cccccc"
d="M 305.1135,316.85015 L 281.1135,304.85015 L 281.1135,302.85015 L 281.12856,302.82044 L 305.1135,314.85015 L 305.1135,316.85015 z" />
<path
style="fill:#c4a000;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4273"
sodipodi:nodetypes="czzzz"
d="M 281.1135,302.85016 C 281.1135,301.85017 282.1135,301.85015 283.1135,301.85015 C 284.1135,301.85015 307.1135,313.28194 307.1135,313.85015 C 307.1135,314.41836 306.1135,314.85015 305.1135,314.85015 C 304.1135,314.85015 281.1135,303.85015 281.1135,302.85016 z" />
<path
style="fill:#fce94f;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4275"
d="M 307.1135,313.85015 L 307.1135,315.85015 L 305.1135,316.85015 L 305.1135,314.85015 L 307.1135,313.85015 z" />
<path
id="path6219"
d="M 283.125,301.34375 C 282.625,301.34375 282.09524,301.33284 281.59375,301.5 C 281.343,301.58358 281.09119,301.73097 280.90625,301.96875 C 280.72131,302.20653 280.625,302.51042 280.625,302.84375 C 280.625,303.84375 280.62501,303.84374 280.625,304.84375 C 280.625,305.11458 280.73624,305.2247 280.8125,305.3125 C 280.88876,305.4003 280.96438,305.45244 281.0625,305.53125 C 281.25873,305.68886 281.51944,305.85868 281.84375,306.0625 C 282.49237,306.47015 283.40886,307.00501 284.5,307.59375 C 286.68227,308.77123 289.58992,310.24813 292.53125,311.6875 C 295.47258,313.12687 298.41606,314.51298 300.71875,315.5625 C 301.87009,316.08726 302.86048,316.5374 303.59375,316.84375 C 303.96038,316.99693 304.2633,317.10187 304.5,317.1875 C 304.61835,317.23031 304.72198,317.2866 304.8125,317.3125 C 304.90302,317.3384 304.97917,317.34375 305.125,317.34375 C 305.625,317.34375 306.15476,317.35466 306.65625,317.1875 C 306.907,317.10392 307.15881,316.98778 307.34375,316.75 C 307.52869,316.51222 307.625,316.17708 307.625,315.84375 C 307.625,314.84375 307.62501,314.84374 307.625,313.84375 C 307.625,313.57292 307.51376,313.49405 307.4375,313.40625 C 307.36124,313.31845 307.25437,313.23506 307.15625,313.15625 C 306.96002,312.99864 306.69931,312.82882 306.375,312.625 C 305.72638,312.21735 304.84114,311.71374 303.75,311.125 C 301.56773,309.94752 298.66008,308.47062 295.71875,307.03125 C 292.77742,305.59188 289.80269,304.17452 287.5,303.125 C 286.34866,302.60024 285.35827,302.18135 284.625,301.875 C 284.25836,301.72182 283.95545,301.58563 283.71875,301.5 C 283.6004,301.45719 283.49677,301.43215 283.40625,301.40625 C 283.31573,301.38035 283.27084,301.34375 283.125,301.34375 z M 283.125,302.34375 C 283.04167,302.34375 283.06983,302.35921 283.125,302.375 C 283.18017,302.39079 283.26996,302.3995 283.375,302.4375 C 283.58508,302.51349 283.89203,302.63169 284.25,302.78125 C 284.96595,303.08037 285.91697,303.50914 287.0625,304.03125 C 289.35356,305.07548 292.34758,306.50187 295.28125,307.9375 C 298.21492,309.37313 301.11977,310.83373 303.28125,312 C 304.36199,312.58313 305.23065,313.08343 305.84375,313.46875 C 306.1503,313.66141 306.39008,313.82412 306.53125,313.9375 C 306.58419,313.98002 306.60398,314.01038 306.625,314.03125 C 306.625,314.7835 306.625,314.9375 306.625,315.84375 C 306.625,316.01041 306.59631,316.08153 306.5625,316.125 C 306.52869,316.16847 306.46801,316.20858 306.34375,316.25 C 306.09524,316.33284 305.625,316.34375 305.125,316.34375 C 305.20833,316.34375 305.14892,316.35954 305.09375,316.34375 C 305.03858,316.32796 304.94879,316.288 304.84375,316.25 C 304.63367,316.174 304.35797,316.05581 304,315.90625 C 303.28405,315.60713 302.30178,315.17836 301.15625,314.65625 C 298.86519,313.61202 295.90242,312.21688 292.96875,310.78125 C 290.03508,309.34562 287.13023,307.88502 284.96875,306.71875 C 283.88801,306.13562 282.9881,305.60407 282.375,305.21875 C 282.06845,305.02609 281.82867,304.86338 281.6875,304.75 C 281.64797,304.71825 281.64745,304.70854 281.625,304.6875 C 281.625,303.89841 281.625,303.76562 281.625,302.84375 C 281.625,302.67708 281.65369,302.63722 281.6875,302.59375 C 281.72131,302.55028 281.782,302.47892 281.90625,302.4375 C 282.15476,302.35466 282.625,302.34375 283.125,302.34375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccc"
id="path9069"
d="M 284.2086,277.50524 L 304.25155,287.56045 L 304.04909,313.06966 L 284.14111,303.2169 L 284.2086,277.50524 z"
style="fill:#edd400;fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4" />
<path
id="path4269"
d="M 284.40625,277.28125 C 284.30032,277.26783 284.18881,277.2693 284.03125,277.3125 C 283.95247,277.3341 283.84394,277.3678 283.75,277.46875 C 283.65606,277.5697 283.625,277.72396 283.625,277.84375 C 283.625,278.84375 283.62501,301.84378 283.625,302.84375 C 283.625,303.08333 283.712,303.19301 283.78125,303.28125 C 283.8505,303.36949 283.91245,303.42354 284,303.5 C 284.1751,303.65292 284.40433,303.84112 284.6875,304.03125 C 285.25384,304.41151 286.0564,304.87154 287,305.40625 C 288.8872,306.47566 291.36904,307.80952 293.875,309.0625 C 296.38096,310.31548 298.89194,311.4938 300.78125,312.3125 C 301.7259,312.72185 302.51771,313.02948 303.09375,313.21875 C 303.38177,313.31339 303.60063,313.37941 303.8125,313.40625 C 303.91843,313.41967 304.02994,313.44945 304.1875,313.40625 C 304.26628,313.38465 304.37481,313.3197 304.46875,313.21875 C 304.56269,313.1178 304.625,312.96354 304.625,312.84375 C 304.625,311.84375 304.62501,288.84378 304.625,287.84375 C 304.625,287.60417 304.50675,287.52574 304.4375,287.4375 C 304.36825,287.34926 304.3063,287.26396 304.21875,287.1875 C 304.04365,287.03458 303.81442,286.87763 303.53125,286.6875 C 302.96491,286.30724 302.1936,285.81596 301.25,285.28125 C 299.3628,284.21184 296.84971,282.90923 294.34375,281.65625 C 291.83779,280.40327 289.32681,279.22495 287.4375,278.40625 C 286.49285,277.9969 285.70104,277.65802 285.125,277.46875 C 284.83698,277.37412 284.61812,277.30809 284.40625,277.28125 z M 284.625,278.375 C 284.69989,278.39599 284.72017,278.40716 284.8125,278.4375 C 285.33021,278.6076 286.1009,278.90935 287.03125,279.3125 C 288.89195,280.1188 291.38096,281.31548 293.875,282.5625 C 296.36904,283.80952 298.8872,285.10066 300.75,286.15625 C 301.6814,286.68404 302.44134,287.14588 302.96875,287.5 C 303.23246,287.67706 303.44463,287.83456 303.5625,287.9375 C 303.60179,287.97181 303.60764,287.98161 303.625,288 C 303.625,289.23981 303.625,310.53023 303.625,312.34375 C 303.54164,312.32109 303.51404,312.31667 303.40625,312.28125 C 302.88854,312.11114 302.11785,311.8094 301.1875,311.40625 C 299.32681,310.59995 296.83779,309.40327 294.34375,308.15625 C 291.84971,306.90923 289.3628,305.58684 287.5,304.53125 C 286.5686,304.00346 285.77741,303.54162 285.25,303.1875 C 284.98629,303.01044 284.77412,302.85294 284.65625,302.75 C 284.6366,302.73284 284.63952,302.73275 284.625,302.71875 C 284.625,301.5318 284.625,280.23986 284.625,278.375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccc"
id="rect9054"
d="M 6.9509201,4.1595091 L 23.01227,12.122699 L 23.079754,33.852761 L 6.8159508,25.754602 L 6.9509201,4.1595091 z"
style="fill:#d3d7cf;fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4"
transform="translate(279.1135,276.31506)" />
<path
id="path4271"
d="M 286.3125,280.25 C 286.21212,280.24665 286.09177,280.27159 285.9375,280.34375 C 285.78323,280.41591 285.625,280.64583 285.625,280.84375 C 285.625,281.92814 285.62501,300.84378 285.625,301.84375 C 285.625,302.04167 285.68994,302.16185 285.75,302.25 C 285.81006,302.33815 285.86335,302.39707 285.9375,302.46875 C 286.0858,302.6121 286.2698,302.77307 286.5,302.9375 C 286.96041,303.26636 287.61754,303.6519 288.375,304.09375 C 289.88992,304.97745 291.86755,306.05877 293.875,307.0625 C 295.88245,308.06623 297.88804,308.99241 299.40625,309.625 C 300.16535,309.94129 300.80595,310.1767 301.28125,310.3125 C 301.5189,310.3804 301.7055,310.43081 301.90625,310.4375 C 302.00663,310.44085 302.12698,310.44716 302.28125,310.375 C 302.43552,310.30284 302.625,310.04167 302.625,309.84375 C 302.625,308.84375 302.62501,289.84378 302.625,288.84375 C 302.625,288.64583 302.52881,288.5569 302.46875,288.46875 C 302.40869,288.3806 302.3554,288.29043 302.28125,288.21875 C 302.13295,288.0754 301.94895,287.94568 301.71875,287.78125 C 301.25834,287.45239 300.63246,287.0356 299.875,286.59375 C 298.36008,285.71005 296.3512,284.65998 294.34375,283.65625 C 292.3363,282.65252 290.33071,281.69508 288.8125,281.0625 C 288.0534,280.74621 287.4128,280.5108 286.9375,280.375 C 286.69985,280.3071 286.51325,280.25669 286.3125,280.25 z M 286.625,281.34375 C 286.64007,281.34783 286.64058,281.33927 286.65625,281.34375 C 287.05595,281.45795 287.66535,281.69129 288.40625,282 C 289.88804,282.61741 291.88245,283.56623 293.875,284.5625 C 295.86755,285.55877 297.88992,286.60245 299.375,287.46875 C 300.11754,287.9019 300.71041,288.29761 301.125,288.59375 C 301.3323,288.74182 301.50767,288.85429 301.59375,288.9375 C 301.62024,288.9631 301.61381,288.98685 301.625,289 C 301.625,290.22815 301.625,307.64251 301.625,309.375 C 301.59302,309.36691 301.59777,309.35383 301.5625,309.34375 C 301.1628,309.22955 300.5534,308.99621 299.8125,308.6875 C 298.33071,308.07009 296.3363,307.15252 294.34375,306.15625 C 292.3512,305.15998 290.36008,304.08505 288.875,303.21875 C 288.13246,302.7856 287.50834,302.42114 287.09375,302.125 C 286.88645,301.97693 286.74233,301.83321 286.65625,301.75 C 286.6397,301.734 286.63577,301.7306 286.625,301.71875 C 286.625,300.54277 286.625,283.20004 286.625,281.34375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path4279"
d="M 294,281.34375 L 293.875,281.40625 L 291.875,282.40625 L 292.34375,283.3125 L 294.28125,282.34375 L 294.625,282.34375 L 294.625,283.4375 L 295.625,283.4375 L 295.625,281.84375 L 295.625,281.34375 L 295.125,281.34375 L 294.125,281.34375 L 294,281.34375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path4281"
d="M 293.625,311.84375 L 293.625,315.6875 L 290.6875,320.59375 L 291.53125,321.09375 L 294.53125,316.09375 L 294.625,316 L 294.625,315.84375 L 294.625,311.84375 L 293.625,311.84375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccccc"
id="path4283"
d="M 293.6875,316.09375 L 296.6875,321.09375 L 297.53125,320.59375 L 295.21875,316.71875 L 296.96875,317.3125 L 297.28125,316.375 L 294.28125,315.375 L 293.6875,316.09375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30"
height="46"
id="svg7594"
sodipodi:version="0.32"
inkscape:version="0.48.3.1 r9886"
version="1.0"
sodipodi:docname="projector2.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
inkscape:export-filename="/home/mcr/galaxy/orlando/r6743/static/images/projector2.png"
inkscape:export-xdpi="100"
inkscape:export-ydpi="100">
<defs
id="defs7596" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="14.818182"
inkscape:cx="16.117651"
inkscape:cy="20.30673"
inkscape:document-units="px"
inkscape:current-layer="g11262"
showgrid="false"
inkscape:window-width="1680"
inkscape:window-height="1003"
inkscape:window-x="-6"
inkscape:window-y="-6"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-maximized="0" />
<metadata
id="metadata7599">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:date>2008-05-15</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Jean Cartier</dc:title>
</cc:Agent>
</dc:creator>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
<dc:language>fr-FR</dc:language>
<dc:subject>
<rdf:Bag>
<rdf:li>hardware</rdf:li>
<rdf:li>computer</rdf:li>
<rdf:li>desk</rdf:li>
<rdf:li>business</rdf:li>
</rdf:Bag>
</dc:subject>
<dc:contributor>
<cc:Agent>
<dc:title>Jean-Victor Balin (jean.victor.balin@gmail.com)</dc:title>
</cc:Agent>
</dc:contributor>
<dc:description>http://www.jcartier.net</dc:description>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="opacity:1;fill:#888888;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d=""
id="path8460" />
<path
style="opacity:1;fill:#888888;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d=""
id="path8462" />
<path
style="opacity:1;fill:#888888;fill-opacity:1;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d=""
id="path8464" />
<g
id="g11262"
transform="translate(-279.1135,-276.31506)">
<g
id="g9079">
<path
style="fill:#edd400;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4277"
sodipodi:nodetypes="cccccc"
d="M 305.1135,316.85015 L 281.1135,304.85015 L 281.1135,302.85015 L 281.12856,302.82044 L 305.1135,314.85015 L 305.1135,316.85015 z" />
<path
style="fill:#c4a000;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4273"
sodipodi:nodetypes="czzzz"
d="M 281.1135,302.85016 C 281.1135,301.85017 282.1135,301.85015 283.1135,301.85015 C 284.1135,301.85015 307.1135,313.28194 307.1135,313.85015 C 307.1135,314.41836 306.1135,314.85015 305.1135,314.85015 C 304.1135,314.85015 281.1135,303.85015 281.1135,302.85016 z" />
<path
style="fill:#fce94f;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4275"
d="M 307.1135,313.85015 L 307.1135,315.85015 L 305.1135,316.85015 L 305.1135,314.85015 L 307.1135,313.85015 z" />
<path
id="path6219"
d="M 283.125,301.34375 C 282.625,301.34375 282.09524,301.33284 281.59375,301.5 C 281.343,301.58358 281.09119,301.73097 280.90625,301.96875 C 280.72131,302.20653 280.625,302.51042 280.625,302.84375 C 280.625,303.84375 280.62501,303.84374 280.625,304.84375 C 280.625,305.11458 280.73624,305.2247 280.8125,305.3125 C 280.88876,305.4003 280.96438,305.45244 281.0625,305.53125 C 281.25873,305.68886 281.51944,305.85868 281.84375,306.0625 C 282.49237,306.47015 283.40886,307.00501 284.5,307.59375 C 286.68227,308.77123 289.58992,310.24813 292.53125,311.6875 C 295.47258,313.12687 298.41606,314.51298 300.71875,315.5625 C 301.87009,316.08726 302.86048,316.5374 303.59375,316.84375 C 303.96038,316.99693 304.2633,317.10187 304.5,317.1875 C 304.61835,317.23031 304.72198,317.2866 304.8125,317.3125 C 304.90302,317.3384 304.97917,317.34375 305.125,317.34375 C 305.625,317.34375 306.15476,317.35466 306.65625,317.1875 C 306.907,317.10392 307.15881,316.98778 307.34375,316.75 C 307.52869,316.51222 307.625,316.17708 307.625,315.84375 C 307.625,314.84375 307.62501,314.84374 307.625,313.84375 C 307.625,313.57292 307.51376,313.49405 307.4375,313.40625 C 307.36124,313.31845 307.25437,313.23506 307.15625,313.15625 C 306.96002,312.99864 306.69931,312.82882 306.375,312.625 C 305.72638,312.21735 304.84114,311.71374 303.75,311.125 C 301.56773,309.94752 298.66008,308.47062 295.71875,307.03125 C 292.77742,305.59188 289.80269,304.17452 287.5,303.125 C 286.34866,302.60024 285.35827,302.18135 284.625,301.875 C 284.25836,301.72182 283.95545,301.58563 283.71875,301.5 C 283.6004,301.45719 283.49677,301.43215 283.40625,301.40625 C 283.31573,301.38035 283.27084,301.34375 283.125,301.34375 z M 283.125,302.34375 C 283.04167,302.34375 283.06983,302.35921 283.125,302.375 C 283.18017,302.39079 283.26996,302.3995 283.375,302.4375 C 283.58508,302.51349 283.89203,302.63169 284.25,302.78125 C 284.96595,303.08037 285.91697,303.50914 287.0625,304.03125 C 289.35356,305.07548 292.34758,306.50187 295.28125,307.9375 C 298.21492,309.37313 301.11977,310.83373 303.28125,312 C 304.36199,312.58313 305.23065,313.08343 305.84375,313.46875 C 306.1503,313.66141 306.39008,313.82412 306.53125,313.9375 C 306.58419,313.98002 306.60398,314.01038 306.625,314.03125 C 306.625,314.7835 306.625,314.9375 306.625,315.84375 C 306.625,316.01041 306.59631,316.08153 306.5625,316.125 C 306.52869,316.16847 306.46801,316.20858 306.34375,316.25 C 306.09524,316.33284 305.625,316.34375 305.125,316.34375 C 305.20833,316.34375 305.14892,316.35954 305.09375,316.34375 C 305.03858,316.32796 304.94879,316.288 304.84375,316.25 C 304.63367,316.174 304.35797,316.05581 304,315.90625 C 303.28405,315.60713 302.30178,315.17836 301.15625,314.65625 C 298.86519,313.61202 295.90242,312.21688 292.96875,310.78125 C 290.03508,309.34562 287.13023,307.88502 284.96875,306.71875 C 283.88801,306.13562 282.9881,305.60407 282.375,305.21875 C 282.06845,305.02609 281.82867,304.86338 281.6875,304.75 C 281.64797,304.71825 281.64745,304.70854 281.625,304.6875 C 281.625,303.89841 281.625,303.76562 281.625,302.84375 C 281.625,302.67708 281.65369,302.63722 281.6875,302.59375 C 281.72131,302.55028 281.782,302.47892 281.90625,302.4375 C 282.15476,302.35466 282.625,302.34375 283.125,302.34375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccc"
id="path9069"
d="M 284.2086,277.50524 L 304.25155,287.56045 L 304.04909,313.06966 L 284.14111,303.2169 L 284.2086,277.50524 z"
style="fill:#edd400;fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4" />
<path
id="path4269"
d="M 284.40625,277.28125 C 284.30032,277.26783 284.18881,277.2693 284.03125,277.3125 C 283.95247,277.3341 283.84394,277.3678 283.75,277.46875 C 283.65606,277.5697 283.625,277.72396 283.625,277.84375 C 283.625,278.84375 283.62501,301.84378 283.625,302.84375 C 283.625,303.08333 283.712,303.19301 283.78125,303.28125 C 283.8505,303.36949 283.91245,303.42354 284,303.5 C 284.1751,303.65292 284.40433,303.84112 284.6875,304.03125 C 285.25384,304.41151 286.0564,304.87154 287,305.40625 C 288.8872,306.47566 291.36904,307.80952 293.875,309.0625 C 296.38096,310.31548 298.89194,311.4938 300.78125,312.3125 C 301.7259,312.72185 302.51771,313.02948 303.09375,313.21875 C 303.38177,313.31339 303.60063,313.37941 303.8125,313.40625 C 303.91843,313.41967 304.02994,313.44945 304.1875,313.40625 C 304.26628,313.38465 304.37481,313.3197 304.46875,313.21875 C 304.56269,313.1178 304.625,312.96354 304.625,312.84375 C 304.625,311.84375 304.62501,288.84378 304.625,287.84375 C 304.625,287.60417 304.50675,287.52574 304.4375,287.4375 C 304.36825,287.34926 304.3063,287.26396 304.21875,287.1875 C 304.04365,287.03458 303.81442,286.87763 303.53125,286.6875 C 302.96491,286.30724 302.1936,285.81596 301.25,285.28125 C 299.3628,284.21184 296.84971,282.90923 294.34375,281.65625 C 291.83779,280.40327 289.32681,279.22495 287.4375,278.40625 C 286.49285,277.9969 285.70104,277.65802 285.125,277.46875 C 284.83698,277.37412 284.61812,277.30809 284.40625,277.28125 z M 284.625,278.375 C 284.69989,278.39599 284.72017,278.40716 284.8125,278.4375 C 285.33021,278.6076 286.1009,278.90935 287.03125,279.3125 C 288.89195,280.1188 291.38096,281.31548 293.875,282.5625 C 296.36904,283.80952 298.8872,285.10066 300.75,286.15625 C 301.6814,286.68404 302.44134,287.14588 302.96875,287.5 C 303.23246,287.67706 303.44463,287.83456 303.5625,287.9375 C 303.60179,287.97181 303.60764,287.98161 303.625,288 C 303.625,289.23981 303.625,310.53023 303.625,312.34375 C 303.54164,312.32109 303.51404,312.31667 303.40625,312.28125 C 302.88854,312.11114 302.11785,311.8094 301.1875,311.40625 C 299.32681,310.59995 296.83779,309.40327 294.34375,308.15625 C 291.84971,306.90923 289.3628,305.58684 287.5,304.53125 C 286.5686,304.00346 285.77741,303.54162 285.25,303.1875 C 284.98629,303.01044 284.77412,302.85294 284.65625,302.75 C 284.6366,302.73284 284.63952,302.73275 284.625,302.71875 C 284.625,301.5318 284.625,280.23986 284.625,278.375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccc"
id="rect9054"
d="M 6.9509201,4.1595091 L 23.01227,12.122699 L 23.079754,33.852761 L 6.8159508,25.754602 L 6.9509201,4.1595091 z"
style="fill:#d3d7cf;fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4"
transform="translate(279.1135,276.31506)" />
<path
id="path4271"
d="M 286.3125,280.25 C 286.21212,280.24665 286.09177,280.27159 285.9375,280.34375 C 285.78323,280.41591 285.625,280.64583 285.625,280.84375 C 285.625,281.92814 285.62501,300.84378 285.625,301.84375 C 285.625,302.04167 285.68994,302.16185 285.75,302.25 C 285.81006,302.33815 285.86335,302.39707 285.9375,302.46875 C 286.0858,302.6121 286.2698,302.77307 286.5,302.9375 C 286.96041,303.26636 287.61754,303.6519 288.375,304.09375 C 289.88992,304.97745 291.86755,306.05877 293.875,307.0625 C 295.88245,308.06623 297.88804,308.99241 299.40625,309.625 C 300.16535,309.94129 300.80595,310.1767 301.28125,310.3125 C 301.5189,310.3804 301.7055,310.43081 301.90625,310.4375 C 302.00663,310.44085 302.12698,310.44716 302.28125,310.375 C 302.43552,310.30284 302.625,310.04167 302.625,309.84375 C 302.625,308.84375 302.62501,289.84378 302.625,288.84375 C 302.625,288.64583 302.52881,288.5569 302.46875,288.46875 C 302.40869,288.3806 302.3554,288.29043 302.28125,288.21875 C 302.13295,288.0754 301.94895,287.94568 301.71875,287.78125 C 301.25834,287.45239 300.63246,287.0356 299.875,286.59375 C 298.36008,285.71005 296.3512,284.65998 294.34375,283.65625 C 292.3363,282.65252 290.33071,281.69508 288.8125,281.0625 C 288.0534,280.74621 287.4128,280.5108 286.9375,280.375 C 286.69985,280.3071 286.51325,280.25669 286.3125,280.25 z M 286.625,281.34375 C 286.64007,281.34783 286.64058,281.33927 286.65625,281.34375 C 287.05595,281.45795 287.66535,281.69129 288.40625,282 C 289.88804,282.61741 291.88245,283.56623 293.875,284.5625 C 295.86755,285.55877 297.88992,286.60245 299.375,287.46875 C 300.11754,287.9019 300.71041,288.29761 301.125,288.59375 C 301.3323,288.74182 301.50767,288.85429 301.59375,288.9375 C 301.62024,288.9631 301.61381,288.98685 301.625,289 C 301.625,290.22815 301.625,307.64251 301.625,309.375 C 301.59302,309.36691 301.59777,309.35383 301.5625,309.34375 C 301.1628,309.22955 300.5534,308.99621 299.8125,308.6875 C 298.33071,308.07009 296.3363,307.15252 294.34375,306.15625 C 292.3512,305.15998 290.36008,304.08505 288.875,303.21875 C 288.13246,302.7856 287.50834,302.42114 287.09375,302.125 C 286.88645,301.97693 286.74233,301.83321 286.65625,301.75 C 286.6397,301.734 286.63577,301.7306 286.625,301.71875 C 286.625,300.54277 286.625,283.20004 286.625,281.34375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path4279"
d="M 294,281.34375 L 293.875,281.40625 L 291.875,282.40625 L 292.34375,283.3125 L 294.28125,282.34375 L 294.625,282.34375 L 294.625,283.4375 L 295.625,283.4375 L 295.625,281.84375 L 295.625,281.34375 L 295.125,281.34375 L 294.125,281.34375 L 294,281.34375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path4281"
d="M 293.625,311.84375 L 293.625,315.6875 L 290.6875,320.59375 L 291.53125,321.09375 L 294.53125,316.09375 L 294.625,316 L 294.625,315.84375 L 294.625,311.84375 L 293.625,311.84375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccccc"
id="path4283"
d="M 293.6875,316.09375 L 296.6875,321.09375 L 297.53125,320.59375 L 295.21875,316.71875 L 296.96875,317.3125 L 297.28125,316.375 L 294.28125,315.375 L 293.6875,316.09375 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<text
xml:space="preserve"
style="font-size:22.81445312px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial Black;-inkscape-font-specification:Arial Black"
x="277.74319"
y="272.51889"
id="text2998"
sodipodi:linespacing="125%"
transform="matrix(1.0132707,0.11796914,0.02070387,0.98931351,0,0)"><tspan
sodipodi:role="line"
id="tspan3000"
x="277.74319"
y="272.51889">2</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -19,16 +19,21 @@
//////////////-GLOBALS----////////////////////////////////////////
var meeting_number = 0; // is the meeting name.
var schedule_id = 0; // what is the schedule we are editing.
var schedule_owner_href = ''; // who owns this schedule
var is_secretariat = false;
var meeting_objs = {}; // contains a list of session objects -- by session_id
var session_objs = {}; // contains a list of session objects -- by session_name
var slot_status = {}; // indexed by domid, contains an array of ScheduledSessions objects
var slot_objs = {}; // scheduledsession indexed by id.
// these need to be setup in landscape_edit's setup_slots() inline function:
//var meeting_number = 0; // is the meeting name.
//var schedule_id = 0; // what is the schedule we are editing.
//var schedule_name; // what is the schedule we are editing.
//var schedule_owner_href = ''; // who owns this schedule
//var scheduledsession_post_href;
//var meeting_base_url;
//var site_base_url;
//var total_rooms = 0; // the number of rooms
//var total_days = 0; // the number of days
var is_secretariat = false;
var agenda_globals;
var group_objs = {}; // list of working groups
var area_directors = {}; // list of promises of area directors, index by href.
var read_only = true; // it is true until we learn otherwise.
@ -47,9 +52,7 @@ var last_json_txt = ""; // last txt from a json call.
var last_json_reply = []; // last parsed content
var hidden_rooms = [];
var total_rooms = 0; // the number of rooms
var hidden_days = [];
var total_days = 0; // the number of days
/****************************************************/
@ -67,21 +70,38 @@ $(document).ready(function() {
This is ran at page load and sets up the entire page.
*/
function initStuff(){
agenda_globals = new AgendaGlobals();
//agenda_globals.__debug_session_move = true;
log("initstuff() running...");
setup_slots();
directorpromises = mark_area_directors();
var directorpromises = [];
/* define a slot for unscheduled items */
var unassigned = new ScheduledSlot();
unassigned.make_unassigned();
setup_slots(directorpromises);
mark_area_directors(directorpromises);
log("setup_slots() ran");
droppable();
log("droppable() ran");
$.when.apply($,directorpromises).done(function() {
/* can not load events until area director info has been loaded */
log("droppable() ran");
/* can not load events until area director info,
timeslots, sessions, and scheduledsessions
have been loaded
*/
log("loading/linking objects");
load_events();
log("load_events() ran");
find_meeting_no_room();
calculate_name_select_box();
calculate_room_select_box();
listeners();
droppable();
duplicate_sessions = find_double_timeslots();
empty_info_table();
count_sessions();
if(load_conflicts) {
recalculate(null);
@ -90,7 +110,6 @@ function initStuff(){
static_listeners();
log("listeners() ran");
calculate_name_select_box();
start_spin();
@ -99,7 +118,7 @@ function initStuff(){
read_only_check();
stop_spin();
meeting_objs_length = Object.keys(meeting_objs).length;
meeting_objs_length = Object.keys(agenda_globals.meeting_objs).length;
/* Comment this out for fast loading */
//load_conflicts = false;
@ -134,7 +153,8 @@ function read_only_result(msg) {
// XX go fetch the owner and display it.
console.log("owner href:", schedule_owner_href);
empty_info_table();
$("#pageloaded").show();
listeners();
droppable();
}

View file

@ -39,83 +39,25 @@ function log(text){
console.log(text);
}
/* move_slot
Moves a meeting(from) to a slot (to).
No checks are done to see if the slot it's moving to is free,
this can be considered a method of forcing a slot to a place.
@params:
'from' - meeting key (searching in meeting_objs[])
'to' - slot_status key (searching in slot_status[])
*/
var gfrom = null;
function move_slot(from,to){
console.log("!!!");
var meeting = meeting_objs[from];
var from_slot = meeting_objs[from].slot_status_key;
var to_slot = slot_status[to];
console.log(meeting_objs[from]);
console.log(from_slot);
var result = update_to_slot(from, to, true); // true if the job succeeded
if(result){
if(update_from_slot(from,from_slot)){
console.log("move_slot: success");
}else{
console.log("move_slot: fail");
}
}
meeting_objs[from].slot_status_key = to;
//***** do dajaxice call here ****** //
var eTemplate = meeting.event_template()
$("#session_"+from).remove();
$("#"+to).append(eTemplate);
var session_id = from;
var scheduledsession_id = slot_status[to].scheduledsession_id;
console.log(session_id);
console.log(scheduledsession_id);
// start_spin();
Dajaxice.ietf.meeting.update_timeslot(dajaxice_callback,
{
'schedule_id':schedule_id,
'session_id':session_id,
'scheduledsession_id': scheduledsession_id,
});
}
function print_all(){
console.log("all");
console.log(meeting_objs.length);
for(var i=0; i<meeting_objs.length; i++){
meeting_objs[i].print_out();
console.log(agenda_globals.meeting_objs.length);
for(var i=0; i<agenda_globals.meeting_objs.length; i++){
agenda_globals.meeting_objs[i].print_out();
}
}
function find_title(title){
$.each(meeting_objs, function(key){
if (meeting_objs[key].title == title) {
console.log(meeting_objs[key]);
$.each(agenda_globals.meeting_objs, function(key){
if (agenda_globals.meeting_objs[key].title == title) {
console.log(agenda_globals.meeting_objs[key]);
}
});
}
function find_session_id(session_id){
$.each(meeting_objs, function(key){
if (meeting_objs[key].session_id == session_id) {
console.log(meeting_objs[key]);
$.each(agenda_globals.meeting_objs, function(key){
if (agenda_globals.meeting_objs[key].session_id == session_id) {
console.log(agenda_globals.meeting_objs[key]);
}
});
}
@ -123,7 +65,7 @@ function find_session_id(session_id){
function find_same_area(area){
var areas = []
area = area.toUpperCase();
$.each(meeting_objs, function(index,obj){
$.each(agenda_globals.meeting_objs, function(index,obj){
if(obj.area == area){
areas.push({id:index,slot_status_key:obj.slot_status_key})
}
@ -138,6 +80,8 @@ function style_empty_slots(){
var __debug_load_events = false;
/* this pushes every event into the agendas */
function load_events(){
var slot_id;
console.log("load events...");
/* first delete all html items that might have gotten saved if
@ -146,17 +90,38 @@ function load_events(){
if(__debug_load_events) {
console.log("processing double slot status relations");
}
$.each(slot_status, function(key) {
ssid_arr = slot_status[key];
/* clear out all the timeslots */
$.each(agenda_globals.timeslot_bydomid, function(key) {
insert_cell(key, "", true);
var timeslot = agenda_globals.timeslot_bydomid[key];
slot_id = ("#"+key);
$(slot_id).addClass("agenda_slot_" + timeslot.roomtype);
if(timeslot.roomtype == "unavail") {
$(slot_id).removeClass("ui-droppable");
$(slot_id).removeClass("free_slot");
$(slot_id).addClass("agenda_slot_unavailable");
} else {
$(slot_id).removeClass("agenda_slot_unavailable");
$(slot_id).addClass("ui-droppable");
}
});
$.each(agenda_globals.slot_status, function(key) {
ssid_arr = agenda_globals.slot_status[key];
for(var q = 0; q<ssid_arr.length; q++){
ssid = ssid_arr[q];
insert_cell(ssid.domid, "", true);
ssid.connect_to_timeslot_session();
// also see if the slots have any declared relationship, and take it forward as
// well as backwards.
if(ssid.extendedfrom_id != false) {
other = slot_objs[ssid.extendedfrom_id];
other = agenda_globals.slot_objs[ssid.extendedfrom_id];
if(__debug_load_events) {
console.log("slot:",ssid.scheduledsession_id, "extended from: ",key,ssid.extendedfrom_id); // ," is: ", other);
}
@ -177,8 +142,8 @@ function load_events(){
if(__debug_load_events) {
console.log("marking extended slots for slots with multiple sessions");
}
$.each(slot_status, function(key) {
ssid_arr = slot_status[key];
$.each(agenda_globals.slot_status, function(key) {
ssid_arr = agenda_globals.slot_status[key];
var extendedto = undefined;
for(var q = 0; q<ssid_arr.length; q++){
@ -186,7 +151,7 @@ function load_events(){
if(extendedto == undefined &&
ssid.extendedto != undefined) {
if(__debug_load_events) {
console.log("ssid",ssid.session_id,"extended");
console.log("ssid",ssid.session_id,"extended 1");
}
extendedto = ssid.extendedto;
}
@ -195,7 +160,7 @@ function load_events(){
ssid = ssid_arr[q];
ssid.extendedto = extendedto;
if(__debug_load_events) {
console.log("ssid",ssid.session_id,"extended");
console.log("ssid",ssid.session_id,"extended 2");
}
}
});
@ -203,35 +168,26 @@ function load_events(){
if(__debug_load_events) {
console.log("finding responsible ad");
}
$.each(meeting_objs, function(key) {
session = meeting_objs[key];
$.each(agenda_globals.meeting_objs, function(key) {
session = agenda_globals.meeting_objs[key];
session.find_responsible_ad();
});
$.each(slot_status, function(key) {
ssid_arr = slot_status[key]
$.each(agenda_globals.slot_status, function(key) {
ssid_arr = agenda_globals.slot_status[key]
if(key == "sortable-list"){
console.log("sortable list");
}else {
for(var q = 0; q<ssid_arr.length; q++){
ssid = ssid_arr[q];
slot_id = ("#"+ssid.domid);
slot_id = ("#"+ssid.domid());
if(__debug_load_events) {
console.log("populating slot: ",slot_id,key);
}
/* also, since we are HERE, set the class to indicate if slot is available */
$(slot_id).addClass("agenda_slot_" + ssid.roomtype);
if(ssid.roomtype == "unavail") {
$(slot_id).removeClass("ui-droppable");
$(slot_id).removeClass("free_slot");
$(slot_id).addClass("agenda_slot_unavailable");
} else {
$(slot_id).removeClass("agenda_slot_unavailable");
$(slot_id).addClass("ui-droppable");
session = meeting_objs[ssid.session_id];
if(ssid.timeslot.roomtype != "unavail") {
session = agenda_globals.meeting_objs[ssid.session_id];
if (session != null) {
if(ssid.extendedto != undefined) {
session.double_wide = true;
@ -250,7 +206,7 @@ function load_events(){
session.populate_event(key);
}
session.placed(ssid, false);
session.placed(ssid.timeslot, false, ssid);
} else {
$(slot_id).addClass('free_slot');
}
@ -259,8 +215,8 @@ function load_events(){
}
});
$.each(meeting_objs, function(key) {
session = meeting_objs[key];
$.each(agenda_globals.meeting_objs, function(key) {
session = agenda_globals.meeting_objs[key];
// note in the group, what the set of column classes is.
// this is an array, as the group might have multiple
@ -278,18 +234,15 @@ function load_events(){
function check_free(inp){
var empty = false;
slot = slot_status[inp.id];
slot = agenda_globals.timeslot_bydomid[inp.id];
if(slot == null){
// console.log("\t from check_free, slot is null?", inp,inp.id, slot_status[inp.id]);
//console.log("\t from check_free, slot is null?", inp,inp.id, agenda_globals.slot_status[inp.id]);
return false;
}
for(var i =0;i<slot.length; i++){
if (slot[i].empty == false || slot[i].empty == "False"){
return false;
}
if (slot.empty == false) {
return false;
}
return true;
}
/* clears any background highlight colors of scheduled sessions */
@ -308,7 +261,7 @@ function clear_highlight(inp_arr){ // @args: array from slot_status{}
/* based on any meeting object, it finds any other objects inside the same timeslot. */
function find_friends(inp){
var ts = $(inp).parent().attr('id');
var ss_arr = slot_status[ts];
var ss_arr = agenda_globals.slot_status[ts];
if (ss_arr != null){
return ss_arr;
}
@ -320,7 +273,7 @@ function find_friends(inp){
function json_to_id(j){
return (j.room+"_"+j.date+"_"+j.time);
return (j.room()+"_"+j.date()+"_"+j.time());
}
function id_to_json(id){
@ -359,6 +312,10 @@ function empty_info_table(){
}
$("#info_responsible").html("");
$("#info_requestedby").html("");
$("#agenda_requested_features").html("");
/* need to reset listeners, because we just changed the HTML */
listeners();
}
@ -367,49 +324,83 @@ var temp_1;
takes in a json.
*/
function compare_timeslot(a,b) {
//console.log("day: a,b", a.day, b.day);
// sometimes (a.day==b.say)==false and (a.day===b.day)==false,
// for days that appear identical, but built from different strings,
// yet (a.day-b.day)==0.
if((a.day - b.day) == 0) {
//console.log("time: a,b", a.starttime, b.starttime);
if(a.starttime == b.starttime) {
//console.log("room: a,b", a.room, b.room, a.room < b.room);
if(a.room > b.room) {
return 1;
} else {
return -1;
}
};
if(a.starttime > b.starttime) {
return 1;
} else {
return -1;
}
}
if(a.day > b.day) {
return 1;
} else {
return -1;
}
}
var room_select_html = "";
function calculate_room_select_box() {
var html = "<select id='info_location_select'>";
var mobj_array = [];
var keys = Object.keys(slot_status)
var sorted = keys.sort(function(a,b) {
a1=slot_status[a];
b1=slot_status[b];
if (a1.date != b1.date) {
return a1.date-b1.date;
}
return a1.time - b1.time;
});
$.each(agenda_globals.timeslot_byid, function(key, value){
mobj_array.push(value)
});
for (n in sorted) {
var k1 = sorted[n];
var val_arr = slot_status[k1];
var sorted = mobj_array.sort(compare_timeslot);
var lastone_id = undefined;
/* k1 is the slot_status key */
/* v1 is the slot_obj */
for(var i = 0; i<val_arr.length; i++){
var v1 = val_arr[i];
html=html+"<option value='"+k1;
html=html+"' id='info_location_select_option_";
html=html+v1.timeslot_id+"'>";
html=html+v1.short_string;
html=html+"</option>";
}
}
$.each(sorted, function(index, value) {
// this check removes duplicates from the list, if there are any.
if(value.roomtype == "break" || value.roomtype=="reg") {
return;
}
if(value.timeslot_id == lastone_id) {
return; // from subfunction.
}
//console.log("room_select_html", index, value, value.short_string);
html=html+"<option value='"+value.timeslot_id;
html=html+"' id='info_location_select_option_";
html=html+value.timeslot_id+"'>";
html=html+value.short_string;
if(value.roomtype != "session") {
html = html+ "(" + value.roomtype + ")";
}
html=html+"</option>";
lastone_id = value.timeslot_id;
});
html = html+"</select>";
room_select_html = html;
return room_select_html;
}
var name_select_html = "";
var name_select_html = undefined;
var temp_sorted = null;
function calculate_name_select_box(){
var html = "<select id='info_name_select'>";
var keys = Object.keys(meeting_objs)
var mobj_array = []
$.each(meeting_objs, function(key, value){ mobj_array.push(value) });
var mobj_array = [];
var mobj_array2;
$.each(agenda_globals.meeting_objs, function(key, value){ mobj_array.push(value) });
mobj_array2 = mobj_array.sort(function(a,b) { return a.title.localeCompare(b.title); });
temp_sorted =mobj_array;
for(var i = 0; i < mobj_array.length; i++){
var mlen = mobj_array.length;
console.log("calculate name_select box with",mlen,"objects");
for(var i = 0; i < mlen; i++){
//console.log("select box mobj["+i+"]="+mobj_array[i]);
// html=html+"<option value='"+mobj_array[i].slot_status_key;
html=html+"<option value='"+mobj_array[i].session_id;
@ -435,13 +426,15 @@ function calculate_name_select_box(){
html = html+"</select>";
name_select_html = html;
return html;
}
function generate_select_box(){
if(!room_select_html) {
calculate_name_select_box();
}
return room_select_html;
}
@ -472,23 +465,10 @@ function insert_cell(js_room_id, text, replace){
}
function find_empty_test(){
$.each(slot_status, function(key){
ss_arr = slot_status[key];
for(var i = 0; i < ss_arr.length; i++){
if(ss_arr[i].scheduledsession_id == null || ss_arr[i].session_id == null){
console.log(ss_arr[i]);
}
}
})
}
function find_meeting_no_room(){
$.each(meeting_objs, function(key){
if(meeting_objs[key].slot_status_key == null) {
session = meeting_objs[key]
$.each(agenda_globals.meeting_objs, function(key){
if(agenda_globals.meeting_objs[key].slot_status_key == null) {
session = agenda_globals.meeting_objs[key]
session.slot_status_key = null;
session.populate_event(bucketlist_id);
}
@ -509,10 +489,10 @@ function find_meeting_no_room(){
function find_double_timeslots(){
var duplicate = {};
$.each(slot_status, function(key){
for(var i =0; i<slot_status[key].length; i++){
$.each(agenda_globals.slot_status, function(key){
for(var i =0; i<agenda_globals.slot_status[key].length; i++){
// goes threw all the slots
var ss_id = slot_status[key][i].session_id;
var ss_id = agenda_globals.slot_status[key][i].session_id;
if(duplicate[ss_id]){
duplicate[ss_id]['count']++;
duplicate[ss_id]['ts'].push(key);
@ -533,7 +513,6 @@ function find_double_timeslots(){
}
});
return dup;
}
@ -564,6 +543,7 @@ function auto_remove(){
}
/* for the spinnner */
/* spinner code from:
@ -629,13 +609,14 @@ function start_spin(opts){
// $("#schedule_name").hide();
$("#spinner").show();
$("#spinner").spin({lines:16, radius:8, length:16, width:4});
$("#pageloaded").hide();
}
function stop_spin(){
//spinner
$("#schedule_name").show();
$("#spinner").hide();
$("#spinner").spin(false);
$("#pageloaded").show();
}
/*

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -18,10 +18,7 @@
//////////////-GLOBALS----////////////////////////////////////////
var meeting_objs = {}; // contains a list of session objects
var slot_status = {}; // the status of the slot, in format { room_year-month-day_hour: { free: t/f, timeslotid: id } }
var slot_objs = {};
var group_objs = {}; // list of working groups
var agenda_globals;
var days = [];
var legend_status = {}; // agenda area colors.
@ -59,41 +56,102 @@ $(document).ready(function() {
This is ran at page load and sets up the entire page.
*/
function init_timeslot_edit(){
agenda_globals = new AgendaGlobals();
log("initstuff() ran");
setup_slots();
var directorpromises = [];
setup_slots(directorpromises);
log("setup_slots() ran");
fill_timeslots();
resize_listeners();
static_listeners();
$.when.apply($,directorpromises).done(function() {
fill_timeslots();
resize_listeners();
$(".delete_room").unbind('click');
$(".delete_room").click(delete_room);
static_listeners();
$("#add_room").unbind('click')
$("#add_room").click(add_room);
$(".delete_room").unbind('click');
$(".delete_room").click(delete_room);
$(".delete_slot").unbind('click');
$(".delete_slot").click(delete_slot);
$("#add_room").unbind('click')
$("#add_room").click(add_room);
$("#add_day").unbind('click')
$("#add_day").click(add_day);
$(".delete_slot").unbind('click');
$(".delete_slot").click(delete_slot);
$("#add_day").unbind('click')
$("#add_day").click(add_day);
console.log("timeslot editor ready");
});
/* datepicker stuff */
create_datetimepicker();
/* hide the django form stuff we don't need */
$("#id_duration").hide();
$("label[for*='id_duration']").hide();
$("#duration_time").val("01:00");
format_datetime();
$("#pageloaded").show();
}
function create_datetimepicker(){
$("#start_date").datepicker({
dateFormat: "yy-mm-dd",
});
$("#duration_time").timepicker({
timeFormat: 'HH:mm',
hourMin: 0,
hourMax: 8,
stepMinute:5,
defaultValue: "01:00",
onSelect: function(selected){
$("input[name*='duration_hours']").val($(this).val().split(':')[0]);
$("input[name*='duration_minutes']").val($(this).val().split(':')[1]);
format_datetime();
}
})
$("#id_time").datetimepicker({
timeFormat: 'HH:mm',
dateFormat: "yy-mm-dd",
defaultValue: first_day,
hourMin: 9,
hourMax: 22,
stepMinute:5,
onSelect: function(selected){
duration_set($(this).datetimepicker('getDate'));
format_datetime()
}
});
$("#id_time").datepicker('setDate', first_day);
}
function format_datetime(){
var startDate = $("#id_time").datetimepicker('getDate');
var endTime = $("#id_time").datetimepicker('getDate');
endTime.setHours(endTime.getHours()+parseInt($("#duration_time").val().split(':')[0]))
endTime.setMinutes(endTime.getMinutes()+parseInt($("#duration_time").val().split(':')[1]))
$("#timespan").html(moment($("#id_time").datetimepicker('getDate')).format('HH:mm') + " <-> " + moment(endTime).format('HH:mm'));
}
function duration_set(d){
$("input[name*='duration_hours']").val(d.getHours());
$("input[name*='duration_minutes']").val(d.getMinutes());
}
function add_room(event) {
event.preventDefault();
var rooms_url = $(event.target).attr('href');
$("#add_room_dialog").dialog({
"title" : "Add new room",
buttons : {
"Cancel" : function() {
$(this).dialog("close");
buttons : {
"Cancel" : function() {
$(this).dialog("close");
}
}
}
});
$("#room_delete_dialog").dialog("open");
$("#add_room_dialog").dialog("open");
}
function delete_room(event) {
@ -168,33 +226,29 @@ function delete_slot(event) {
}
function fill_timeslots() {
$.each(slot_status, function(key) {
ssid_arr = slot_status[key];
for(var q = 0; q<ssid_arr.length; q++){
ssid = ssid_arr[q];
insert_timeslotedit_cell(ssid);
}
// add no_timeslot class to all timeslots, it will be removed
// when an item is placed into the slot.
$(".agenda_slot").addClass("no_timeslot");
$.each(agenda_globals.timeslot_bydomid, function(key) {
ts = agenda_globals.timeslot_bydomid[key];
insert_timeslotedit_cell(ts);
});
// now add a create option for every slot which hasn't got a timeslot
$.each($(".no_timeslot"),function(slot) {
create_timeslotedit_cell(this);
});
}
function insert_timeslotedit_cell(ssid) {
var domid = ssid.domid
var roomtype=ssid.roomtype
var slot_id = ("#"+domid)
function build_select_box(roomtype, domid, slot_id, select_id) {
//console.log("updating for", ts);
roomtypesession="";
roomtypeother="";
roomtypeplenary="";
roomtypereserved="";
roomtypeclass="";
roomtypeunavailable="";
//console.log("domid: "+domid+" has roomtype: "+roomtype)
$(slot_id).removeClass("agenda_slot_unavailable")
$(slot_id).removeClass("agenda_slot_other")
$(slot_id).removeClass("agenda_slot_session")
$(slot_id).removeClass("agenda_slot_plenary")
$(slot_id).removeClass("agenda_slot_reserved")
if(roomtype == "session") {
roomtypesession="selected";
@ -213,7 +267,6 @@ function insert_timeslotedit_cell(ssid) {
roomtypeclass="agenda_slot_unavailable";
}
var select_id = domid + "_select"
html = "<form action=\"/some/place\" method=\"post\"><select id='"+select_id+"'>";
html = html + "<option value='session' "+roomtypesession+" id='option_"+domid+"_session'>session</option>";
html = html + "<option value='other' "+roomtypeother+" id='option_"+domid+"_other'>non-session</option>";
@ -222,9 +275,26 @@ function insert_timeslotedit_cell(ssid) {
html = html + "<option value='unavail' "+roomtypeunavailable+" id='option_"+domid+"_unavail'>unavailable</option>";
html = html + "</select>";
$(slot_id).html(html)
$(slot_id).addClass(roomtypeclass)
$(slot_id).addClass(roomtypeclass);
return roomtypeclass;
}
function insert_timeslotedit_cell(ts) {
var roomtype=ts.roomtype;
var domid =ts.domid;
var slot_id =("#" + domid);
$(slot_id).removeClass("agenda_slot_unavailable")
$(slot_id).removeClass("agenda_slot_other")
$(slot_id).removeClass("agenda_slot_session")
$(slot_id).removeClass("agenda_slot_plenary")
$(slot_id).removeClass("agenda_slot_reserved")
$(slot_id).removeClass("no_timeslot");
var select_id = domid + "_select";
var roomtypeclass = build_select_box(roomtype, domid, slot_id, select_id);
$("#"+select_id).change(function(eventObject) {
start_spin();
@ -239,18 +309,68 @@ function insert_timeslotedit_cell(ssid) {
} else {
stop_spin();
for(var key in json) {
ssid[key]=json[key];
ts[key]=json[key];
}
console.log("server replied, updating cell contents: "+ssid.roomtype);
insert_timeslotedit_cell(ssid);
console.log("server replied, updating cell contents: "+ts.roomtype);
insert_timeslotedit_cell(ts);
}
},
{
'timeslot_id': ssid.timeslot_id,
'meeting_num': meeting_number,
'timeslot_id': ts.timeslot_id,
'purpose': newpurpose,
});
});
}
var __debug_object;
function create_timeslotedit_cell(slot_id) {
var roomtype = "unavailable";
__debug_object = object;
var object = $(slot_id);
var room = object.attr('slot_room');
var time = object.attr('slot_time');
var duration=object.attr('slot_duration');
var domid= object.attr('id');
//$(slot_id).removeClass("agenda_slot_unavailable")
$(slot_id).removeClass("agenda_slot_other")
$(slot_id).removeClass("agenda_slot_session")
$(slot_id).removeClass("agenda_slot_plenary")
$(slot_id).removeClass("agenda_slot_reserved")
var select_id = domid + "_select";
var roomtypeclass = build_select_box(roomtype, "default", slot_id, select_id);
$("#"+select_id).change(function(eventObject) {
start_spin();
var newpurpose = $("#"+select_id).val()
console.log("creating setting id: #"+select_id+" to "+newpurpose+" ("+roomtypeclass+")");
Dajaxice.ietf.meeting.update_timeslot_purpose(
function(json) {
if(json == "") {
console.log("No reply from server....");
} else {
stop_spin();
for(var key in json) {
ts[key]=json[key];
}
console.log("server replied, updating cell contents: "+ts.roomtype);
insert_timeslotedit_cell(ts);
}
},
{
'timeslot_id': "0", /* 0 indicates to make a new one */
'meeting_num': meeting_number,
'room_id': room,
'time' : time,
'duration':duration,
'purpose': newpurpose,
});
});
}
/*

View file

@ -0,0 +1,91 @@
/*
* jQuery UI Slider Access
* By: Trent Richardson [http://trentrichardson.com]
* Version 0.3
* Last Modified: 10/20/2012
*
* Copyright 2011 Trent Richardson
* Dual licensed under the MIT and GPL licenses.
* http://trentrichardson.com/Impromptu/GPL-LICENSE.txt
* http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
*
*/
(function($){
$.fn.extend({
sliderAccess: function(options){
options = options || {};
options.touchonly = options.touchonly !== undefined? options.touchonly : true; // by default only show it if touch device
if(options.touchonly === true && !("ontouchend" in document)){
return $(this);
}
return $(this).each(function(i,obj){
var $t = $(this),
o = $.extend({},{
where: 'after',
step: $t.slider('option','step'),
upIcon: 'ui-icon-plus',
downIcon: 'ui-icon-minus',
text: false,
upText: '+',
downText: '-',
buttonset: true,
buttonsetTag: 'span',
isRTL: false
}, options),
$buttons = $('<'+ o.buttonsetTag +' class="ui-slider-access">'+
'<button data-icon="'+ o.downIcon +'" data-step="'+ (o.isRTL? o.step : o.step*-1) +'">'+ o.downText +'</button>'+
'<button data-icon="'+ o.upIcon +'" data-step="'+ (o.isRTL? o.step*-1 : o.step) +'">'+ o.upText +'</button>'+
'</'+ o.buttonsetTag +'>');
$buttons.children('button').each(function(j, jobj){
var $jt = $(this);
$jt.button({
text: o.text,
icons: { primary: $jt.data('icon') }
})
.click(function(e){
var step = $jt.data('step'),
curr = $t.slider('value'),
newval = curr += step*1,
minval = $t.slider('option','min'),
maxval = $t.slider('option','max'),
slidee = $t.slider("option", "slide") || function(){},
stope = $t.slider("option", "stop") || function(){};
e.preventDefault();
if(newval < minval || newval > maxval){
return;
}
$t.slider('value', newval);
slidee.call($t, null, { value: newval });
stope.call($t, null, { value: newval });
});
});
// before or after
$t[o.where]($buttons);
if(o.buttonset){
$buttons.removeClass('ui-corner-right').removeClass('ui-corner-left').buttonset();
$buttons.eq(0).addClass('ui-corner-left');
$buttons.eq(1).addClass('ui-corner-right');
}
// adjust the width so we don't break the original layout
var bOuterWidth = $buttons.css({
marginLeft: ((o.where === 'after' && !o.isRTL) || (o.where === 'before' && o.isRTL)? 10:0),
marginRight: ((o.where === 'before' && !o.isRTL) || (o.where === 'after' && o.isRTL)? 10:0)
}).outerWidth(true) + 5;
var tOuterWidth = $t.outerWidth(true);
$t.css('display','inline-block').width(tOuterWidth-bOuterWidth);
});
}
});
})(jQuery);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

6
static/js/moment.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,316 @@
// globals needed for tests cases.
var agenda_globals;
var scheduledsession_post_href = "/test/agenda_ui.html";
var read_only = false;
var days = [];
function reset_globals() {
// hack to reach in and manipulate global specifically.
window.agenda_globals = new AgendaGlobals();
}
function three_by_eight_grid() {
var rooms = ["apple", "orange", "grape", "pineapple",
"tomato","squash", "raisin","cucumber" ];
var times = [
{"time":"0900", "date":"2013-12-02"},
{"time":"1300", "date":"2013-12-02"},
{"time":"0900", "date":"2013-12-03"}
];
var slots = [{}];
var slotid= 1;
days.push("2013-12-02");
days.push("2013-12-03");
for(var roomkey in rooms) {
var room = rooms[roomkey];
for(var timekey in times) {
var time = times[timekey];
//console.log("data", room, time.date, time.time);
slot = make_timeslot({"timeslot_id": slotid,
"room" : room,
"roomtype" : "session",
"date" : time.date,
"time" : time.time,
"domid": "room" + roomkey + "_" + time.date + "_" + time.time
});
slots[slotid] = slot;
slotid += 1;
}
}
return slots;
}
function make_6_sessions() {
monarchs = ["henry", "george", "richard", "victoria", "william", "elizabeth"];
$.each(monarchs, function(index) {
monarch = monarchs[index];
console.log("monarch", monarch);
var group = create_group_by_href("http://localhost:8000/group/"+monarch+".json");
group.acronym = monarch;
group.name = "Royalty fun" + monarch;
group.type = "wg";
group.group_id = 1
});
var sessions = {};
var sessionid = 1;
monarch = "henry";
sessions[monarch] =
session_obj({"title": monarch,
"description": "Henry Beauclerc",
"session_id": sessionid,
"attendees": 50,
"short_name": monarch,
"comments": "Long Live the King!",
"special_request": "",
"requested_time": "2013-11-27",
"requested_by": "Pope Francis",
"requested_duration": "1.0",
"area" : "TSV",
"group_href": "http://localhost:8000/group/"+monarch+".json"
});
sessionid += 1;
monarch = "george";
sessions[monarch] =
session_obj({"title": monarch,
"description": "Georg Ludwig",
"session_id": sessionid,
"attendees": 60,
"short_name": monarch,
"comments": "Long Live the King!",
"special_request": "",
"requested_time": "2013-11-27",
"requested_by": "Pope Bacon",
"requested_duration": "1.5",
"area" : "SEC",
"group_href": "http://localhost:8000/group/"+monarch+".json"
});
sessionid += 1;
monarch = "richard";
sessions[monarch] =
session_obj({"title": monarch,
"description": "Richard the Lionheart",
"session_id": sessionid,
"attendees": 70,
"short_name": monarch,
"comments": "Lion Hart!",
"special_request": "",
"requested_time": "2013-11-27",
"requested_by": "Robin Hood",
"requested_duration": "2.0",
"area" : "RTG",
"group_href": "http://localhost:8000/group/"+monarch+".json"
});
sessionid += 1;
monarch = "victoria";
sessions[monarch] =
session_obj({"title": monarch,
"description": "the grandmother of Europe",
"session_id": sessionid,
"attendees": 80,
"short_name": monarch,
"comments": "Long Live the Queen!",
"special_request": "",
"requested_time": "2013-11-27",
"requested_by": "Docter Who",
"requested_duration": "1.0",
"area" : "INT",
"group_href": "http://localhost:8000/group/"+monarch+".json"
});
sessionid += 1;
monarch = "william";
sessions[monarch] =
session_obj({"title": monarch,
"description": "William the Conqueror",
"session_id": sessionid,
"attendees": 90,
"short_name": monarch,
"comments": "Just Married!",
"special_request": "",
"requested_time": "2013-11-27",
"requested_by": "Pope Francis",
"requested_duration": "2.5",
"area" : "RAI",
"group_href": "http://localhost:8000/group/"+monarch+".json"
});
sessionid += 1;
monarch = "elizabeth";
sessions[monarch] =
session_obj({"title": monarch,
"session_id": sessionid,
"description": "Head of the Commonwealth",
"attendees": 100,
"short_name": monarch,
"comments": "Long Live the Queen!",
"special_request": "",
"requested_time": "2013-11-27",
"requested_by": "Margaret Thatcher",
"requested_duration": "1.0",
"area" : "GEN",
"group_href": "http://localhost:8000/group/"+monarch+".json"
});
sessionid += 1;
return sessions;
}
function place_6_sessions(slots, sessions) {
var ss_id = 1;
make_ss({"scheduledsession_id": ss_id,
"timeslot_id": slots[3].timeslot_id,
"session_id": sessions["henry"].session_id});
ss_id += 1;
make_ss({"scheduledsession_id": ss_id,
"timeslot_id": slots[20].timeslot_id,
"session_id": sessions["george"].session_id});
ss_id += 1;
make_ss({"scheduledsession_id": ss_id,
"timeslot_id": slots[5].timeslot_id,
"session_id": sessions["richard"].session_id});
ss_id += 1;
make_ss({"scheduledsession_id": ss_id,
"timeslot_id": slots[9].timeslot_id,
"session_id": sessions["victoria"].session_id});
ss_id += 1;
make_ss({"scheduledsession_id": ss_id,
"timeslot_id": slots[13].timeslot_id,
"session_id": sessions["william"].session_id});
// last session is unscheduled.
}
function conflict_4_sessions(sessions) {
// fill in session constraints
$.each(sessions, function(index) {
var session = sessions[index];
var deferred = $.Deferred();
session.constraints_promise = deferred;
// $.ajax has a success option.
deferred.success = function(func) {
deferred.done(function(obj) {
func(obj, "success", {});
});
};
deferred.resolve({});
session.fill_in_constraints([]);
find_and_populate_conflicts(session);
});
sessions["henry"].fill_in_constraints([
{ "constraint_id": 21046,
"href": "http://localhost:8000/meeting/83/constraint/21046.json",
"meeting_href": "http://localhost:8000/meeting/83.json",
"name": "conflict",
"source_href": "http://localhost:8000/group/henry.json",
"target_href": "http://localhost:8000/group/george.json"
},
{ "constraint_id": 21047,
"href": "http://localhost:8000/meeting/83/constraint/21047.json",
"meeting_href": "http://localhost:8000/meeting/83.json",
"name": "conflic2",
"source_href": "http://localhost:8000/group/henry.json",
"target_href": "http://localhost:8000/group/richard.json"
}]);
find_and_populate_conflicts(sessions["henry"]);
}
function full_83_setup() {
reset_globals();
scheduledsession_post_href = "/meeting/83/schedule/mtg_83/sessions.json";
var ts_promise = load_timeslots("/meeting/83/timeslots.json");
var session_promise = load_sessions("/meeting/83/sessions.json");
var ss_promise = load_scheduledsessions(ts_promise, session_promise,
scheduledsession_post_href)
return ss_promise;
}
function henry_setup(sessions) {
reset_globals();
/* define a slot for unscheduled items */
var unassigned = new ScheduledSlot();
unassigned.make_unassigned();
t_slots = three_by_eight_grid();
t_sessions = make_6_sessions();
place_6_sessions(t_slots, t_sessions);
conflict_4_sessions(t_sessions);
load_events();
var henry0 = agenda_globals.sessions_objs["henry"];
var henry = henry0[0];
return henry;
}
var ss_id_next = 999;
function mock_scheduledslot_id(json) {
if(json.scheduledsession_id == undefined) {
console.log("adding scheduledsession_id to answer", ss_id_next);
ss_id_next += 1;
json.scheduledsession_id = ss_id_next;
}
};
ScheduledSlot.prototype.initialize = function(json) {
mock_scheduledslot_id(json);
this.real_initialize(json);
}
function mock_ui_draggable() {
// mock up the ui object.
var ui = new Object();
ui.draggable = new Object();
ui.draggable.remove = function() { return true; };
return ui;
}
function mock_dom_obj(domid) {
// mock up the dom object
var dom_obj = "#" + domid;
// in the unit tests, the object won't exist, so make it.
// when testing this test code, it might already be there
if($(dom_obj).length == 0) {
var div = document.createElement("div");
div.innerHTML = "welcome";
div.id = dom_obj;
}
return dom_obj;
}
function richard_move() {
var richard0 = agenda_globals.sessions_objs["richard"];
var richard = richard0[0];
var ui = mock_ui_draggable();
var dom_obj = mock_dom_obj(t_slots[4].domid);
/* current situation was tested in above test, so go ahead */
/* and move "richard" to another slot */
move_slot({"session": richard,
"to_slot_id": t_slots[4].domid,
"to_slot": t_slots[4],
"from_slot_id":t_slots[5].domid,
"from_slot": [t_slots[5]],
"bucket_list": false,
"ui": ui,
"dom_obj": dom_obj,
"force": true});
return richard;
}

View file

@ -0,0 +1,370 @@
test( "hello test", function() {
ok( 1 == "1", "Passed!" );
});
test( "TimeSlot Create test", function() {
reset_globals();
var nts = make_timeslot({"timeslot_id":"123",
"room" :"Regency A",
"time" :"0900",
"date" :"2013-11-04",
"domid" :"regencya_2013-11-04_0900"});
equal(nts.slot_title(), "id#123 dom:regencya_2013-11-04_0900", "slot_title correct");
});
asyncTest("Load Timeslots", function() {
reset_globals();
expect( 1 ); // expect one assertion.
var ts_promise = load_timeslots("/meeting/83/timeslots.json");
ts_promise.done(function() {
equal(Object.keys(agenda_globals.timeslot_byid).length, 179, "179 timeslots loaded");
start();
});
});
asyncTest("Load Sessions", function() {
reset_globals();
expect( 1 ); // expect one assertion.
var session_promise = load_sessions("/meeting/83/sessions.json");
session_promise.done(function() {
equal(Object.keys(agenda_globals.meeting_objs).length, 145, "145 sessions loaded");
start();
});
});
asyncTest("Load ScheduledSlot (ticket 1210)", function() {
expect( 1 ); // expect one assertion.
var ss_promise = full_83_setup();
ss_promise.done(function() {
equal(Object.keys(agenda_globals.slot_objs).length, 148, "148 scheduled sessions loaded");
start();
});
});
asyncTest( "move a session using the API (ticket 1211)", function() {
expect(4);
var ss_promise = full_83_setup();
ss_promise.done(function() {
equal(Object.keys(agenda_globals.slot_objs).length, 148, "148 scheduled sessions loaded");
// now move a session.. like selenium test, move forced from Monday to Friday:
// monday_room_253 = is #room208_2012-03-26_1510
// friday_room_252A = is #room209_2012-03-30_1230
var forces_list = agenda_globals.sessions_objs["forces"];
var forces = forces_list[0];
var from_slot_id = "room208_2012-03-26_1510";
var from_slot = agenda_globals.timeslot_bydomid[from_slot_id];
var to_slot_id = "room209_2012-03-30_1230";
var to_slot = agenda_globals.timeslot_bydomid[to_slot_id];
var ui = mock_ui_draggable();
var dom_obj = "#" + to_slot_id;
/* current situation was tested in above test, so go ahead */
/* and move "richard" to another slot */
var move_promise = move_slot({"session": forces,
"to_slot_id": to_slot_id,
"to_slot": to_slot,
"from_slot_id":from_slot_id,
"from_slot": [from_slot],
"bucket_list": false,
"ui": ui,
"dom_obj": dom_obj,
"force": true});
notEqual(move_promise, undefined);
if(move_promise != undefined) {
// now we need to check that it is all been done.
move_promise.done(function() {
// see that the placed is right.
equal(forces.slot.domid, to_slot_id);
// now move the item back again.
var return_promise = move_slot({"session": forces,
"to_slot_id": from_slot_id,
"to_slot": from_slot,
"from_slot_id":to_slot_id,
"from_slot": [to_slot],
"bucket_list": false,
"ui": ui,
"dom_obj": dom_obj,
"force": true});
return_promise.done(function() {
// see that the placed is right.
equal(forces.slot.domid, from_slot_id);
start();
});
});
} else {
// it is not legitimate to wind up here, but it does
// keep the test cases from hanging.
start();
}
});
});
test( "3x8 grid create (ticket 1212 - part 1)", function() {
expect(0); // just make sure things run without error
reset_globals();
t_slots = three_by_eight_grid();
t_sessions = make_6_sessions();
place_6_sessions(t_slots, t_sessions);
});
test( "calculate conflict columns for henry (ticket 1212 - part 2)", function() {
expect(10);
scheduledsession_post_href = "/test/agenda_ui.html";
var henry = henry_setup();
equal(henry.session_id, 1);
equal(henry.column_class_list.length, 1);
equal(henry.column_class_list[0].room, "apple");
equal(henry.column_class_list[0].time, "0900");
equal(henry.column_class_list[0].date, "2013-12-03");
equal(henry.conflicts.length, 2);
var conflict0 = henry.conflicts[0];
equal(conflict0.conflict_groupP(), true);
var classes = conflict0.column_class_list();
var cc00 = classes[0];
equal(cc00.th_tag, ".day_2013-12-02-1300");
var conflict1 = henry.conflicts[1];
equal(conflict1.conflict_groupP(), true);
var classes = conflict1.column_class_list();
var cc10 = classes[0];
equal(cc10.th_tag, ".day_2013-12-02-1300");
});
test( "re-calculate conflict columns for henry (ticket 1213)", function() {
expect(5);
reset_globals();
scheduledsession_post_href = "/test/agenda_ui.html";
agenda_globals.__debug_session_move = true;
var henry = henry_setup();
equal(henry.session_id, 1);
var richard = richard_move();
var conflict0 = henry.conflicts[0];
equal(conflict0.conflict_groupP(), true);
var classes = conflict0.column_class_list();
var cc00 = classes[0];
equal(cc00.th_tag, ".day_2013-12-02-1300");
var conflict1 = henry.conflicts[1];
equal(conflict1.conflict_groupP(), true);
var classes = conflict1.column_class_list();
var cc10 = classes[0];
equal(cc10.th_tag, ".day_2013-12-02-0900");
});
test( "build WG template for regular group (ticket #1135)", function() {
reset_globals();
var nts = make_timeslot({"timeslot_id":"123",
"room" :"Regency A",
"time" :"0900",
"date" :"2013-11-04",
"domid" :"regencya_2013-11-04_0900"});
// this is from http://localhost:8000/meeting/83/session/2157.json
var group1 = session_obj(
{
"agenda_note": "",
"area": "SEC",
"attendees": "45",
"bof": "False",
"comments": "please, no evening sessions.",
"description": "Public-Key Infrastructure (X.509)",
"group": {
"acronym": "pkix",
"ad_href": "http://localhost:8000/person/19483.json",
"comments": "1st met, 34th IETF Dallas, TX (December 4-8, 1995)",
"href": "http://localhost:8000/group/pkix.json",
"list_archive": "http://www.ietf.org/mail-archive/web/pkix/",
"list_email": "pkix@ietf.org",
"list_subscribe": "pkix-request@ietf.org",
"name": "Public-Key Infrastructure (X.509)",
"parent_href": "http://localhost:8000/group/sec.json",
"state": "active",
"type": "wg"
},
"group_acronym": "pkix",
"group_href": "http://localhost:8000/group/pkix.json",
"group_id": "1223",
"href": "http://localhost:8000/meeting/83/session/2157.json",
"name": "",
"requested_by": "Stephen Kent",
"requested_duration": "2.0",
"requested_time": "2011-12-19",
"session_id": "2157",
"short_name": "pkix",
"special_request": "*",
"status": "Scheduled",
"title": "pkix"
});
// validate that the session id is there as a basic check.
ok(group1.event_template().search(/meeting_box_container/) > 0);
ok(group1.event_template().search(/session_2157/) > 0);
ok(group1.event_template().search(/wg_style /) > 0);
});
test( "build WG template for BOF group (ticket #1135)", function() {
reset_globals();
// this is from http://localhost:8000/meeting/83/session/2157.json
var group1 = session_obj(
{
"agenda_note": "",
"area": "GEN",
"attendees": "50",
"bof": "True",
"comments": "",
"description": "RFC Format",
"group": {
"acronym": "rfcform",
"ad_href": "http://localhost:8000/person/5376.json",
"comments": "",
"href": "http://localhost:8000/group/rfcform.json",
"list_archive": "",
"list_email": "",
"list_subscribe": "",
"name": "RFC Format",
"parent_href": "http://localhost:8000/group/gen.json",
"state": "bof",
"type": "wg"
},
"group_acronym": "rfcform",
"group_href": "http://localhost:8000/group/rfcform.json",
"group_id": "1845",
"href": "http://localhost:8000/meeting/83/session/22081.json",
"name": "",
"requested_by": "Wanda Lo",
"requested_duration": "1.0",
"requested_time": "2012-02-27",
"session_id": "22081",
"short_name": "rfcform",
"special_request": "",
"status": "Scheduled",
"title": "rfcform"
}
);
// validate that the session id is there as a basic check.
ok(group1.event_template().search(/meeting_box_container/) > 0);
ok(group1.event_template().search(/session_22081/) > 0);
ok(group1.event_template().search(/bof_style /) > 0);
});
test( "compare timeslots sanely (ticket #1135)", function() {
var timeSlotA = {"timeslot_id":2383,
"room":"243",
"day":"2012-03-26T00:00:00.000Z",
"starttime":1300};
var timeSlotB = {"timeslot_id":2389,
"room":"241",
"day":"2012-03-26T00:00:00.000Z",
"starttime":900};
var timeSlotC = {"timeslot_id":2381,
"room":"245A",
"day":"2012-03-26T00:00:00.000Z",
"starttime":1300};
var timeSlotD = {"timeslot_id":2382,
"room":"245A",
"day":"2012-03-27T00:00:00.000Z",
"starttime":1510};
// three have the same day
ok(timeSlotA.day == timeSlotB.day);
ok(timeSlotA.day == timeSlotC.day);
ok(timeSlotA.day < timeSlotD.day);
// two have the same starttime
ok(timeSlotA.starttime == timeSlotC.starttime);
// canonical order is B, A, C, D.
equal(compare_timeslot(timeSlotB, timeSlotA), -1, "B < A");
equal(compare_timeslot(timeSlotA, timeSlotC), -1, "A < C");
equal(compare_timeslot(timeSlotC, timeSlotD), -1, "C < D");
equal(compare_timeslot(timeSlotB, timeSlotD), -1, "B < D");
equal(compare_timeslot(timeSlotA, timeSlotD), -1, "A < D");
});
asyncTest( "calculate info_room_select box (ticket 1220/1214)", function() {
expect(3);
var ss_promise = full_83_setup();
ss_promise.done(function() {
var box = calculate_room_select_box();
// this is a box which has no session, and therefore no ss.
// validate that calculate_name_select_box() provides all the timeslots
ok(box.search(/Mon, 1510, Maillot/) > 0);
ok(box.search(/undefined/) == -1);
// this one crept in: it is breakfast!
ok(box.search(/Mon, 0800, Halle Maillot A/) == -1);
start();
});
});
asyncTest( "calculate info_group_select box (ticket 1214)", function() {
expect(1);
var ss_promise = full_83_setup();
ss_promise.done(function() {
var box = calculate_name_select_box();
// the list of all of the groups.
// count the number of occurances of value=
var count = 0;
var valueloc = box.search(/value=/);
while(valueloc != -1) {
//console.log(count, "valueat",valueloc, "box contains", box);
count += 1;
// eat everything upto value=, and then a bit.
box = box.substring(valueloc+1);
valueloc = box.search(/value=/);
}
// 145 WG and other requests that "can meet"
equal(count, 145);
start();
});
});
asyncTest( "look for an empty slot(ticket 1215)", function() {
expect(1);
var ss_promise = full_83_setup();
ss_promise.done(function() {
target_session = agenda_globals.sessions_objs["pcp"][0];
ok(find_empty_slot(target_session) != null);
start();
});
});

View file

@ -490,6 +490,10 @@ tr.break td {
border-top: 2px solid black;
}
#id_schedule_selector {
display: inline;
}
/* ==========================================================================
Proceedings Tool
========================================================================== */
@ -608,6 +612,11 @@ tr.bg2 {
background: #EEEEEE;
}
tr.bg3 {
background: #DDDDDD;
}
/*
table#sessions-new-table td {
padding: 2px;

View file

@ -59,7 +59,7 @@ $(function() {
'</ul>'
$('.inline-group').each(function(i) {
//prefix is in the name of the input fields before the "-"
var prefix = $("input[type='hidden']", this).attr("name").split("-")[0]
var prefix = $("input[type='hidden'][name!='csrfmiddlewaretoken']", this).attr("name").split("-")[0]
$(this).append(html_template.replace("{{prefix}}", prefix))
})
})

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Agenda JS Unit Testing</title>
<link rel="stylesheet" href="/css/lib/qunit-1.12.0.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="/js/lib/qunit-1.12.0.js"></script>
<script type="text/javascript" src="/js/lib/jquery-1.8.2.min.js"></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery-ui.custom.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.widget.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.droppable.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.sortable.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.accordion.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.draggable.min.js'></script>
<script type='text/javascript' src='/js/spin/dist/spin.min.js'></script>
<script src="/js/agenda/agenda_objects.js"></script>
<script src="/js/agenda/agenda_helpers.js"></script>
<script src="/js/agenda/agenda_listeners.js"></script>
<script src="/js/test/agenda_funcs.js"></script>
<script src="/js/test/agenda_tests.js"></script>
</body>
</html>

603
static/test/agenda_ui.html Normal file
View file

@ -0,0 +1,603 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Agenda JS Unit Testing</title>
<link rel="stylesheet" type="text/css" href="/css/yui/yui-20100305.css"></link>
<link rel="stylesheet" type="text/css" href="/css/base2.css"></link>
<style type="text/css">
.APP-scheme, .meeting_event th.APP-scheme, #APP-groups, #selector-APP { color:#008; background-color: #eef }
.director-mark-APP {
border: 2px solid #008;
color:#008;
background-color: #eef
}
.GEN-scheme, .meeting_event th.GEN-scheme, #GEN-groups, #selector-GEN { color:#080; background-color: #efe }
.director-mark-GEN {
border: 2px solid #080;
color:#080;
background-color: #efe
}
.INT-scheme, .meeting_event th.INT-scheme, #INT-groups, #selector-INT { color:#088; background-color: #eff }
.director-mark-INT {
border: 2px solid #088;
color:#088;
background-color: #eff
}
.IRTF-scheme, .meeting_event th.IRTF-scheme, #IRTF-groups, #selector-IRTF { color:#448; background-color: #ddf }
.director-mark-IRTF {
border: 2px solid #448;
color:#448;
background-color: #ddf
}
.OPS-scheme, .meeting_event th.OPS-scheme, #OPS-groups, #selector-OPS { color:#800; background-color: #fee }
.director-mark-OPS {
border: 2px solid #800;
color:#800;
background-color: #fee
}
.RAI-scheme, .meeting_event th.RAI-scheme, #RAI-groups, #selector-RAI { color:#808; background-color: #fef }
.director-mark-RAI {
border: 2px solid #808;
color:#808;
background-color: #fef
}
.RTG-scheme, .meeting_event th.RTG-scheme, #RTG-groups, #selector-RTG { color:#880; background-color: #ffe }
.director-mark-RTG {
border: 2px solid #880;
color:#880;
background-color: #ffe
}
.SEC-scheme, .meeting_event th.SEC-scheme, #SEC-groups, #selector-SEC { color:#488; background-color: #dff }
.director-mark-SEC {
border: 2px solid #488;
color:#488;
background-color: #dff
}
.TSV-scheme, .meeting_event th.TSV-scheme, #TSV-groups, #selector-TSV { color:#484; background-color: #dfd }
.director-mark-TSV {
border: 2px solid #484;
color:#484;
background-color: #dfd
}
</style>
<link rel='stylesheet' type='text/css' href='/css/jquery-ui-themes/jquery-ui-1.8.11.custom.css' />
<link rel="stylesheet" type="text/css" href="/css/base2.css"></link>
<link rel='stylesheet' type='text/css' href='/css/agenda.css' />
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script type="text/javascript" src="/js/lib/jquery-1.8.2.min.js"></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery-ui.custom.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.widget.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.droppable.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.sortable.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.accordion.min.js'></script>
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.draggable.min.js'></script>
<script type='text/javascript' src='/js/spin/dist/spin.min.js'></script>
<div id="unassigned-items">
<div id="all_agendas" class="events_bar_buttons">
<a href="/meeting/83/agendas/edit">
<button class="styled_button">all agendas</button>
</a>
</div>
<div id="hidden_room" class="hide_buttons events_bar_buttons">
<div class="very_small left">hidden rooms:<span id="hidden_rooms" >0/11</span></div>
<div><button class="small_button" id="show_hidden_rooms">Show</button></div>
</div>
<div id="hidden_day" class="hide_buttons events_bar_buttons">
<div class="very_small left">hidden days:<span id="hidden_days" >0/7</span></div>
<div><button class="small_button" id="show_hidden_days">Show</button></div>
</div>
</div>
<div id="unassigned_order" class="events_bar_buttons">
<select id="unassigned_sort_button" class="dialog">
<option id="unassigned_alpha" value="alphaname" selected>Alphabetical</option>
<option id="unassigned_area" value="area">By Area</option>
<option id="unassigned_duration" value="duration">By Duration</option>
<option id="unassigned_special" value="special">Special Request</option>
</select>
</div>
<div class="agenda_slot_title" >
<div style="ui-icon ui-icon-arrow-1-w" id="close_ietf_menubar">
&lt;
</div>
<b>Unassigned Events:</b>
<span id="schedule_name">name: mtg_83</span>
</div>
<div id="sortable-list" class="ui-droppable bucket-list room_title">
</div>
</div>
<div class="agenda_div">
<div id="dialog-confirm" title="" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
Are you sure you want to put two sessions into the same slot?
</p>
</div>
<div id="can-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
</div>
<div id="can-not-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
You can not extend this session. The slot is not available.
</p>
</div>
<div id="dialog-confirm" title="" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
Are you sure you want to put two sessions into the same slot?
</p>
</div>
<table id="meetings" class="ietf-navbar" style="width:100%">
<tr>
<th class="schedule_title"><div id="spinner"><!-- spinney goes here --></div></th>
<th colspan="2" id="2013-12-02-btn" class="day_2013-12-02 agenda_slot_title agenda_slot_unavailable">
<div id="close_2013-12-02" class="close top_left very_small close_day">x</div>
Mon&nbsp;(2013-12-02)
</th>
<th class="day_2013-12-02 spacer 2013-12-02-spacer" id="">
<div class="ui-widget-content ui-resizable" id="resize-2013-12-02-spacer">
<div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
</div>
</th>
<th colspan="1" id="2013-12-03-btn" class="day_2013-12-03 agenda_slot_title agenda_slot_unavailable">
<div id="close_2013-12-03" class="close top_left very_small close_day">x</div>
Tue&nbsp;(2013-12-03)
</th>
<th class="day_2013-12-03 spacer 2013-12-03-spacer" id="">
<div class="ui-widget-content ui-resizable" id="resize-2013-12-03-spacer">
<div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
</div>
</th>
</tr>
<tr>
<th class="th_column"><button id="show_all_button" class="styled_button">show all</button></th>
<th class="day_2013-12-02-0900 day_2013-12-02 room_title ">0900-1130 </th>
<th class="day_2013-12-02-1300 day_2013-12-02 room_title ">1300-1500 </th>
<th class="day_2013-12-02 spacer 2013-12-02-spacer"></th>
<th class="day_2013-12-03-0900 day_2013-12-03 room_title ">0900-1130 </th>
<th class="day_2013-12-03 spacer 2013-12-03-spacer"></th>
</tr>
<tr id="room0" class="agenda_row_alt agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_room0">X</div>
<div class="right room_name">apple <span class="capacity">(61)</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">212/213</span>-->
</th>
<td id="room0_2013-12-02_0900" class="day_2013-12-02 agenda-column-2013-12-02-0900 agenda_slot agenda_slot_unavailable" capacity="62" ></td>
<td id="room0_2013-12-02_1300" class="day_2013-12-02 agenda-column-2013-12-02-1300 agenda_slot agenda_slot_unavailable" capacity="63" ></td>
<td class="day_2013-12-02 spacer 2013-12-02-spacer"></td>
<td id="room0_2013-12-03_0900" class="day_2013-12-03 agenda-column-2013-12-03-0900 agenda_slot agenda_slot_unavailable" capacity="64" ></td>
</tr>
<tr id="room1" class="agenda_row_alt agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_room1">X</div>
<div class="right room_name">orange <span class="capacity">(70)</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">212/213</span>-->
</th>
<td id="room1_2013-12-02_0900" class="day_2013-12-02 agenda-column-2013-12-02-0900 agenda_slot agenda_slot_unavailable" capacity="71" ></td>
<td id="room1_2013-12-02_1300" class="day_2013-12-02 agenda-column-2013-12-02-1300 agenda_slot agenda_slot_unavailable" capacity="72" ></td>
<td class="day_2013-12-02 spacer 2013-12-02-spacer"></td>
<td id="room1_2013-12-03_0900" class="day_2013-12-03 agenda-column-2013-12-03-0900 agenda_slot agenda_slot_unavailable" capacity="73" ></td>
</tr>
<tr id="room2" class="agenda_row_alt agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_room2">X</div>
<div class="right room_name">grape <span class="capacity">(80)</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">212/213</span>-->
</th>
<td id="room2_2013-12-02_0900" class="day_2013-12-02 agenda-column-2013-12-02-0900 agenda_slot agenda_slot_unavailable" capacity="81" ></td>
<td id="room2_2013-12-02_1300" class="day_2013-12-02 agenda-column-2013-12-02-1300 agenda_slot agenda_slot_unavailable" capacity="82" ></td>
<td class="day_2013-12-02 spacer 2013-12-02-spacer"></td>
<td id="room2_2013-12-03_0900" class="day_2013-12-03 agenda-column-2013-12-03-0900 agenda_slot agenda_slot_unavailable" capacity="83" ></td>
</tr>
<tr id="room3" class="agenda_row_alt agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_room3">X</div>
<div class="right room_name">pineapple <span class="capacity">(90)</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">212/213</span>-->
</th>
<td id="room3_2013-12-02_0900" class="day_2013-12-02 agenda-column-2013-12-02-0900 agenda_slot agenda_slot_unavailable" capacity="91" ></td>
<td id="room3_2013-12-02_1300" class="day_2013-12-02 agenda-column-2013-12-02-1300 agenda_slot agenda_slot_unavailable" capacity="92" ></td>
<td class="day_2013-12-02 spacer 2013-12-02-spacer"></td>
<td id="room3_2013-12-03_0900" class="day_2013-12-03 agenda-column-2013-12-03-0900 agenda_slot agenda_slot_unavailable" capacity="93" ></td>
</tr>
<tr id="room4" class="agenda_row_alt agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_room4">X</div>
<div class="right room_name">tomato <span class="capacity">(100)</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">212/213</span>-->
</th>
<td id="room4_2013-12-02_0900" class="day_2013-12-02 agenda-column-2013-12-02-0900 agenda_slot agenda_slot_unavailable" capacity="101" ></td>
<td id="room4_2013-12-02_1300" class="day_2013-12-02 agenda-column-2013-12-02-1300 agenda_slot agenda_slot_unavailable" capacity="102" ></td>
<td class="day_2013-12-02 spacer 2013-12-02-spacer"></td>
<td id="room4_2013-12-03_0900" class="day_2013-12-03 agenda-column-2013-12-03-0900 agenda_slot agenda_slot_unavailable" capacity="103" ></td>
</tr>
<tr id="room5" class="agenda_row_alt agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_room5">X</div>
<div class="right room_name">squash <span class="capacity">(110)</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">212/213</span>-->
</th>
<td id="room5_2013-12-02_0900" class="day_2013-12-02 agenda-column-2013-12-02-0900 agenda_slot agenda_slot_unavailable" capacity="111" ></td>
<td id="room5_2013-12-02_1300" class="day_2013-12-02 agenda-column-2013-12-02-1300 agenda_slot agenda_slot_unavailable" capacity="112" ></td>
<td class="day_2013-12-02 spacer 2013-12-02-spacer"></td>
<td id="room5_2013-12-03_0900" class="day_2013-12-03 agenda-column-2013-12-03-0900 agenda_slot agenda_slot_unavailable" capacity="113" ></td>
</tr>
<tr id="room6" class="agenda_row_alt agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_room6">X</div>
<div class="right room_name">raisin <span class="capacity">(120)</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">212/213</span>-->
</th>
<td id="room6_2013-12-02_0900" class="day_2013-12-02 agenda-column-2013-12-02-0900 agenda_slot agenda_slot_unavailable" capacity="121" ></td>
<td id="room6_2013-12-02_1300" class="day_2013-12-02 agenda-column-2013-12-02-1300 agenda_slot agenda_slot_unavailable" capacity="122" ></td>
<td class="day_2013-12-02 spacer 2013-12-02-spacer"></td>
<td id="room6_2013-12-03_0900" class="day_2013-12-03 agenda-column-2013-12-03-0900 agenda_slot agenda_slot_unavailable" capacity="123" ></td>
</tr>
<tr id="room7" class="agenda_row_alt agenda_slot">
<th class="vert_time">
<div class="close very_small close_room top_left small_button" id="close_room7">X</div>
<div class="right room_name">cucumber <span class="capacity">(130)</span></div>
<!-- <span class="hide_room light_blue_border">X</span><span class="left">212/213</span>-->
</th>
<td id="room7_2013-12-02_0900" class="day_2013-12-02 agenda-column-2013-12-02-0900 agenda_slot agenda_slot_unavailable" capacity="131" ></td>
<td id="room7_2013-12-02_1300" class="day_2013-12-02 agenda-column-2013-12-02-1300 agenda_slot agenda_slot_unavailable" capacity="132" ></td>
<td class="day_2013-12-02 spacer 2013-12-02-spacer"></td>
<td id="room7_2013-12-03_0900" class="day_2013-12-03 agenda-column-2013-12-03-0900 agenda_slot agenda_slot_unavailable" capacity="133" ></td>
</tr>
</table>
</div>
<div id="session-info" class="ui-droppable bucket-list room_title">
<div class="agenda_slot_title"><b>Session Information:</b></div>
<div class="ss_info_box">
<div class="ss_info ss_info_left">
<table>
<tr><td class="ss_info_name_short">Group:</td><td><span id="info_grp"></span>
<!-- <button id="agenda_sreq_button" class="right">Edit Request</button> --></tr>
<tr><td class="ss_info_name_short">Name:</td> <td id="info_name"></td></tr>
<tr><td class="ss_info_name_short">Area:</td> <td><span id="info_area"></span><button id="show_all_area" class="right">Show All</button></td></tr>
</table>
</div>
<div class="ss_info ss_info_right">
<table>
<tr><td class="ss_info_name_long">Duration/Capacity:</td><td class="info_split" id="info_duration"></td> <td class="info_split" id="info_capacity"></td></tr>
<tr><td class="ss_info_name_long">Location:</td><td colspan=2 id="info_location"></td></tr>
<tr><td class="ss_info_name_long">Responsible AD:</td><td colspan=2 id="info_responsible"></td></tr>
<tr><td class="ss_info_name_long">Requested By:</td><td colspan=2 id="info_requestedby"></td></tr>
</table>
</div>
<div id="conflict_table">
<div id="special_requests">Special Requests</div>
<table>
<tbody id="conflict_table_body">
<tr class="conflict_list_row">
<td class="conflict_list_title">
Group conflicts
</td>
<td id="conflict_group_list">
<ul>
</ul>
</td>
</tr>
<tr class="conflict_list_row">
<td class="conflict_list_title">
<b>be present</b>
</td>
<td id="conflict_people_list">
<ul>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<div class="agenda_find_free"><button class="agenda_selected_buttons small_button" id="find_free">Find Free</button></div>
<div class="agenda_double_slot button_disabled">
<button class="agenda_selected_buttons small_button" disabled
id="double_slot">Extend</button>
</div>
<div id="agenda_pin_slot" class="button_disabled">
<button class="agenda_selected_buttons small_button" disabled
id="pin_slot">Pin</button>
</div>
<div class="color_legend">
<span class="APP-scheme"><input class='color_checkboxes' type="checkbox" id="APP" value="APP-value" checked>APP</span>
<span class="GEN-scheme"><input class='color_checkboxes' type="checkbox" id="GEN" value="GEN-value" checked>GEN</span>
<span class="INT-scheme"><input class='color_checkboxes' type="checkbox" id="INT" value="INT-value" checked>INT</span>
<span class="IRTF-scheme"><input class='color_checkboxes' type="checkbox" id="IRTF" value="IRTF-value" checked>IRTF</span>
<span class="OPS-scheme"><input class='color_checkboxes' type="checkbox" id="OPS" value="OPS-value" checked>OPS</span>
<span class="RAI-scheme"><input class='color_checkboxes' type="checkbox" id="RAI" value="RAI-value" checked>RAI</span>
<span class="RTG-scheme"><input class='color_checkboxes' type="checkbox" id="RTG" value="RTG-value" checked>RTG</span>
<span class="SEC-scheme"><input class='color_checkboxes' type="checkbox" id="SEC" value="SEC-value" checked>SEC</span>
<span class="TSV-scheme"><input class='color_checkboxes' type="checkbox" id="TSV" value="TSV-value" checked>TSV</span>
</div>
</div>
<div class="agenda_save_box">
<div id="agenda_title"><b>Agenda name: </b><span>mtg_83</span></div>
<div id="agenda_saveas">
<form action="/meeting/83/schedule/mtg_83/edit" method="post">
<p><label for="id_savename">Savename:</label> <input id="id_savename" type="text" name="savename" maxlength="100" /></p>
<input type="submit" name="saveas" value="saveas">
</form>
</div>
</div>
</div>
<!-- <div class="ui-resizable-handle ui-resizable-se ui-icon ui-icon-gripsmall-diagonal-ew">*</div> -->
</div>
<!-- some boxes for dialogues -->
<div id="dialog-confirm-two" title="" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
<span class="dialog-confirm-text">Are you sure you want to put two sessions into the same slot?</span>
</p>
</div>
<div id="dialog-confirm-toosmall" title="" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
<span class="dialog-confirm-text">The room you are moving to has a lower
room capacity then the requested capacity,<br>
Are you sure you want to continue?
</span>
</p>
</div>
<div id="dialog-confirm-twotoosmall" title="" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
<span class="dialog-confirm-text">
The slot you are moving to already has a session in it, <br>
the room is also smaller than the requested amount.<br>
Are you sure you want to continue?
</span>
</p>
</div>
<div id="can-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
</div>
<div id="can-not-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
<p>
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
You can not extend this session. The slot is not available.
</p>
</div>
<script type="text/javascript" src="/js/yui/yui-20100305.js"></script>
<script type="text/javascript">
//<![CDATA[
YAHOO.util.Event.onContentReady("wgs", function () {
var oMenu = new YAHOO.widget.Menu("wgs", { position: "static", hidedelay: 750, lazyload: true });
oMenu.render();
});
//]]>
</script>
<script src="/dajaxice/dajaxice.core.js" type="text/javascript" charset="utf-8"></script>
<script type='text/javascript' src='/js/agenda/agenda_edit.js'></script>
<script type='text/javascript' src='/js/agenda/agenda_helpers.js'></script>
<script type='text/javascript' src='/js/agenda/agenda_objects.js'></script>
<script type='text/javascript' src='/js/agenda/agenda_listeners.js'></script>
<script type='text/javascript' src='/js/test/agenda_funcs.js'></script>
<script type='text/javascript'>
__debug_conflict_calculate = true;
var meeting_number = "83";
var schedule_id = 24;
var schedule_owner_href = "wlo@amsl.com";
var schedule_name = "83";
var scheduledsession_post_href = "/test/agenda_ui.html";
var meeting_base_url = "http://localhost:8000/meeting/83";
var site_base_url = "http://localhost:8000";
var total_days = 7;
var total_rooms = 11;
function setup_slots(promiselist){
days.push("2013-12-02");
days.push("2013-12-03");
area_directors["app"] = [];
area_directors["app"] = [];
area_directors["gen"] = [];
area_directors["int"] = [];
area_directors["int"] = [];
area_directors["ops"] = [];
area_directors["ops"] = [];
area_directors["rai"] = [];
area_directors["rai"] = [];
area_directors["rtg"] = [];
area_directors["rtg"] = [];
area_directors["sec"] = [];
area_directors["sec"] = [];
area_directors["tsv"] = [];
area_directors["tsv"] = [];
//area_directors["app"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/21684.json"));
//area_directors["app"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/18321.json"));
//area_directors["gen"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/21072.json"));
//area_directors["int"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/100664.json"));
//area_directors["int"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/20356.json"));
//area_directors["ops"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/105682.json"));
//area_directors["ops"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/20959.json"));
//area_directors["rai"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/103539.json"));
//area_directors["rai"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/108049.json"));
//area_directors["rtg"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/2329.json"));
//area_directors["rtg"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/104198.json"));
//area_directors["sec"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/19177.json"));
//area_directors["sec"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/19483.json"));
//area_directors["tsv"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/105519.json"));
//area_directors["tsv"].push(find_person_by_href("http://knothole.gatineau.credil.org:8000/person/107190.json"));
t_slots = three_by_eight_grid();
t_sessions = make_6_sessions();
place_6_sessions(t_slots, t_sessions);
conflict_4_sessions(t_sessions);
console.log("setup_slots run");
legend_status["APP"] = true;
legend_status["GEN"] = true;
legend_status["INT"] = true;
legend_status["IRTF"] = true;
legend_status["OPS"] = true;
legend_status["RAI"] = true;
legend_status["RTG"] = true;
legend_status["SEC"] = true;
legend_status["TSV"] = true;
}
</script>
<style type='text/css'>
</style>
<div id="ietf-extras"></div>
</body></html>
</body>
</html>