add Location.details_display()
This commit is contained in:
parent
ba3e1e485e
commit
949b88389e
8 changed files with 114 additions and 78 deletions
|
@ -1,17 +1,15 @@
|
||||||
import mimetypes
|
import mimetypes
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from operator import attrgetter
|
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db.models import FieldDoesNotExist, Model, Prefetch
|
from django.db.models import Prefetch
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.cache import get_conditional_response
|
from django.utils.cache import get_conditional_response
|
||||||
from django.utils.http import quote_etag
|
from django.utils.http import quote_etag
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import get_language, get_language_info
|
from django.utils.translation import get_language
|
||||||
from rest_framework.decorators import detail_route, list_route
|
from rest_framework.decorators import detail_route, list_route
|
||||||
from rest_framework.exceptions import NotFound, ValidationError
|
from rest_framework.exceptions import NotFound, ValidationError
|
||||||
from rest_framework.mixins import RetrieveModelMixin
|
from rest_framework.mixins import RetrieveModelMixin
|
||||||
|
@ -19,7 +17,7 @@ from rest_framework.response import Response
|
||||||
from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet, ViewSet
|
from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet, ViewSet
|
||||||
|
|
||||||
from c3nav.mapdata.models import AccessRestriction, Building, Door, Hole, LocationGroup, MapUpdate, Source, Space
|
from c3nav.mapdata.models import AccessRestriction, Building, Door, Hole, LocationGroup, MapUpdate, Source, Space
|
||||||
from c3nav.mapdata.models.access import AccessPermission, AccessRestrictionMixin
|
from c3nav.mapdata.models.access import AccessPermission
|
||||||
from c3nav.mapdata.models.geometry.level import LevelGeometryMixin
|
from c3nav.mapdata.models.geometry.level import LevelGeometryMixin
|
||||||
from c3nav.mapdata.models.geometry.space import POI, Area, Column, LineObstacle, Obstacle, SpaceGeometryMixin, Stair
|
from c3nav.mapdata.models.geometry.space import POI, Area, Column, LineObstacle, Obstacle, SpaceGeometryMixin, Stair
|
||||||
from c3nav.mapdata.models.level import Level
|
from c3nav.mapdata.models.level import Level
|
||||||
|
@ -301,78 +299,7 @@ class LocationViewSet(RetrieveModelMixin, GenericViewSet):
|
||||||
if isinstance(location, LocationRedirect):
|
if isinstance(location, LocationRedirect):
|
||||||
return redirect('../' + location.target.slug + '/display/')
|
return redirect('../' + location.target.slug + '/display/')
|
||||||
|
|
||||||
result = location.serialize(geometry=True)
|
return Response(location.details_display())
|
||||||
|
|
||||||
display = [
|
|
||||||
(str(_('Type')), str(location.__class__._meta.verbose_name)),
|
|
||||||
(str(_('ID')), str(location.pk)),
|
|
||||||
]
|
|
||||||
|
|
||||||
for lang, title in sorted(location.titles.items(), key=lambda item: item[0] != get_language()):
|
|
||||||
language = _('Title ({lang})').format(lang=get_language_info(lang)['name_translated'])
|
|
||||||
display.append((language, title))
|
|
||||||
|
|
||||||
display.append((str(_('Slug')), str(location.get_slug())))
|
|
||||||
|
|
||||||
if isinstance(location, Level):
|
|
||||||
display.append((str(_('short label')), location.short_label))
|
|
||||||
|
|
||||||
if isinstance(location, LevelGeometryMixin):
|
|
||||||
display.append((str(_('Level')), {'slug': location.level.get_slug(), 'title': location.level.title}))
|
|
||||||
|
|
||||||
if isinstance(location, SpaceGeometryMixin):
|
|
||||||
display.append((str(_('Space')), {'slug': location.space.get_slug(), 'title': location.space.title}))
|
|
||||||
|
|
||||||
if isinstance(location, LocationGroup):
|
|
||||||
display.append((str(_('Category')), location.category.title))
|
|
||||||
|
|
||||||
if isinstance(location, AccessRestrictionMixin):
|
|
||||||
display.append((str(_('Access Restriction')),
|
|
||||||
location.access_restriction_id and location.access_restriction.title))
|
|
||||||
|
|
||||||
groupcategories = {}
|
|
||||||
if isinstance(location, SpecificLocation):
|
|
||||||
for group in location.groups.all():
|
|
||||||
groupcategories.setdefault(group.category, []).append(group)
|
|
||||||
|
|
||||||
for category, groups in sorted(groupcategories.items(), key=lambda item: item[0].priority, reverse=True):
|
|
||||||
display.append((category.title, tuple(
|
|
||||||
{'slug': group.get_slug(), 'title': group.title}
|
|
||||||
for group in sorted(groups, key=attrgetter('priority'), reverse=True)
|
|
||||||
)))
|
|
||||||
|
|
||||||
model: Model = location.__class__
|
|
||||||
for name in ('can_search', 'can_describe', 'color', 'outside', 'base_altitude', 'height', 'default_height',
|
|
||||||
'priority'):
|
|
||||||
try:
|
|
||||||
field = model._meta.get_field(name)
|
|
||||||
except FieldDoesNotExist:
|
|
||||||
continue
|
|
||||||
|
|
||||||
value = getattr(location, name)
|
|
||||||
if isinstance(value, bool):
|
|
||||||
value = _('Ja') if value else _('Nein')
|
|
||||||
display.append((str(field.verbose_name), value and str(value)))
|
|
||||||
|
|
||||||
editor_url = None
|
|
||||||
if isinstance(location, Level):
|
|
||||||
editor_url = reverse('editor.levels.detail', kwargs={'pk': location.pk})
|
|
||||||
elif isinstance(location, Space):
|
|
||||||
editor_url = reverse('editor.spaces.detail', kwargs={'level': location.level_id, 'pk': location.pk})
|
|
||||||
elif isinstance(location, Area):
|
|
||||||
editor_url = reverse('editor.areas.edit', kwargs={'space': location.space_id, 'pk': location.pk})
|
|
||||||
elif isinstance(location, POI):
|
|
||||||
editor_url = reverse('editor.pois.edit', kwargs={'space': location.space_id, 'pk': location.pk})
|
|
||||||
elif isinstance(location, LocationGroup):
|
|
||||||
editor_url = reverse('editor.locationgroups.edit', kwargs={'pk': location.pk})
|
|
||||||
|
|
||||||
result = {
|
|
||||||
'editor_url': editor_url,
|
|
||||||
'display': display,
|
|
||||||
'geometry': result['geometry'],
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response(result)
|
|
||||||
|
|
||||||
@list_route(methods=['get'])
|
@list_route(methods=['get'])
|
||||||
@api_etag(permissions=False)
|
@api_etag(permissions=False)
|
||||||
|
|
|
@ -89,6 +89,13 @@ class AccessRestrictionMixin(SerializableMixin, models.Model):
|
||||||
result['access_restriction'] = self.access_restriction_id
|
result['access_restriction'] = self.access_restriction_id
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['display'].extend([
|
||||||
|
(str(_('Access Restriction')), self.access_restriction_id and self.access_restriction.title),
|
||||||
|
])
|
||||||
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def qs_for_request(cls, request, allow_none=False):
|
def qs_for_request(cls, request, allow_none=False):
|
||||||
return cls.objects.filter(cls.q_for_request(request, allow_none=allow_none))
|
return cls.objects.filter(cls.q_for_request(request, allow_none=allow_none))
|
||||||
|
|
|
@ -3,7 +3,7 @@ from collections import OrderedDict
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import get_language
|
from django.utils.translation import get_language, get_language_info
|
||||||
|
|
||||||
from c3nav.mapdata.fields import JSONField
|
from c3nav.mapdata.fields import JSONField
|
||||||
from c3nav.mapdata.models import MapUpdate
|
from c3nav.mapdata.models import MapUpdate
|
||||||
|
@ -32,6 +32,14 @@ class SerializableMixin(models.Model):
|
||||||
result['id'] = self.pk
|
result['id'] = self.pk
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
return {
|
||||||
|
'display': [
|
||||||
|
(str(_('Type')), str(self.__class__._meta.verbose_name)),
|
||||||
|
(str(_('ID')), str(self.pk)),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
return self._meta.verbose_name + ' ' + str(self.id)
|
return self._meta.verbose_name + ' ' + str(self.id)
|
||||||
|
@ -57,6 +65,13 @@ class TitledMixin(SerializableMixin, models.Model):
|
||||||
result['title'] = self.title
|
result['title'] = self.title
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
for lang, title in sorted(self.titles.items(), key=lambda item: item[0] != get_language()):
|
||||||
|
language = _('Title ({lang})').format(lang=get_language_info(lang)['name_translated'])
|
||||||
|
result['display'].append((language, title))
|
||||||
|
return result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
lang = get_language()
|
lang = get_language()
|
||||||
|
|
|
@ -88,6 +88,11 @@ class GeometryMixin(SerializableMixin):
|
||||||
(int(math.ceil(self.maxx)), int(math.ceil(self.maxy))))
|
(int(math.ceil(self.maxx)), int(math.ceil(self.maxy))))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['geometry'] = format_geojson(mapping(self.geometry), round=False)
|
||||||
|
return result
|
||||||
|
|
||||||
def get_shadow_geojson(self):
|
def get_shadow_geojson(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from operator import attrgetter, itemgetter
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.text import format_lazy
|
from django.utils.text import format_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from scipy.sparse.csgraph._shortest_path import dijkstra
|
from scipy.sparse.csgraph._shortest_path import dijkstra
|
||||||
|
@ -43,6 +44,11 @@ class LevelGeometryMixin(GeometryMixin):
|
||||||
result['level'] = self.level_id
|
result['level'] = self.level_id
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['display'].insert(3, (str(_('Level')), {'slug': self.level.get_slug(), 'title': self.level.title}))
|
||||||
|
return result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def subtitle(self):
|
def subtitle(self):
|
||||||
base_subtitle = super().subtitle
|
base_subtitle = super().subtitle
|
||||||
|
@ -96,6 +102,15 @@ class Space(LevelGeometryMixin, SpecificLocation, models.Model):
|
||||||
result['height'] = None if self.height is None else float(str(self.height))
|
result['height'] = None if self.height is None else float(str(self.height))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['display'].extend([
|
||||||
|
(str(_('height')), self.height),
|
||||||
|
(str(_('outside only')), str(_('yes') if self.outside else _('no'))),
|
||||||
|
])
|
||||||
|
result['editor_url'] = reverse('editor.spaces.detail', kwargs={'level': self.level_id, 'pk': self.pk})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class Door(LevelGeometryMixin, AccessRestrictionMixin, models.Model):
|
class Door(LevelGeometryMixin, AccessRestrictionMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.text import format_lazy
|
from django.utils.text import format_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -52,6 +53,11 @@ class SpaceGeometryMixin(GeometryMixin):
|
||||||
self.geometry if force else self.get_changed_geometry()
|
self.geometry if force else self.get_changed_geometry()
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['display'].insert(3, (str(_('Space')), {'slug': self.space.get_slug(), 'title': self.space.title}))
|
||||||
|
return result
|
||||||
|
|
||||||
def register_delete(self):
|
def register_delete(self):
|
||||||
space = self.space
|
space = self.space
|
||||||
changed_geometries.register(space.level_id, space.geometry.intersection(self.geometry))
|
changed_geometries.register(space.level_id, space.geometry.intersection(self.geometry))
|
||||||
|
@ -88,6 +94,11 @@ class Area(SpaceGeometryMixin, SpecificLocation, models.Model):
|
||||||
result = super()._serialize(**kwargs)
|
result = super()._serialize(**kwargs)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['editor_url'] = reverse('editor.areas.edit', kwargs={'space': self.space_id, 'pk': self.pk})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class Stair(SpaceGeometryMixin, models.Model):
|
class Stair(SpaceGeometryMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
@ -168,6 +179,11 @@ class POI(SpaceGeometryMixin, SpecificLocation, models.Model):
|
||||||
verbose_name_plural = _('Points of Interest')
|
verbose_name_plural = _('Points of Interest')
|
||||||
default_related_name = 'pois'
|
default_related_name = 'pois'
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['editor_url'] = reverse('editor.pois.edit', kwargs={'space': self.space_id, 'pk': self.pk})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class Hole(SpaceGeometryMixin, models.Model):
|
class Hole(SpaceGeometryMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,6 +6,7 @@ from operator import attrgetter, itemgetter
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from shapely.geometry import JOIN_STYLE, box
|
from shapely.geometry import JOIN_STYLE, box
|
||||||
|
@ -88,6 +89,16 @@ class Level(SpecificLocation, models.Model):
|
||||||
result['default_height'] = float(str(self.default_height))
|
result['default_height'] = float(str(self.default_height))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['display'].insert(3, (str(_('short label')), self.short_label))
|
||||||
|
result['display'].extend([
|
||||||
|
(str(_('outside only')), self.base_altitude),
|
||||||
|
(str(_('default height')), self.default_height),
|
||||||
|
])
|
||||||
|
result['editor_url'] = reverse('editor.levels.detail', kwargs={'pk': self.pk})
|
||||||
|
return result
|
||||||
|
|
||||||
def _render_space_ground(self, svg, space):
|
def _render_space_ground(self, svg, space):
|
||||||
areas_by_color = {}
|
areas_by_color = {}
|
||||||
for area in space.areas.all():
|
for area in space.areas.all():
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
from operator import attrgetter
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.text import format_lazy
|
from django.utils.text import format_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -57,6 +59,11 @@ class LocationSlug(SerializableMixin, models.Model):
|
||||||
result['slug'] = self.get_slug()
|
result['slug'] = self.get_slug()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['display'].insert(2, (str(_('Slug')), str(self.get_slug())))
|
||||||
|
return result
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def order(self):
|
def order(self):
|
||||||
return (-1, 0)
|
return (-1, 0)
|
||||||
|
@ -94,6 +101,14 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model):
|
||||||
result['can_describe'] = self.can_search
|
result['can_describe'] = self.can_search
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['display'].extend([
|
||||||
|
(str(_('can be searched')), str(_('yes') if self.can_search else _('no'))),
|
||||||
|
(str(_('can describe')), str(_('yes') if self.can_describe else _('no')))
|
||||||
|
])
|
||||||
|
return result
|
||||||
|
|
||||||
def get_slug(self):
|
def get_slug(self):
|
||||||
if self.slug is None:
|
if self.slug is None:
|
||||||
code = self.LOCATION_TYPE_CODES.get(self.__class__.__name__)
|
code = self.LOCATION_TYPE_CODES.get(self.__class__.__name__)
|
||||||
|
@ -139,6 +154,21 @@ class SpecificLocation(Location, models.Model):
|
||||||
result['groups'] = groups
|
result['groups'] = groups
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
|
||||||
|
groupcategories = {}
|
||||||
|
for group in self.groups.all():
|
||||||
|
groupcategories.setdefault(group.category, []).append(group)
|
||||||
|
|
||||||
|
for category, groups in sorted(groupcategories.items(), key=lambda item: item[0].priority):
|
||||||
|
result['display'].insert(3, (category.title, tuple(
|
||||||
|
{'slug': group.get_slug(), 'title': group.title}
|
||||||
|
for group in sorted(groups, key=attrgetter('priority'), reverse=True)
|
||||||
|
)))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def subtitle(self):
|
def subtitle(self):
|
||||||
groups = tuple(self.groups.all())
|
groups = tuple(self.groups.all())
|
||||||
|
@ -230,6 +260,16 @@ class LocationGroup(Location, models.Model):
|
||||||
result['locations'] = tuple(obj.pk for obj in getattr(self, 'locations', ()))
|
result['locations'] = tuple(obj.pk for obj in getattr(self, 'locations', ()))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def details_display(self):
|
||||||
|
result = super().details_display()
|
||||||
|
result['display'].insert(3, (str(_('Category')), self.category.title))
|
||||||
|
result['display'].extend([
|
||||||
|
(str(_('color')), self.color),
|
||||||
|
(str(_('priority')), self.priority),
|
||||||
|
])
|
||||||
|
result['editor_url'] = reverse('editor.locationgroups.edit', kwargs={'pk': self.pk})
|
||||||
|
return result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title_for_forms(self):
|
def title_for_forms(self):
|
||||||
attributes = []
|
attributes = []
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue