Merged in ^/personal/henrik/6.25.2-floorplans@11546, which provides basic meeting venue floorplan and room location support, including JSON API access to floorplan images and room location. Floorplan data for IETF 96 in Berlin is also provided in a data migration. Not part of this release, but planned for a later release, is support for arrows pointing out rooms, and secretariat support for room location input.

- Legacy-Id: 11547
This commit is contained in:
Henrik Levkowetz 2016-07-07 17:18:00 +00:00
commit ca36d50484
27 changed files with 641 additions and 90 deletions

View file

@ -575,7 +575,7 @@ class DocHistory(DocumentInfo):
def save_document_in_history(doc):
"""This should be called before saving changes to a Document instance,
so that the DocHistory entries contain all previous states, while
the Group entry contain the current state. XXX TODO: Call this
the Document entry contain the current state. XXX TODO: Call this
directly from Document.save(), and add event listeners for save()
on related objects so we can save as needed when they change, too.
"""

View file

@ -1,7 +1,7 @@
# Autogenerated by the makeresources management command 2015-10-19 12:29 PDT
from tastypie.resources import ModelResource
from ietf.api import ToOneField
from tastypie.fields import ToManyField, CharField # pyflakes:ignore
from tastypie.fields import ToManyField, CharField
from tastypie.constants import ALL, ALL_WITH_RELATIONS # pyflakes:ignore
from tastypie.cache import SimpleCache

View file

@ -616,3 +616,7 @@ def format_timedelta(timedelta):
hours, remainder = divmod(s, 3600)
minutes, seconds = divmod(remainder, 60)
return '{hours:02d}:{minutes:02d}'.format(hours=hours,minutes=minutes)
@register.filter()
def nbsp(value):
return mark_safe(" ".join(value.split(' ')))

View file

@ -1,9 +1,10 @@
from django.contrib import admin
from ietf.meeting.models import Meeting, Room, Session, TimeSlot, Constraint, Schedule, SchedTimeSessAssignment, ResourceAssociation
from ietf.meeting.models import (Meeting, Room, Session, TimeSlot, Constraint, Schedule,
SchedTimeSessAssignment, ResourceAssociation, FloorPlan)
class RoomAdmin(admin.ModelAdmin):
list_display = ["id", "meeting", "name", "capacity", ]
list_display = ["id", "meeting", "name", "capacity", "x1", "y1", "x2", "y2", ]
list_filter = ["meeting"]
ordering = ["-meeting"]
@ -98,3 +99,8 @@ admin.site.register(SchedTimeSessAssignment, SchedTimeSessAssignmentAdmin)
class ResourceAssociationAdmin(admin.ModelAdmin):
list_display = ["desc", "icon", "desc", ]
admin.site.register(ResourceAssociation, ResourceAssociationAdmin)
class FloorPlanAdmin(admin.ModelAdmin):
list_display = ['id', 'meeting', 'name', 'order', 'image', ]
raw_id_fields = ['meeting', ]
admin.site.register(FloorPlan, FloorPlanAdmin)

View file

@ -3,8 +3,9 @@ import random
import datetime
from django.db.models import Max
from django.core.files.base import ContentFile
from ietf.meeting.models import Meeting, Session, Schedule, TimeSlot, SessionPresentation
from ietf.meeting.models import Meeting, Session, Schedule, TimeSlot, SessionPresentation, FloorPlan
from ietf.group.factories import GroupFactory
from ietf.person.factories import PersonFactory
@ -106,3 +107,18 @@ class SessionPresentationFactory(factory.DjangoModelFactory):
def rev(self):
return self.document.rev
class FloorPlanFactory(factory.DjangoModelFactory):
class Meta:
model = FloorPlan
name = factory.Sequence(lambda n: u'Venue Floor %d' % n)
meeting = factory.SubFactory(MeetingFactory)
order = factory.Sequence(lambda n: n)
image = factory.LazyAttribute(
lambda _: ContentFile(
factory.django.ImageField()._make_data(
{'width': 1024, 'height': 768}
), 'floorplan.jpg'
)
)

View file

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import ietf.utils.storage
import ietf.meeting.models
class Migration(migrations.Migration):
dependencies = [
('meeting', '0024_migrate_interim_meetings'),
]
operations = [
migrations.CreateModel(
name='FloorPlan',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=255)),
('order', models.SmallIntegerField()),
('image', models.ImageField(default=None, storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=ietf.meeting.models.floorplan_path, blank=True)),
('meeting', models.ForeignKey(to='meeting.Meeting')),
],
options={
},
bases=(models.Model,),
),
migrations.AddField(
model_name='room',
name='floorplan',
field=models.ForeignKey(default=None, blank=True, to='meeting.FloorPlan', null=True),
preserve_default=True,
),
migrations.AddField(
model_name='room',
name='x1',
field=models.SmallIntegerField(default=None, null=True, blank=True),
preserve_default=True,
),
migrations.AddField(
model_name='room',
name='x2',
field=models.SmallIntegerField(default=None, null=True, blank=True),
preserve_default=True,
),
migrations.AddField(
model_name='room',
name='y1',
field=models.SmallIntegerField(default=None, null=True, blank=True),
preserve_default=True,
),
migrations.AddField(
model_name='room',
name='y2',
field=models.SmallIntegerField(default=None, null=True, blank=True),
preserve_default=True,
),
]

