From 9e5d99095445012335fc7a530bbbcd40923f96d9 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz <henrik@levkowetz.com> Date: Thu, 7 Jul 2016 12:37:09 +0000 Subject: [PATCH] Added meeting FloorPlan model and added location parameters and ordering to the Room model. - Legacy-Id: 11540 --- ietf/meeting/admin.py | 10 +++- ...0025_add_floorplan_and_room_coordinates.py | 59 +++++++++++++++++++ ietf/meeting/models.py | 43 ++++++++++++++ ietf/meeting/resources.py | 20 ++++++- 4 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 ietf/meeting/migrations/0025_add_floorplan_and_room_coordinates.py diff --git a/ietf/meeting/admin.py b/ietf/meeting/admin.py index 31a842c72..e575eeef0 100644 --- a/ietf/meeting/admin.py +++ b/ietf/meeting/admin.py @@ -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) diff --git a/ietf/meeting/migrations/0025_add_floorplan_and_room_coordinates.py b/ietf/meeting/migrations/0025_add_floorplan_and_room_coordinates.py new file mode 100644 index 000000000..8c41e29df --- /dev/null +++ b/ietf/meeting/migrations/0025_add_floorplan_and_room_coordinates.py @@ -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, + ), + ] diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index 5db515988..67bc8fb21 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -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 + diff --git a/ietf/meeting/resources.py b/ietf/meeting/resources.py index e83e6bbca..2ba4456b6 100644 --- a/ietf/meeting/resources.py +++ b/ietf/meeting/resources.py @@ -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())