diff --git a/src/c3nav/editor/api.py b/src/c3nav/editor/api.py index fe976f10..fcd46cf4 100644 --- a/src/c3nav/editor/api.py +++ b/src/c3nav/editor/api.py @@ -7,7 +7,7 @@ from rest_framework.response import Response from rest_framework.viewsets import ViewSet from shapely.ops import cascaded_union -from c3nav.mapdata.models import Level, Space +from c3nav.editor.models import ChangeSet class EditorViewSet(ViewSet): @@ -16,7 +16,7 @@ class EditorViewSet(ViewSet): /geometries/ returns a list of geojson features, you have to specify ?level= or ?space= /geometrystyles/ returns styling information for all geometry types """ - def _get_level_geometries(self, level: Level): + def _get_level_geometries(self, level): buildings = level.buildings.all() buildings_geom = cascaded_union([building.geometry for building in buildings]) spaces = {space.id: space for space in level.spaces.all()} @@ -44,10 +44,11 @@ class EditorViewSet(ViewSet): results.extend(spaces.values()) return results - def _get_levels_pk(self, level): + def _get_levels_pk(self, request, level): + Level = request.changeset.wrap('Level') levels_under = () levels_on_top = () - lower_level = level.lower().first() + lower_level = level.lower(Level).first() primary_levels = (level,) + ((lower_level,) if lower_level else ()) secondary_levels = Level.objects.filter(on_top_of__in=primary_levels).values_list('pk', 'on_top_of') if lower_level: @@ -59,6 +60,11 @@ class EditorViewSet(ViewSet): @list_route(methods=['get']) def geometries(self, request, *args, **kwargs): + request.changeset = ChangeSet.get_for_request(request) + + Level = request.changeset.wrap('Level') + Space = request.changeset.wrap('Space') + level = request.GET.get('level') space = request.GET.get('space') if level is not None: @@ -66,7 +72,7 @@ class EditorViewSet(ViewSet): raise ValidationError('Only level or space can be specified.') level = get_object_or_404(Level, pk=level) - levels, levels_on_top, levels_under = self._get_levels_pk(level) + levels, levels_on_top, levels_under = self._get_levels_pk(request, level) levels = Level.objects.filter(pk__in=levels).prefetch_related('buildings', 'spaces', 'doors', 'spaces__groups', 'spaces__holes', 'spaces__columns') @@ -90,7 +96,7 @@ class EditorViewSet(ViewSet): doors = [door for door in level.doors.all() if door.geometry.intersects(space.geometry)] doors_space_geom = cascaded_union([door.geometry for door in doors]+[space.geometry]) - levels, levels_on_top, levels_under = self._get_levels_pk(level.primary_level) + levels, levels_on_top, levels_under = self._get_levels_pk(request, level.primary_level) other_spaces = Space.objects.filter(level__pk__in=levels).prefetch_related('groups') other_spaces = [s for s in other_spaces if s.geometry.intersects(doors_space_geom) and s.pk != space.pk] diff --git a/src/c3nav/editor/wrappers.py b/src/c3nav/editor/wrappers.py index e51b89a0..0d95b155 100644 --- a/src/c3nav/editor/wrappers.py +++ b/src/c3nav/editor/wrappers.py @@ -1,3 +1,4 @@ +from collections import Iterable from django.db import models from django.db.models import Manager @@ -34,7 +35,8 @@ class BaseWrapper: elif isinstance(value, type) and issubclass(value, Exception): pass elif callable(value) and name not in self._allowed_callables: - raise TypeError('Can not call %s.%s wrapped!' % (self._obj, name)) + if not isinstance(self, ModelInstanceWrapper) or hasattr(models.Model, name): + raise TypeError('Can not call %s.%s wrapped!' % (self._obj, name)) # print(self._obj, name, type(value), value) return value @@ -110,6 +112,9 @@ class BaseQueryWrapper(BaseWrapper): def filter(self, *args, **kwargs): kwargs = {name: (value._obj if isinstance(value, ModelInstanceWrapper) else value) for name, value in kwargs.items()} + kwargs = {name: ([(item._obj if isinstance(item, ModelInstanceWrapper) else item) for item in value] + if isinstance(value, Iterable) else value) + for name, value in kwargs.items()} return self._wrap_queryset(self._obj.filter(*args, **kwargs)) def count(self): @@ -118,6 +123,12 @@ class BaseQueryWrapper(BaseWrapper): def values_list(self, *args, flat=False): return self._obj.values_list(*args, flat=flat) + def first(self): + first = self._obj.first() + if first is not None: + first = self._wrap_instance(first) + return first + def __iter__(self): return iter([instance for instance in self._obj]) diff --git a/src/c3nav/mapdata/models/level.py b/src/c3nav/mapdata/models/level.py index f938c593..26c67dd4 100644 --- a/src/c3nav/mapdata/models/level.py +++ b/src/c3nav/mapdata/models/level.py @@ -28,14 +28,18 @@ class Level(SpecificLocation, EditorFormMixin, models.Model): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def lower(self): + def lower(self, level_model=None): if self.on_top_of_id is not None: raise TypeError + if level_model is not None: + Level = level_model return Level.objects.filter(altitude__lt=self.altitude, on_top_of__isnull=True).order_by('-altitude') - def higher(self): + def higher(self, level_model=None): if self.on_top_of_id is not None: raise TypeError + if level_model is not None: + Level = level_model return Level.objects.filter(altitude__gt=self.altitude, on_top_of__isnull=True).order_by('altitude') @property