View file

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
floors = [
(1, "Berlin Intercontinental Floor 1", 1, 'floor/floorplan-96-berlin-intercontinental-floor-1.jpg'),
(2, "Berlin Intercontinental Floor 1", 2, 'floor/floorplan-96-berlin-intercontinental-floor-2.jpg'),
(3, "Berlin Intercontinental Floor 1", 14, 'floor/floorplan-96-berlin-intercontinental-floor-14.jpg'),
]
rooms = [
("Bellevue", 1, 176, 1348, 324, 1526),
("Kaminzimmer", 1, 696, 820, 812, 1038),
("Charlottenburg I", 1, 374, 320, 528, 400),
("Charlottenburg II/III", 1, 374, 172, 528, 316),
("Chess", 2, 238, 614, 336, 782),
("Glienicke", 1, 228, 1251, 324, 1310),
("Hugos 360", 3, 801, 1346, 976, 1509),
("King", 2, 802, 1389, 890, 1508),
("Koepenick I/II", 1, 370, 453, 458, 602),
("Lincke", 2, 40, 99, 532, 166),
("Potsdam I", 1, 1228, 790, 1550, 994),
("Potsdam I/III", 1, 1017, 792, 1550, 994),
("Potsdam II", 1, 1311, 1036, 1536, 1142),
("Potsdam III", 1, 1017, 792, 1228, 987),
("Rook", 2, 915, 1150, 1004, 1269),
("Schoeneberg", 1, 369, 42, 534, 126),
("Tegel", 1, 201, 1088, 326, 1184),
("Tiergarten", 1, 240, 612, 334, 780),
("Wintergarten/Pavillion", 1, 466, 1038, 711, 1504),
]
def forward(apps, schema_editor):
FloorPlan = apps.get_model('meeting','FloorPlan')
Room = apps.get_model('meeting','Room')
Meeting = apps.get_model('meeting','Meeting')
meeting = Meeting.objects.get(number='96')
for item in floors:
id, name, order, image = item
f = FloorPlan(id=id, name=name, meeting=meeting, order=order, image=image)
f.save()
for item in rooms:
name, floor_id, x1, y1, x2, y2 = item
room = Room.objects.get(name=name, meeting=meeting)
room.floorplan_id = floor_id
room.x1 = x1
room.y1 = y1
room.x2 = x2
room.y2 = y2
room.save()
def backward(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('meeting', '0025_add_floorplan_and_room_coordinates'),
]
operations = [
migrations.RunPython(forward,backward)
]

View file

