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

208 lines
6.9 KiB
Python
Raw Normal View History

from contextlib import suppress
from django.apps import apps
from django.db import models
from django.utils.translation import ugettext_lazy as _
2017-05-12 13:52:01 +02:00
from django.utils.translation import get_language
2017-05-11 19:36:49 +02:00
from c3nav.mapdata.fields import JSONField
from c3nav.mapdata.models.base import SerializableMixin
2017-06-22 19:27:51 +02:00
from c3nav.mapdata.utils.models import get_submodels
2017-05-11 19:36:49 +02: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
class LocationSlug(SerializableMixin, models.Model):
LOCATION_TYPE_CODES = {
2017-06-11 14:43:14 +02:00
'Level': 'l',
'Space': 's',
'Area': 'a',
2017-07-08 16:29:12 +02:00
'POI': 'p',
'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]:
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:
verbose_name = _('Location with Slug')
verbose_name_plural = _('Lucation with Slug')
2017-05-10 18:03:57 +02:00
default_related_name = 'locationslugs'
class Location(LocationSlug, SerializableMixin, models.Model):
2017-05-10 18:03:57 +02:00
titles = JSONField(default={})
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
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):
result = super().serialize(detailed=detailed, **kwargs)
2017-05-12 13:21:41 +02:00
if not detailed:
for key in set(result.keys()) - {'type', 'id', 'slug', 'title', 'target'}:
result.pop(key)
return result
2017-05-11 19:36:49 +02:00
def _serialize(self, **kwargs):
result = super()._serialize(**kwargs)
result['titles'] = self.titles
2017-05-12 13:15:13 +02:00
result['title'] = self.title
2017-05-11 19:36:49 +02:00
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
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):
lang = get_language()
if self.titles:
if lang in self.titles:
return self.titles[lang]
return next(iter(self.titles.values()))
if self.slug:
return self._meta.verbose_name + ' ' + self.slug
return super().title
2017-05-10 18:03:57 +02:00
def get_color(self, instance=None):
2017-05-13 16:39:01 +02:00
if self.color:
return self.color
# dont filter in the query here so prefetch_related works
if instance is None:
instance = self
2017-06-17 23:40:23 +02:00
groups = [group for group in instance.groups.all() if group.color]
if not groups:
return None
for group in groups:
if group.compiled_area:
return group.color
for group in groups:
if group.compiled_room:
return group.color
return groups[0].color
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
def _serialize(self, detailed=True, **kwargs):
result = super()._serialize(detailed=detailed, **kwargs)
if detailed:
result['groups'] = list(g.pk for g in self.groups.all())
2017-05-11 19:36:49 +02:00
return result
class LocationGroup(Location, models.Model):
2016-12-24 21:41:57 +01:00
compiled_room = models.BooleanField(default=False, verbose_name=_('is a compiled room'))
2017-05-10 21:24:31 +02:00
compiled_area = models.BooleanField(default=False, verbose_name=_('is a compiled area'))
class Meta:
verbose_name = _('Location Group')
verbose_name_plural = _('Location Groups')
default_related_name = 'locationgroups'
2017-05-11 19:36:49 +02:00
def _serialize(self, **kwargs):
result = super()._serialize(**kwargs)
result['compiled_room'] = self.compiled_room
result['compiled_area'] = self.compiled_area
return result
@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)+')'
class LocationRedirect(LocationSlug):
target = models.ForeignKey(LocationSlug, verbose_name=_('target'), related_name='redirects')
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'