2017-05-31 02:38:59 +02:00
|
|
|
from contextlib import suppress
|
|
|
|
|
2017-05-11 22:40:48 +02:00
|
|
|
from django.apps import apps
|
2016-12-16 11:03:40 +01:00
|
|
|
from django.db import models
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
|
2017-07-10 13:01:35 +02:00
|
|
|
from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
|
2017-06-22 19:27:51 +02:00
|
|
|
from c3nav.mapdata.utils.models import get_submodels
|
2017-05-11 19:36:49 +02:00
|
|
|
|
2016-12-16 11:03:40 +01:00
|
|
|
|
2017-06-18 18:42:30 +02:00
|
|
|
class LocationSlugManager(models.Manager):
|
|
|
|
def get_queryset(self):
|
|
|
|
result = super().get_queryset()
|
|
|
|
if self.model == LocationSlug:
|
|
|
|
result = result.select_related(*(model._meta.default_related_name
|
2017-06-22 19:27:51 +02:00
|
|
|
for model in get_submodels(Location)+[LocationRedirect]))
|
2017-06-18 18:42:30 +02:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
2017-05-11 22:40:48 +02:00
|
|
|
class LocationSlug(SerializableMixin, models.Model):
|
|
|
|
LOCATION_TYPE_CODES = {
|
2017-06-11 14:43:14 +02:00
|
|
|
'Level': 'l',
|
|
|
|
'Space': 's',
|
2017-05-11 22:40:48 +02:00
|
|
|
'Area': 'a',
|
2017-07-08 16:29:12 +02:00
|
|
|
'POI': 'p',
|
2017-05-11 22:40:48 +02:00
|
|
|
'LocationGroup': 'g'
|
|
|
|
}
|
|
|
|
LOCATION_TYPE_BY_CODE = {code: model_name for model_name, code in LOCATION_TYPE_CODES.items()}
|
2017-07-08 16:50:37 +02:00
|
|
|
slug = models.SlugField(_('Slug'), unique=True, null=True, blank=True, max_length=50)
|
2017-06-18 16:52:50 +02:00
|
|
|
|
2017-06-18 18:42:30 +02:00
|
|
|
objects = LocationSlugManager()
|
2017-05-10 18:03:57 +02:00
|
|
|
|
2017-05-11 19:36:49 +02:00
|
|
|
def get_child(self):
|
2017-06-22 19:27:51 +02:00
|
|
|
for model in get_submodels(Location)+[LocationRedirect]:
|
2017-05-31 02:38:59 +02:00
|
|
|
with suppress(AttributeError):
|
2017-05-11 19:36:49 +02:00
|
|
|
return getattr(self, model._meta.default_related_name)
|
|
|
|
return None
|
|
|
|
|
2017-05-12 12:54:10 +02:00
|
|
|
def get_slug(self):
|
|
|
|
return self.slug
|
|
|
|
|
|
|
|
def _serialize(self, **kwargs):
|
|
|
|
result = super()._serialize(**kwargs)
|
|
|
|
result['slug'] = self.get_slug()
|
|
|
|
return result
|
|
|
|
|
2017-05-10 18:03:57 +02:00
|
|
|
class Meta:
|
2017-05-27 18:29:36 +02:00
|
|
|
verbose_name = _('Location with Slug')
|
2017-07-10 13:04:02 +02:00
|
|
|
verbose_name_plural = _('Location with Slug')
|
2017-05-10 18:03:57 +02:00
|
|
|
default_related_name = 'locationslugs'
|
|
|
|
|
|
|
|
|
2017-07-10 13:01:35 +02:00
|
|
|
class Location(LocationSlug, TitledMixin, models.Model):
|
2017-05-10 18:03:57 +02:00
|
|
|
can_search = models.BooleanField(default=True, verbose_name=_('can be searched'))
|
|
|
|
can_describe = models.BooleanField(default=True, verbose_name=_('can be used to describe a position'))
|
2017-05-27 18:37:26 +02:00
|
|
|
color = models.CharField(null=True, blank=True, max_length=16, verbose_name=_('background color'))
|
2017-05-10 18:03:57 +02:00
|
|
|
public = models.BooleanField(verbose_name=_('public'), default=True)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
abstract = True
|
|
|
|
|
2017-06-18 18:08:58 +02:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.titles = self.titles.copy()
|
|
|
|
|
2017-05-12 13:21:41 +02:00
|
|
|
def serialize(self, detailed=True, **kwargs):
|
2017-06-22 19:53:25 +02:00
|
|
|
result = super().serialize(detailed=detailed, **kwargs)
|
2017-05-12 13:21:41 +02:00
|
|
|
if not detailed:
|
2017-07-10 13:01:35 +02:00
|
|
|
result.pop('type', None)
|
|
|
|
result.pop('id', None)
|
|
|
|
result.pop('slug', None)
|
|
|
|
result.pop('target', None)
|
2017-05-12 13:21:41 +02:00
|
|
|
return result
|
|
|
|
|
2017-05-11 19:36:49 +02:00
|
|
|
def _serialize(self, **kwargs):
|
|
|
|
result = super()._serialize(**kwargs)
|
|
|
|
result['can_search'] = self.can_search
|
|
|
|
result['can_describe'] = self.can_search
|
|
|
|
result['color'] = self.color
|
|
|
|
result['public'] = self.public
|
2017-05-10 18:03:57 +02:00
|
|
|
return result
|
|
|
|
|
2017-05-11 22:40:48 +02:00
|
|
|
def get_slug(self):
|
|
|
|
if self.slug is None:
|
|
|
|
code = self.LOCATION_TYPE_CODES.get(self.__class__.__name__)
|
|
|
|
if code is not None:
|
|
|
|
return code+':'+str(self.id)
|
|
|
|
return self.slug
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_by_slug(cls, slug, queryset=None):
|
|
|
|
if queryset is None:
|
|
|
|
queryset = LocationSlug.objects.all()
|
|
|
|
|
|
|
|
if ':' in slug:
|
|
|
|
code, pk = slug.split(':', 1)
|
|
|
|
model_name = cls.LOCATION_TYPE_BY_CODE.get(code)
|
|
|
|
if model_name is None or not pk.isdigit():
|
|
|
|
return None
|
|
|
|
|
|
|
|
model = apps.get_model('mapdata', model_name)
|
|
|
|
try:
|
|
|
|
location = model.objects.get(pk=pk)
|
|
|
|
except model.DoesNotExist:
|
|
|
|
return None
|
|
|
|
|
|
|
|
if location.slug is not None:
|
|
|
|
return LocationRedirect(slug=slug, target=location)
|
|
|
|
|
|
|
|
return location
|
|
|
|
|
|
|
|
return queryset.filter(slug=slug).first()
|
|
|
|
|
2017-05-11 09:06:19 +02:00
|
|
|
@property
|
|
|
|
def title(self):
|
2017-07-10 13:01:35 +02:00
|
|
|
if not self.titles and self.slug:
|
2017-05-16 18:11:33 +02:00
|
|
|
return self._meta.verbose_name + ' ' + self.slug
|
|
|
|
return super().title
|
2017-05-10 18:03:57 +02:00
|
|
|
|
2017-06-16 18:38:41 +02:00
|
|
|
def get_color(self, instance=None):
|
2017-05-13 16:39:01 +02:00
|
|
|
if self.color:
|
|
|
|
return self.color
|
2017-05-29 16:36:17 +02:00
|
|
|
# dont filter in the query here so prefetch_related works
|
2017-06-16 18:38:41 +02:00
|
|
|
if instance is None:
|
|
|
|
instance = self
|
2017-07-11 18:01:48 +02:00
|
|
|
for group in instance.groups.all():
|
|
|
|
if group.color:
|
2017-05-29 16:36:17 +02:00
|
|
|
return group.color
|
2017-07-11 18:01:48 +02:00
|
|
|
return None
|
2017-05-13 16:39:01 +02:00
|
|
|
|
2017-05-10 18:03:57 +02:00
|
|
|
|
|
|
|
class SpecificLocation(Location, models.Model):
|
|
|
|
groups = models.ManyToManyField('mapdata.LocationGroup', verbose_name=_('Location Groups'), blank=True)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
abstract = True
|
|
|
|
|
2017-06-22 19:53:25 +02:00
|
|
|
def _serialize(self, detailed=True, **kwargs):
|
|
|
|
result = super()._serialize(detailed=detailed, **kwargs)
|
|
|
|
if detailed:
|
2017-07-10 16:30:38 +02:00
|
|
|
groups = {}
|
|
|
|
for group in self.groups.all():
|
|
|
|
groups.setdefault(group.category.name, []).append(group.pk)
|
|
|
|
result['groups'] = groups
|
2017-05-11 19:36:49 +02:00
|
|
|
return result
|
2016-12-16 11:03:40 +01:00
|
|
|
|
|
|
|
|
2017-07-10 13:54:33 +02:00
|
|
|
class LocationGroupCategory(TitledMixin, models.Model):
|
|
|
|
name = models.SlugField(_('Name'), unique=True, max_length=50)
|
2017-07-10 18:55:35 +02:00
|
|
|
single = models.BooleanField(_('single selection'), default=False)
|
|
|
|
allow_levels = models.BooleanField(_('allow levels'), db_index=True, default=True)
|
|
|
|
allow_spaces = models.BooleanField(_('allow spaces'), db_index=True, default=True)
|
|
|
|
allow_areas = models.BooleanField(_('allow areas'), db_index=True, default=True)
|
|
|
|
allow_pois = models.BooleanField(_('allow pois'), db_index=True, default=True)
|
|
|
|
priority = models.IntegerField(default=0, db_index=True)
|
2017-07-10 13:54:33 +02:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _('Location Group Category')
|
|
|
|
verbose_name_plural = _('Location Group Categories')
|
|
|
|
default_related_name = 'locationgroupcategories'
|
2017-07-10 19:16:35 +02:00
|
|
|
ordering = ('-priority', )
|
2017-07-10 13:54:33 +02:00
|
|
|
|
|
|
|
def _serialize(self, **kwargs):
|
|
|
|
result = super()._serialize(**kwargs)
|
|
|
|
result['name'] = self.name
|
|
|
|
result.move_to_end('name', last=False)
|
|
|
|
result.move_to_end('id', last=False)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2017-06-21 12:47:28 +02:00
|
|
|
class LocationGroup(Location, models.Model):
|
2017-07-10 13:54:33 +02:00
|
|
|
category = models.ForeignKey(LocationGroupCategory, related_name='groups', on_delete=models.PROTECT,
|
2017-07-10 14:10:48 +02:00
|
|
|
verbose_name=_('Category'))
|
2017-07-10 19:16:35 +02:00
|
|
|
priority = models.IntegerField(default=0, db_index=True)
|
2016-12-16 11:03:40 +01:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _('Location Group')
|
|
|
|
verbose_name_plural = _('Location Groups')
|
|
|
|
default_related_name = 'locationgroups'
|
2017-07-11 17:41:16 +02:00
|
|
|
ordering = ('-category__priority', '-priority')
|
2016-12-16 11:03:40 +01:00
|
|
|
|
2017-05-11 19:36:49 +02:00
|
|
|
def _serialize(self, **kwargs):
|
|
|
|
result = super()._serialize(**kwargs)
|
2017-07-10 13:54:33 +02:00
|
|
|
result['category'] = self.category_id
|
2017-05-11 19:36:49 +02:00
|
|
|
result['compiled_room'] = self.compiled_room
|
|
|
|
result['compiled_area'] = self.compiled_area
|
2016-12-23 22:42:42 +01:00
|
|
|
return result
|
|
|
|
|
2017-05-16 14:50:36 +02:00
|
|
|
@property
|
|
|
|
def title_for_forms(self):
|
|
|
|
attributes = []
|
|
|
|
if self.can_search:
|
|
|
|
attributes.append(_('search'))
|
|
|
|
if self.can_describe:
|
|
|
|
attributes.append(_('describe'))
|
|
|
|
if self.color:
|
|
|
|
attributes.append(_('color'))
|
|
|
|
if not attributes:
|
|
|
|
attributes.append(_('internal'))
|
|
|
|
if self.compiled_room:
|
|
|
|
attributes.append(_('comp. room'))
|
|
|
|
if self.compiled_area:
|
|
|
|
attributes.append(_('comp. area'))
|
|
|
|
return self.title + ' ('+', '.join(str(s) for s in attributes)+')'
|
|
|
|
|
|
|
|
|
2017-05-11 22:40:48 +02:00
|
|
|
class LocationRedirect(LocationSlug):
|
2017-07-10 13:54:33 +02:00
|
|
|
target = models.ForeignKey(LocationSlug, related_name='redirects', on_delete=models.CASCADE,
|
|
|
|
verbose_name=_('target'))
|
2017-05-11 22:40:48 +02:00
|
|
|
|
|
|
|
def _serialize(self, with_type=True, **kwargs):
|
|
|
|
result = super()._serialize(with_type=with_type, **kwargs)
|
|
|
|
if type(self.target) == LocationSlug:
|
|
|
|
result['target'] = self.target.get_child().slug
|
|
|
|
else:
|
|
|
|
result['target'] = self.target.slug
|
|
|
|
if with_type:
|
|
|
|
result['type'] = 'redirect'
|
|
|
|
result.pop('id')
|
|
|
|
return result
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
default_related_name = 'redirect'
|