@ -25,6 +25,7 @@ from ietf.group.models import Group
from ietf.group.utils import can_manage_materials
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName
from ietf.person.models import Person
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
countries = pytz.country_names.items()
countries.sort(lambda x,y: cmp(x[1], y[1]))
@ -274,6 +275,8 @@ class Meeting(models.Model):
class Meta:
ordering = ["-date", "id"]
# === Rooms, Resources, Floorplans =============================================
class ResourceAssociation(models.Model):
name = models.ForeignKey(RoomResourceName)
#url = models.UrlField() # not sure what this was for.
@ -298,6 +301,15 @@ class Room(models.Model):
capacity = models.IntegerField(null=True, blank=True)
resources = models.ManyToManyField(ResourceAssociation, blank = True)
session_types = models.ManyToManyField(TimeSlotTypeName, blank = True)
# floorplan-related properties
floorplan = models.ForeignKey('FloorPlan', null=True, blank=True, default=None)
# floorplan: room pixel position : (0,0) is top left of image, (xd, yd)
# is room width, height.
x1 = models.SmallIntegerField(null=True, blank=True, default=None)
y1 = models.SmallIntegerField(null=True, blank=True, default=None)
x2 = models.SmallIntegerField(null=True, blank=True, default=None)
y2 = models.SmallIntegerField(null=True, blank=True, default=None)
# end floorplan-related stuff
def __unicode__(self):
return "%s size: %s" % (self.name, self.capacity)
@ -332,6 +344,36 @@ class Room(models.Model):
'capacity': self.capacity,
}
def left(self):
return min(self.x1, self.x2) if (self.x1 and self.x2) else 0
def top(self):
return min(self.y1, self.y2) if (self.y1 and self.y2) else 0
def right(self):
return max(self.x1, self.x2) if (self.x1 and self.x2) else 0
def bottom(self):
return max(self.y1, self.y2) if (self.y1 and self.y2) else 0
def functional_display_name(self):
if not self.functional_name:
return ""
if self.functional_name.lower().startswith('breakout'):
return ""
if self.functional_name[0].isdigit():
return ""
return self.functional_name
class Meta:
ordering = ["-meeting", "name"]
def floorplan_path(instance, filename):
root, ext = os.path.splitext(filename)
return u"%s/floorplan-%s-%s%s" % (settings.FLOORPLAN_MEDIA_DIR, instance.meeting.number, slugify(instance.name), ext)
class FloorPlan(models.Model):
name = models.CharField(max_length=255)
meeting = models.ForeignKey(Meeting)
order = models.SmallIntegerField()
image = models.ImageField(storage=NoLocationMigrationFileSystemStorage(), upload_to=floorplan_path, blank=True, default=None)
# === Schedules, Sessions, Timeslots and Assignments ===========================
class TimeSlot(models.Model):
"""
@ -1307,3 +1349,4 @@ class Session(models.Model):
if self.badness_test(1):
self.badness_log(1, "badgroup: %s badness = %u\n" % (self.group.acronym, badness))
return badness

View file

@ -8,7 +8,7 @@ from tastypie.cache import SimpleCache
from ietf import api
from ietf.meeting.models import ( Meeting, ResourceAssociation, Constraint, Room, Schedule, Session,
TimeSlot, SchedTimeSessAssignment, SessionPresentation )
TimeSlot, SchedTimeSessAssignment, SessionPresentation, FloorPlan )
from ietf.name.resources import MeetingTypeNameResource
class MeetingResource(ModelResource):
@ -83,11 +83,28 @@ class ConstraintResource(ModelResource):
}
api.meeting.register(ConstraintResource())
class FloorPlanResource(ModelResource):
meeting = ToOneField(MeetingResource, 'meeting')
class Meta:
queryset = FloorPlan.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'floorplan'
filtering = {
"id": ALL,
"name": ALL,
"order": ALL,
"image": ALL,
"meeting": ALL_WITH_RELATIONS,
}
api.meeting.register(FloorPlanResource())
from ietf.name.resources import TimeSlotTypeNameResource
class RoomResource(ModelResource):
meeting = ToOneField(MeetingResource, 'meeting')
resources = ToManyField(ResourceAssociationResource, 'resources', null=True)
session_types = ToManyField(TimeSlotTypeNameResource, 'session_types', null=True)
floorplan = ToOneField(FloorPlanResource, 'floorplan', null=True)
class Meta:
cache = SimpleCache()
queryset = Room.objects.all()
@ -101,6 +118,7 @@ class RoomResource(ModelResource):
"meeting": ALL_WITH_RELATIONS,
"resources": ALL_WITH_RELATIONS,
"session_types": ALL_WITH_RELATIONS,
"floorplan": ALL_WITH_RELATIONS,
}
api.meeting.register(RoomResource())

View file

@ -40,7 +40,7 @@ def make_meeting_test_data():
unofficial_schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="test-unofficial-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 = Room.objects.create(meeting=meeting, name="Test Room", capacity=123, functional_name="Testing Ground")
breakfast_room = Room.objects.create(meeting=meeting, name="Breakfast Room", capacity=40)
room.session_types.add("session")
breakfast_room.session_types.add("lead")

View file

@ -46,7 +46,7 @@ class ScheduleEditTests(StaticLiveServerTestCase):
def setUp(self):
set_coverage_checking(False)
condition_data()
self.driver = webdriver.PhantomJS(service_log_path=settings.TEST_GHOSTDRIVER_LOG_PATH)
self.driver = webdriver.PhantomJS(port=0, service_log_path=settings.TEST_GHOSTDRIVER_LOG_PATH)
self.driver.set_window_size(1024,768)
def tearDown(self):

View file

@ -26,7 +26,8 @@ from ietf.utils.mail import outbox
from ietf.person.factories import PersonFactory
from ietf.group.factories import GroupFactory
from ietf.meeting.factories import SessionFactory, SessionPresentationFactory, ScheduleFactory, MeetingFactory
from ietf.meeting.factories import ( SessionFactory, SessionPresentationFactory, ScheduleFactory,
MeetingFactory, FloorPlanFactory )
from ietf.doc.factories import DocumentFactory
class MeetingTests(TestCase):
@ -1092,3 +1093,20 @@ class AjaxTests(TestCase):
self.assertTrue('utc' in data)
self.assertTrue('error' not in data)
self.assertEqual(data['utc'], '20:00')
class FloorPlanTests(TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_floor_plan_page(self):
make_meeting_test_data()
meeting = Meeting.objects.filter(type_id='ietf').order_by('id').last()
FloorPlanFactory.create(meeting=meeting)
url = urlreverse('ietf.meeting.views.floor_plan')
r = self.client.get(url)
self.assertEqual(r.status_code, 200)

View file

@ -52,7 +52,7 @@ type_ietf_only_patterns = [
]
type_ietf_only_patterns_id_optional = [
url(r'^agenda(-utc)?(?P<ext>.html)?/?$', views.agenda),
url(r'^agenda(?P<utc>-utc)?(?P<ext>.html)?/?$', views.agenda),
url(r'^agenda(?P<ext>.txt)$', views.agenda),
url(r'^agenda(?P<ext>.csv)$', views.agenda),
url(r'^agenda/edit$', views.edit_agenda),
@ -61,6 +61,8 @@ type_ietf_only_patterns_id_optional = [
url(r'^agenda.ics$', views.ical_agenda),
url(r'^agenda/week-view(?:.html)?/?$', views.week_view),
url(r'^agenda/room-view(?:.html)?/?$', views.room_view),
url(r'^agenda/floor-plan/?$', views.floor_plan),
url(r'^agenda/floor-plan/(?P<floor>[-a-z0-9_]+)/?$', views.floor_plan),
url(r'^week-view(?:.html)?/?$', views.week_view),
url(r'^room-view(?:.html)?/$', views.room_view),
url(r'^materials(?:.html)?/$', views.materials),

View file

@ -32,7 +32,7 @@ from ietf.doc.models import Document, State, DocEvent
from ietf.group.models import Group
from ietf.group.utils import can_manage_materials
from ietf.ietfauth.utils import role_required, has_role
from ietf.meeting.models import Meeting, Session, Schedule, Room
from ietf.meeting.models import Meeting, Session, Schedule, Room, FloorPlan
from ietf.meeting.helpers import get_areas, get_person_by_email, get_schedule_by_name
from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list
from ietf.meeting.helpers import get_all_assignments_from_schedule
@ -393,7 +393,7 @@ def list_agendas(request, num=None ):
})
@ensure_csrf_cookie
def agenda(request, num=None, name=None, base=None, ext=None, owner=None):
def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""):
base = base if base else 'agenda'
ext = ext if ext else '.html'
mimetype = {
@ -1424,3 +1424,15 @@ def upcoming_ical(request):
response['Content-Disposition'] = 'attachment; filename="upcoming.ics"'
return response
def floor_plan(request, num=None, floor=None, ):
meeting = get_meetings(num).first()
schedule = meeting.agenda
floors = FloorPlan.objects.filter(meeting=meeting).order_by('order')
if floor:
floors = floors.filter(name=floor)
return render(request, 'meeting/floor-plan.html', {
"schedule": schedule,
"number": num,
"floors": floors,
})

View file

@ -29,8 +29,8 @@ class PersonInfo(models.Model):
affiliation = models.CharField(max_length=255, blank=True, help_text="Employer, university, sponsor, etc.")
address = models.TextField(max_length=255, blank=True, help_text="Postal mailing address.")
biography = models.TextField(blank=True, help_text="Short biography for use on leadership pages. Use plain text or reStructuredText markup.")
photo = models.ImageField(storage=NoLocationMigrationFileSystemStorage(location=settings.PHOTOS_DIR),upload_to=settings.PHOTOS_DIRNAME,blank=True, default=None)
photo_thumb = models.ImageField(storage=NoLocationMigrationFileSystemStorage(location=settings.PHOTOS_DIR),upload_to=settings.PHOTOS_DIRNAME,blank=True, default=None)
photo = models.ImageField(storage=NoLocationMigrationFileSystemStorage(), upload_to=settings.PHOTOS_DIRNAME, blank=True, default=None)
photo_thumb = models.ImageField(storage=NoLocationMigrationFileSystemStorage(), upload_to=settings.PHOTOS_DIRNAME, blank=True, default=None)
def __unicode__(self):
return self.plain_name()

View file

@ -556,12 +556,18 @@ YANG_INVAL_MODEL_DIR = '/a/www/ietf-ftp/yang/invalmod/'
XML_LIBRARY = "/www/tools.ietf.org/tools/xml2rfc/web/public/rfc/"
# === Meeting Related Settings =================================================
MEETING_MATERIALS_SUBMISSION_START_DAYS = -90
MEETING_MATERIALS_SUBMISSION_CUTOFF_DAYS = 26
MEETING_MATERIALS_SUBMISSION_CORRECTION_DAYS = 50
INTERNET_DRAFT_DAYS_TO_EXPIRE = 185
FLOORPLAN_MEDIA_DIR = 'floor'
# ==============================================================================
DOT_BINARY = '/usr/bin/dot'
UNFLATTEN_BINARY= '/usr/bin/unflatten'
RSYNC_BINARY = '/usr/bin/rsync'

View file

@ -110,6 +110,7 @@ div.anchor-target:before {
height: 65px;
margin-top: -65px;
}
div.anchor-target { z-index: 0; }
/* Make the panel title font normally large */
.panel-title { font-size: 14px }
@ -124,6 +125,7 @@ label.required:after { content: "\2217"; color: #a94442; font-weight: bold; }
/* Add some margin to tabs */
.nav-tabs[role=tablist] { margin-bottom: 0.7em; }
.nav a { z-index: 100; }
/* Styles needed for the ballot table */
/*

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,173 @@
var verbose = 0;
function suffixmap(nm)
// Given a name like "foo-ab" or "foo-X-and-Y", change it to the "list-of-room-names" format, "foo-a/foo-b".
{
var andsuffix = /^(.*-)([^-]+)-and-(.*)$/;
var andMatch = andsuffix.exec(nm);
if (andMatch && andMatch[0] != '') {
nm = andMatch[1] + andMatch[2] + "-" + andMatch[3];
}
// xyz-a/b/c => xyz-a/xyz-b/xyz-c
var abcsuffix = /^(.*)-([a-h0-9]+)[-\/]([a-h0-9]+)([-\/][a-h0-9]+)?$/;
var suffixMatch = abcsuffix.exec(nm);
if (verbose) alert("nm=" + nm);
if (suffixMatch && suffixMatch[0] != '') {
if (verbose) alert("matched");
nm = suffixMatch[1] + "-" + suffixMatch[2] + "/" +
suffixMatch[1] + "-" + suffixMatch[3];
if (verbose) alert("nm=>" + nm);
if (suffixMatch[4] && suffixMatch[4] != '')
nm += "/" + suffixMatch[1] + "-" + suffixMatch[4];
if (verbose) alert("nm=>" + nm);
}
// xyz-abc => xyz-a/xyz-b/xyz-c
abcsuffix = /^(.*)-([a-h])([a-h]+)([a-h])?$/;
var suffixMatch = abcsuffix.exec(nm);
if (suffixMatch && suffixMatch[0] != '') {
nm = suffixMatch[1] + "-" + suffixMatch[2] + "/" +
suffixMatch[1] + "-" + suffixMatch[3];
if (suffixMatch[4] && suffixMatch[4] != '')
nm += "/" + suffixMatch[1] + "-" + suffixMatch[4];
}
if (verbose) alert("suffixmap returning: " + nm);
return nm;
}
function roomcoords(nm)
// Find the coordinates of a room or list of room names separated by "/".
// Calls the function findroom() to get the coordinates for a specific room.
{
if (!nm) return null;
if (nm.match("/")) {
var nms = nm.split("/");
var nm0 = findroom(nms[0]);
if (!nm0) return null;
for (var i = 1; i < nms.length; i++) {
var nmi = roomcoords(nms[i]);
if (!nmi) return null;
if (nmi[0] < nm0[0]) nm0[0] = nmi[0];
if (nmi[1] < nm0[1]) nm0[1] = nmi[1];
if (nmi[2] > nm0[2]) nm0[2] = nmi[2];
if (nmi[3] > nm0[3]) nm0[3] = nmi[3];
}
return [nm0[0], nm0[1], nm0[2], nm0[3]];
} else {
return findroom(nm);
}
}
function setarrow(nm, fl)
// Place an arrow at the center of a given room name (or list of room names separated by "/").
{
for (var i = 0; i < arrowsuffixlist.length; i++) {
removearrow(arrowsuffixlist[i], fl);
}
for (var i = 0; i < arguments.length; i+=2) {
nm = roommap(arguments[i]);
if (verbose) alert("nm=" + nm);
var rooms = nm.split(/[|]/);
for (var j = 0; j < rooms.length; j++) {
var room = rooms[j];
var ret = roomcoords(room);
if (verbose) alert("roomcoords returned: " + ret);
if (!ret) continue;
var left = ret[0], top = ret[1], right = ret[2], bottom = ret[3], offsetleft = -25, offsettop = -25;
if (verbose) alert("left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom);
//alert("left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom);
var arrowdiv = fl+'-arrowdiv' + (j > 0 ? j : "");
//if (verbose) alert("arrowdiv: " + arrowdiv);
var adiv = document.getElementById(arrowdiv);
// if (verbose) alert("looking for 'arrowdiv'+" + j);
if (adiv) {
//if (verbose) alert("adiv found");
adiv.style.left = left + (right - left) / 2 + offsetleft;
adiv.style.top = top + (bottom - top) / 2 + offsettop;
adiv.style.visibility = "visible";
}
}
}
}
function removearrow(which, fl)
{
for (var i = 0; i < arguments.length; i++) {
var which = arguments[i];
var arrowdiv = fl+'-arrowdiv' + (which ? which : "");
var adiv = document.getElementById(arrowdiv);
// if (verbose) alert("looking for '" + arrowdiv + "'");
if (adiv) {
// if (verbose) alert("adiv found");
adiv.style.left = -500;
adiv.style.top = -500;
adiv.style.visibility = "hidden";
}
}
}
function setarrowlist(which, names)
{
for (var i = 1; i < arguments.length; i++) {
setarrow(arguments[i], which);
}
}
function QueryString()
// Create a QueryString object
{
// get the query string, ignore the ? at the front.
var querystring = location.search.substring(1);
// parse out name/value pairs separated via &
var args = querystring.split('&');
// split out each name = value pair
for (var i = 0; i < args.length; i++) {
var pair = args[i].split('=');
// Fix broken unescaping
var temp = unescape(pair[0]).split('+');
var name_ = temp.join(' ');
var value_ = '';
if (typeof pair[1] == 'string') {
temp = unescape(pair[1]).split('+');
value_ = temp.join(' ');
}
this[name_] = value_;
}
this.get = function(nm, def) {
var value_ = this[nm];
if (value_ == null) return def;
else return value_;
};
}
function checkParams()
// Check the parameters for one named "room". If found, call setarrow(room).
{
var querystring = new QueryString();
var room = querystring.get("room");
if (room && room != "") setarrow(room);
}
// new functions
function located(loc)
{
if (loc.civic && loc.civic.ROOM) {
// map from "TerminalRoom" to "terminal-room" as necessary.
setarrow(loc.civic.ROOM.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(), "-green");
}
}
// this needs to be called onload
function automaticarrow()
{
// if (navigator.geolocation) {
// navigator.geolocation.getCurrentPosition(located);
// }
}

View file

@ -546,6 +546,7 @@ class SubmitTests(TestCase):
# edit
mailbox_before = len(outbox)
# FIXME If this test is started before midnight, and ends after, it will fail
document_date = datetime.date.today() - datetime.timedelta(days=-3)
r = self.client.post(edit_url, {
"edit-title": "some title",

View file

@ -31,33 +31,17 @@
{% origin %}
<div class="row">
<div class="col-md-10">
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated %}
<p class="noprint h6 text-center panel panel-heading ">
{% if "-utc" in request.path %}
<a href="{% url 'ietf.meeting.views.agenda' num=schedule.meeting.number %}">Agenda in local timezone</a> |
{% else %}
{% comment %}<a href="{% url 'ietf.meeting.views.agenda' base='agenda-utc' %}">Agenda in UTC timezone</a> | {% endcomment %}
<a href="/meeting/agenda-utc">Agenda in UTC timezone</a> |
{% endif %}
<a href="{% url 'ietf.meeting.views.agenda' num=schedule.meeting.number ext='.txt' %}">Plaintext agenda</a> |
<a href="https://tools.ietf.org/agenda/{{schedule.meeting.number}}/">Tools-style agenda</a>
{% if user|has_role:"Secretariat,Area Director,IAB" %}
|
{% if schedule != meeting.agenda %}
<a href="{% url 'ietf.meeting.views.agenda_by_room' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">List by Room</a> |
<a href="{% url 'ietf.meeting.views.agenda_by_type' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">List by Type</a> |
<a href="{% url 'ietf.meeting.views.room_view' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">Room Grid</a>
{% else %}
<a href="{% url 'ietf.meeting.views.agenda_by_room' num=schedule.meeting.number%}">List by Room</a> |
<a href="{% url 'ietf.meeting.views.agenda_by_type' num=schedule.meeting.number%}">List by Type</a> |
<a href="{% url 'ietf.meeting.views.room_view' num=schedule.meeting.number%}">Room Grid</a>
{% endif %}
{% endif %}
</p>
<div class="col-md-12">
{% if "-utc" in request.path %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="agenda-utc" title_extra="(UTC)" %}
{% else %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=updated selected="agenda" title_extra="" %}
{% endif %}
</div>
</div>
<div class="row">
<div class="col-md-10">
{# cache this part for 5 minutes -- it takes 3-6 seconds to generate #}
{% load cache %}
{% cache 300 ietf_meeting_agenda_utc schedule.meeting.number request.path %}

View file

@ -1,13 +1,13 @@
{% extends "base.html" %}
{% block morecss %}
ul.daylist { list-style:none; padding-left:0; }
li.daylistentry { font-size:162%; font-weight:700; }
li h2 { font-weight: 600; margin-top: 0.5em; }
li h3 { font-weight: 400; margin-top: 0.5em; }
li.even { background-color:#EDF5FF; }
li.odd { background-color:white; }
ul.roomlist {list-style:none; padding-left:0; margin-bottom:20px;}
ul.roomlist {list-style:none; margin-top: 0.5em; }
li.roomlistentry { font-weight: 400; }
ul.sessionlist { list-style:none; padding-left:2em; margin-bottom:10px;}
li.sessionlistentry { font-size:62%; }
.type-lead:after { content: " (DO NOT POST)"; color:red; }
.type-offagenda:after { content:" (not published on agenda)"; }
@ -17,25 +17,26 @@ li.sessionlistentry { font-size:62%; }
{% block title %}Agenda for {{meeting}} by Room{% endblock %}
{% block content %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=meeting.updated %}
<h1>Agenda for {{meeting}} by Room</h1>
<ul class="daylist">
{% for day,sessions in ss_by_day.items %}
<li class="daylistentry {% cycle 'even' 'odd' %}">{{day|date:'l, j F Y'}}
{% regroup sessions by timeslot.get_functional_location as room_list %}
<ul class="roomlist">
{% for room in room_list %}
<li class="roomlistentry">{{room.grouper|default:"Location Unavailable"}}
<ul class="sessionlist">
{% for ss in room.list %}
<li class="sessionlistentry type-{{ss.timeslot.type.slug}}">{{ss.timeslot.time|date:"H:i"}}-{{ss.timeslot.end_time|date:"H:i"}} {{ss.session.short_name}}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=meeting.updated selected="by-room" title_extra="by Room" %}
<ul class="daylist">
{% for day,sessions in ss_by_day.items %}
<li class="daylistentry {% cycle 'even' 'odd' %}"><h2>{{day|date:'l, j F Y'}}</h2>
{% regroup sessions by timeslot.get_functional_location as room_list %}
<ul class="roomlist">
{% for room in room_list %}
<li class="roomlistentry"><h3>{{room.grouper|default:"Location Unavailable"}}</h3>
<ul class="sessionlist">
{% for ss in room.list %}
<li class="sessionlistentry type-{{ss.timeslot.type.slug}}">{{ss.timeslot.time|date:"H:i"}}-{{ss.timeslot.end_time|date:"H:i"}} {{ss.session.short_name}}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -2,13 +2,15 @@
{% block morecss %}
ul.typelist { list-style:none; padding-left:0; }
li.typelistentry { font-size:162%; font-weight:700; }
li h2 { font-weight: 600; margin-top: 0.5em; }
li h3 { font-weight: 400; margin-top: 0.5em; }
li.even { background-color:#EDF5FF; }
li.odd { background-color:white; }
ul.daylist {list-style:none; padding-left:0; margin-bottom:20px;}
li.daylistentry { margin-left:2em; font-weight: 400; }
.sessiontable {margin-left:2em; font-size:62%;}
.sessiontable {margin-left: 2em; }
.sessiontable td {padding-right: 1em;}
.typelabel { font-size:162%; font-weight:700; }
@ -25,18 +27,18 @@ li.daylistentry { margin-left:2em; font-weight: 400; }
{% block title %}Agenda for {{meeting}} by Session Type{% endblock %}
{% block content %}
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=meeting.updated %}
<h1>Agenda for {{meeting}} by Session Type</h1>
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=meeting.updated selected="by-type" title_extra="by Session Type" %}
{% regroup assignments by session.type.slug as type_list %}
<ul class="typelist">
{% for type in type_list %}
<li class="typelistentry {% cycle 'even' 'odd' %}">
{{type.grouper}} {% if schedule == meeting.agenda %}<a id="ical-link" class="btn btn-primary" href="{% url "ietf.meeting.views.agenda_by_type_ics" num=meeting.number type=type.grouper %}">Download to Calendar</a>{% endif %}
<h2>{{type.grouper|title}}</h2> {% if schedule == meeting.agenda %}<a id="ical-link" class="btn btn-primary" href="{% url "ietf.meeting.views.agenda_by_type_ics" num=meeting.number type=type.grouper %}">Download to Calendar</a>{% endif %}
<ul class="daylist">
{% regroup type.list by timeslot.time|date:"l Y-M-d" as daylist %}
{% for day in daylist %}
<li class="daylistentry">
{{ day.grouper }}
<h3>{{ day.grouper }}</h3>
<table class="sessiontable">
{% for ss in day.list %}
<tr>

View file

@ -0,0 +1,106 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load ietf_filters %}
{% load staticfiles %}
{% block title %}
IETF {{ schedule.meeting.number }} meeting agenda
{% if "-utc" in request.path %}
(UTC)
{% endif %}
{% endblock %}
{% block morecss %}
.floor-plan {
position: relative;
top: 0;
left: 0;
}
.rooms a {
text-decoration: underline;
}
{% endblock %}
{% block bodyAttrs %}data-spy="scroll" data-target="#affix"{% endblock %}
{% block content %}
{% origin %}
<div class="row">
<div class="col-md-12" >
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting selected="floor-plan" title_extra="Floor Plan" %}
</div>
</div>
<div class="row">
<div class="col-md-10">
{% for floor in floors %}
<div class="anchor-target" id="{{floor.name|slugify}}"></div>
<h3>{{ floor.name }}</h3>
<div class="floor-plan">
<img class="col-md-12" src="{{ floor.image.url }}" >
<!-- We need as many of these as we can have individual rooms combining into one -->
<div id="{{floor.name|slugify}}-arrowdiv" style="position: absolute; left: 0; top: 67.5px; visibility: hidden;"><img id="arrow" src="{% static 'ietf/images/arrow-ani.gif' %}"></div>
<div id="{{floor.name|slugify}}-arrowdiv1" style="position: absolute; left: 0; top: 67.5px; visibility: hidden;"><img id="arrow" src="{% static 'ietf/images/arrow-ani.gif' %}"></div>
<div id="{{floor.name|slugify}}-arrowdiv2" style="position: absolute; left: 0; top: 67.5px; visibility: hidden;"><img id="arrow" src="{% static 'ietf/images/arrow-ani.gif' %}"></div>
<div id="{{floor.name|slugify}}-arrowdiv3" style="position: absolute; left: 0; top: 67.5px; visibility: hidden;"><img id="arrow" src="{% static 'ietf/images/arrow-ani.gif' %}"></div>
</div>
<div class="rooms">
{% for room in floor.room_set.all %}
{#<a href="javascript: setarrow('{{room.name|slugify}}','{{floor.name|slugify}}')">{{ room.name|nbsp }}</a>#}
{{ room.name|nbsp }}
{% endfor %}
</div>
<div class="row"></div>
{% endfor %}
</div>
<div class="col-md-2 hidden-print bs-docs-sidebar" id="affix">
<ul class="nav nav-pills nav-stacked small" data-spy="affix">
{% for floor in floors %}
<li><a href="#{{floor.name|slugify}}">{{ floor.name }}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}
{% block js %}
{% with meeting=schedule.meeting %}
<script src="{% static 'ietf/js/room_params.js' %}"></script>
<script>
// These must match the 'arrowdiv' divs above
var arrowsuffixlist = [ '', '1', '2', '3' ];
function roommap(nm)
{
var c = findroom(nm);
if (c) return nm;
var m = suffixmap(nm);
// alert("m=" + m);
return m;
}
function findroom(nm)
{
var left = 0, top = 0, right = 0, bottom = 0;
if (0) { }
{% for room in meeting.room_set.all %}
else if (nm == '{{room.name|slugify}}') { left = {{room.left}}; top = {{room.top}}; right = {{room.right}}; bottom = {{room.bottom}}; }{% endfor %}
{% for room in meeting.room_set.all %}{% if room.functional_display_name %}
else if (nm == '{{room.functional_name|slugify}}') { left = {{room.left}}; top = {{room.top}}; right = {{room.right}}; bottom = {{room.bottom}}; }{% endif %}{% endfor %}
else return null;
// alert("nm=" + nm + ",left=" + left + ",top=" + top + ",r=" + right + ",b=" + bottom);
return [left, top, right, bottom];
}
</script>
{% endwith %}
{% endblock %}

View file

@ -1,24 +1,57 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}{% load origin %}{% origin %}
{# assumes meeting is in context #}
{% if schedule != meeting.agenda %}
<h3 class="alert alert-danger text-center">
This is schedule {{schedule.owner.email}}/{{ schedule.name }}, not the official schedule.
</h3>
{% endif %}
{% load origin %}
{% load ietf_filters %}
<h1>
IETF {{ meeting.number }} Meeting Agenda
<br>
<small>
{{ meeting.city }}, {{ meeting.date|date:"F j" }} -
{% if meeting.date.month != meeting.end_date.month %}
{{ meeting.end_date|date:"F " }}
{% endif %}
{{ meeting.end_date|date:"j, Y" }}
<span class="pull-right">
Updated {{ updated|date:"Y-m-d \a\t G:i:s (T)" }}
</span>
{% origin %}
</small>
{% if schedule != meeting.agenda %}
<h3 class="alert alert-danger text-center">
This is schedule {{schedule.owner.email}}/{{ schedule.name }}, not the official schedule.
</h3>
{% endif %}
</h1>
<h1>
IETF {{ meeting.number }} Meeting Agenda {{ title_extra }}
<br>
<small>
{{ meeting.city }}, {{ meeting.date|date:"F j" }} -
{% if meeting.date.month != meeting.end_date.month %}
{{ meeting.end_date|date:"F " }}
{% endif %}
{{ meeting.end_date|date:"j, Y" }}
{% if updated %}
<span class="pull-right">
Updated {{ updated|date:"Y-m-d \a\t G:i:s (T)" }}
</span>
{% endif %}
</small>
</h1>
<ul class="nav nav-tabs" role="tablist">
<li {% if selected == "agenda" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.agenda' num=schedule.meeting.number %}">Agenda</a></li>
<li {% if selected == "agenda-utc" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.agenda' num=schedule.meeting.number utc='-utc' %}">UTC Agenda</a></li>
{% if user|has_role:"Secretariat,Area Director,IAB" %}
{% if schedule != meeting.agenda %}
<li {% if selected == "by-room" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.agenda_by_room' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">by Room</a></li>
<li {% if selected == "by-type" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.agenda_by_type' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">by Type</a></li>
<li {% if selected == "room-view" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.room_view' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">Room grid</a></li>
{% else %}
<li {% if selected == "by-room" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.agenda_by_room' num=schedule.meeting.number %}">by Room</a></li>
<li {% if selected == "by-type" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.agenda_by_type' num=schedule.meeting.number %}">by Type</a></li>
<li {% if selected == "room-view" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.room_view' num=schedule.meeting.number %}">Room grid</a></li>
{% endif %}
{% endif %}
<li {% if selected == "floor-plan" %}class="active"{% endif %}>
<a href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}">Floor plan</a></li>
<li><a href="{% url 'ietf.meeting.views.agenda' num=schedule.meeting.number ext='.txt' %}">Plaintext</a></li>
<li><a href="https://tools.ietf.org/agenda/{{schedule.meeting.number}}/">Tools-style &raquo;</a></li>
</ul>

View file

@ -6,7 +6,7 @@
{% block content %}
{% origin %}
{% include "meeting/meeting_heading.html" %}
{% include "meeting/meeting_heading.html" with title_extra="" selected="" %}
<div class="jumbotron">
<p>There is no agenda available yet.</p>

View file

@ -582,7 +582,7 @@
</script>
</head>
<body onload="draw_calendar()" onresize="draw_calendar()" id="body">
<div id="mtgheader" style="overflow:auto">{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=schedule.meeting.updated %}</div>
<div id="mtgheader" style="overflow:auto">{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=schedule.meeting.updated selected="room-view" title_extra="Room Grid" %}</div>
<div id="daycontainer" role="tabpanel">
<ul id="daytabs" class="nav nav-tabs" role="tablist">
{% for day in days %}