team-3/src/c3nav/mapdata/models/level.py

99 lines
3.9 KiB
Python
Raw Normal View History

from decimal import Decimal
2017-06-11 00:00:25 +02:00
from itertools import chain
2017-11-09 17:32:39 +01:00
from operator import attrgetter
2017-06-11 00:00:25 +02:00
from django.core.validators import MinValueValidator
2016-08-29 18:49:24 +02:00
from django.db import models
2017-11-02 13:35:58 +01:00
from django.urls import reverse
2017-08-07 14:55:08 +02:00
from django.utils.functional import cached_property
2016-08-29 18:49:24 +02:00
from django.utils.translation import ugettext_lazy as _
2016-11-26 13:48:44 +01:00
from shapely.ops import cascaded_union
2016-08-29 18:49:24 +02:00
2017-05-10 18:03:57 +02:00
from c3nav.mapdata.models.locations import SpecificLocation
2016-08-29 18:49:24 +02:00
class Level(SpecificLocation, models.Model):
2016-08-29 18:49:24 +02:00
"""
2017-06-11 14:43:14 +02:00
A map level
2016-08-29 18:49:24 +02:00
"""
2017-08-05 16:49:10 +02:00
base_altitude = models.DecimalField(_('base altitude'), null=False, unique=True, max_digits=6, decimal_places=2)
default_height = models.DecimalField(_('default space height'), max_digits=6, decimal_places=2, default=3.0,
validators=[MinValueValidator(Decimal('0'))])
door_height = models.DecimalField(_('door height'), max_digits=6, decimal_places=2, default=2.0,
validators=[MinValueValidator(Decimal('0'))])
2017-06-11 14:43:14 +02:00
on_top_of = models.ForeignKey('mapdata.Level', null=True, on_delete=models.CASCADE,
related_name='levels_on_top', verbose_name=_('on top of'))
short_label = models.SlugField(max_length=20, verbose_name=_('short label'), unique=True)
2016-08-29 18:49:24 +02:00
class Meta:
2017-06-11 14:43:14 +02:00
verbose_name = _('Level')
verbose_name_plural = _('Levels')
default_related_name = 'levels'
2017-08-05 16:49:10 +02:00
ordering = ['base_altitude']
2016-09-24 14:09:52 +02:00
2016-11-26 13:48:44 +01:00
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
2016-12-04 01:49:49 +01:00
2017-06-12 23:33:59 +02:00
def lower(self, level_model=None):
2017-06-11 15:37:25 +02:00
if self.on_top_of_id is not None:
raise TypeError
if level_model is None:
level_model = Level
2017-08-05 16:49:10 +02:00
return level_model.objects.filter(base_altitude__lt=self.base_altitude,
on_top_of__isnull=True).order_by('-base_altitude')
2016-12-04 01:49:49 +01:00
2017-06-12 23:33:59 +02:00
def higher(self, level_model=None):
2017-06-11 15:37:25 +02:00
if self.on_top_of_id is not None:
raise TypeError
if level_model is None:
level_model = Level
2017-08-05 16:49:10 +02:00
return level_model.objects.filter(base_altitude__gt=self.base_altitude,
on_top_of__isnull=True).order_by('base_altitude')
2016-12-04 01:49:49 +01:00
2017-06-11 00:00:25 +02:00
@property
2017-06-11 14:43:14 +02:00
def sublevels(self):
2017-06-11 00:00:25 +02:00
if self.on_top_of is not None:
raise TypeError
2017-06-11 14:43:14 +02:00
return chain((self, ), self.levels_on_top.all())
2017-06-11 00:00:25 +02:00
@property
2017-06-11 14:43:14 +02:00
def sublevel_title(self):
2017-06-11 00:00:25 +02:00
return '-' if self.on_top_of_id is None else self.title
@property
2017-06-11 14:43:14 +02:00
def primary_level(self):
2017-06-11 00:00:25 +02:00
return self if self.on_top_of_id is None else self.on_top_of
2017-06-10 23:04:28 +02:00
@property
2017-06-11 14:43:14 +02:00
def primary_level_pk(self):
2017-06-10 23:04:28 +02:00
return self.pk if self.on_top_of_id is None else self.on_top_of_id
2017-06-11 14:43:14 +02:00
def _serialize(self, level=True, **kwargs):
2017-05-11 19:36:49 +02:00
result = super()._serialize(**kwargs)
2017-10-25 12:29:08 +02:00
result['short_label'] = self.short_label
result['on_top_of'] = self.on_top_of_id
2017-08-05 16:49:10 +02:00
result['base_altitude'] = float(str(self.base_altitude))
2017-08-21 16:05:31 +02:00
result['default_height'] = float(str(self.default_height))
result['door_height'] = float(str(self.door_height))
2017-05-11 19:36:49 +02:00
return result
2016-12-01 12:25:02 +01:00
2018-09-19 19:08:47 +02:00
def details_display(self, editor_url=True, **kwargs):
result = super().details_display(**kwargs)
result['display'].insert(3, (_('short label'), self.short_label))
2017-11-02 13:35:58 +01:00
result['display'].extend([
(_('outside only'), self.base_altitude),
(_('default height'), self.default_height),
2017-11-02 13:35:58 +01:00
])
2018-09-19 19:08:47 +02:00
if editor_url:
result['editor_url'] = reverse('editor.levels.detail', kwargs={'pk': self.pk})
2017-11-02 13:35:58 +01:00
return result
2017-08-07 14:55:08 +02:00
@cached_property
def min_altitude(self):
return min(self.altitudeareas.all(), key=attrgetter('altitude'), default=self.base_altitude).altitude
@cached_property
def bounds(self):
return cascaded_union(tuple(item.geometry.buffer(0)
for item in chain(self.altitudeareas.all(), self.buildings.all()))).bounds