def get_user_data(request): permissions = AccessPermission.get_for_request(request) result = { 'logged_in': bool(request.user.is_authenticated), 'allow_editor': can_access_editor(request), 'allow_control_panel': request.user_permissions.control_panel, 'has_positions': Position.user_has_positions(request.user) } if permissions: result.update({ 'title': _('not logged in'), 'subtitle': ungettext_lazy('%d area unlocked', '%d areas unlocked', len(permissions)) % len(permissions), 'permissions': tuple(permissions), }) else: result.update({ 'title': _('Login'), 'subtitle': None, 'permissions': (), }) if request.user.is_authenticated: result['title'] = request.user.username return result
def get_location_by_slug_for_request(slug: str, request) -> Optional[LocationSlug]: cache_key = 'mapdata:location:by_slug:%s:%s' % ( AccessPermission.cache_key_for_request(request), slug) location = cache.get(cache_key, None) if location is not None: return location if slug.startswith('c:'): location = get_custom_location_for_request(slug, request) if location is None: return None elif ':' in slug: code, pk = slug.split(':', 1) model_name = LocationSlug.LOCATION_TYPE_BY_CODE.get(code) if model_name is None or not pk.isdigit(): return None model = apps.get_model('mapdata', model_name) location = locations_for_request(request).get(int(pk), None) if location is None or not isinstance(location, model): return None if location.slug is not None: location = LocationRedirect(slug=slug, target=location) else: location = locations_by_slug_for_request(request).get(slug, None) cache.set(cache_key, location, 300) return location
def get_location_by_slug_for_request(slug: str, request) -> Optional[LocationSlug]: cache_key = 'mapdata:location:by_slug:%s:%s' % (AccessPermission.cache_key_for_request(request), slug) location = proxied_cache.get(cache_key, None) if location is not None: return location if slug.startswith('c:'): location = get_custom_location_for_request(slug, request) if location is None: return None elif ':' in slug: code, pk = slug.split(':', 1) model_name = LocationSlug.LOCATION_TYPE_BY_CODE.get(code) if model_name is None or not pk.isdigit(): return None model = apps.get_model('mapdata', model_name) location = locations_for_request(request).get(int(pk), None) if location is None or not isinstance(location, model): return None if location.slug is not None: location = LocationRedirect(slug=slug, target=location) else: location = locations_by_slug_for_request(request).get(slug, None) proxied_cache.set(cache_key, location, 1800) return location
def list(self, request, *args, **kwargs): searchable = 'searchable' in request.GET detailed = 'detailed' in request.GET geometry = 'geometry' in request.GET cache_key = 'mapdata:api:location:list:%d:%s:%d' % ( searchable + detailed * 2 + geometry * 4, AccessPermission.cache_key_for_request(request), request.user_permissions.can_access_base_mapdata) result = cache.get(cache_key, None) if result is None: if searchable: locations = searchable_locations_for_request(request) else: locations = visible_locations_for_request(request).values() result = tuple( obj.serialize(include_type=True, detailed=detailed, search=searchable, geometry=geometry and MapdataViewSet.can_access_geometry(request, obj), simple_geometry=True) for obj in locations) cache.set(cache_key, result, 300) return Response(result)
def route(self, request, *args, **kwargs): params = request.POST if request.method == 'POST' else request.GET form = RouteForm(params, request=request) if not form.is_valid(): return Response({ 'errors': form.errors, }, status=400) options = RouteOptions.get_for_request(request) try: options.update(params, ignore_unknown=True) except ValidationError as e: return Response({ 'errors': (str(e), ), }, status=400) try: route = Router.load().get_route( origin=form.cleaned_data['origin'], destination=form.cleaned_data['destination'], permissions=AccessPermission.get_for_request(request), options=options) except NotYetRoutable: return Response({ 'error': _('Not yet routable, try again shortly.'), }) except LocationUnreachable: return Response({ 'error': _('Unreachable location.'), }) except NoRouteFound: return Response({ 'error': _('No route found.'), }) origin_values = api_stats_clean_location_value( form.cleaned_data['origin'].pk) destination_values = api_stats_clean_location_value( form.cleaned_data['destination'].pk) increment_cache_key('apistats__route') for origin_value in origin_values: for destination_value in destination_values: increment_cache_key('apistats__route_tuple_%s_%s' % (origin_value, destination_value)) for value in origin_values: increment_cache_key('apistats__route_origin_%s' % value) for value in destination_values: increment_cache_key('apistats__route_destination_%s' % value) return Response({ 'request': { 'origin': form.cleaned_data['origin'].pk, 'destination': form.cleaned_data['destination'].pk, }, 'options': options.serialize(), 'result': route.serialize(locations=visible_locations_for_request(request)), })
def get_custom_location_for_request(slug: str, request): match = re.match(r'^c:(?P<level>[a-z0-9-_]+):(?P<x>-?\d+(\.\d+)?):(?P<y>-?\d+(\.\d+)?)$', slug) if match is None: return None level = levels_by_short_label_for_request(request).get(match.group('level')) if not isinstance(level, Level): return None return CustomLocation(level, float(match.group('x')), float(match.group('y')), AccessPermission.get_for_request(request))
def get_visible_spaces(request): cache_key = 'editor:visible_spaces:%s:%s' % ( request.changeset.raw_cache_key_by_changes, AccessPermission.cache_key_for_request(request, with_update=False)) visible_spaces = cache.get(cache_key, None) if visible_spaces is None: Space = request.changeset.wrap_model('Space') visible_spaces = tuple( Space.qs_for_request(request).values_list('pk', flat=True)) cache.set(cache_key, visible_spaces, 900) return visible_spaces
def get_visible_spaces(request): cache_key = 'editor:visible_spaces:%s:%s' % ( request.changeset.raw_cache_key_by_changes, AccessPermission.cache_key_for_request(request, with_update=False) ) visible_spaces = cache.get(cache_key, None) if visible_spaces is None: Space = request.changeset.wrap_model('Space') visible_spaces = tuple(Space.qs_for_request(request).values_list('pk', flat=True)) cache.set(cache_key, visible_spaces, 900) return visible_spaces
def set_tile_access_cookie(request, response): access_permissions = AccessPermission.get_for_request(request) if access_permissions: cookie = build_tile_access_cookie(access_permissions, settings.SECRET_TILE_KEY) response.set_cookie(settings.TILE_ACCESS_COOKIE_NAME, cookie, max_age=60, domain=settings.TILE_ACCESS_COOKIE_DOMAIN, httponly=settings.TILE_ACCESS_COOKIE_HTTPONLY, secure=settings.TILE_ACCESS_COOKIE_SECURE) else: response.delete_cookie(settings.TILE_ACCESS_COOKIE_NAME) response['Cache-Control'] = 'no-cache'
def locate(self, request, *args, **kwargs): try: location = Locator.load().locate(request.data, permissions=AccessPermission.get_for_request(request)) if location is not None: increment_cache_key('apistats__locate__%s' % location.pk) except ValidationError: return Response({ 'errors': (_('Invalid scan data.'),), }, status=400) return Response({'location': None if location is None else location.serialize(simple_geometry=True)})
def route(self, request, *args, **kwargs): params = request.POST if request.method == 'POST' else request.GET form = RouteForm(params, request=request) if not form.is_valid(): return Response({ 'errors': form.errors, }, status=400) options = RouteOptions.get_for_request(request) try: options.update(params, ignore_unknown=True) except ValidationError as e: return Response({ 'errors': (str(e), ), }, status=400) try: route = Router.load().get_route(origin=form.cleaned_data['origin'], destination=form.cleaned_data['destination'], permissions=AccessPermission.get_for_request(request), options=options) except NotYetRoutable: return Response({ 'error': _('Not yet routable, try again shortly.'), }) except LocationUnreachable: return Response({ 'error': _('Unreachable location.'), }) except NoRouteFound: return Response({ 'error': _('No route found.'), }) origin_values = api_stats_clean_location_value(form.cleaned_data['origin'].pk) destination_values = api_stats_clean_location_value(form.cleaned_data['destination'].pk) increment_cache_key('apistats__route') for origin_value in origin_values: for destination_value in destination_values: increment_cache_key('apistats__route_tuple_%s_%s' % (origin_value, destination_value)) for value in origin_values: increment_cache_key('apistats__route_origin_%s' % value) for value in destination_values: increment_cache_key('apistats__route_destination_%s' % value) return Response({ 'request': { 'origin': form.cleaned_data['origin'].pk, 'destination': form.cleaned_data['destination'].pk, }, 'options': options.serialize(), 'result': route.serialize(locations=visible_locations_for_request(request)), })
def locations_by_slug_for_request(request) -> Mapping[str, LocationSlug]: cache_key = 'mapdata:locations:by_slug:%s' % AccessPermission.cache_key_for_request(request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = {location.slug: location for location in locations_for_request(request).values() if location.slug} proxied_cache.set(cache_key, locations, 1800) return locations
def etag_func(request, *args, **kwargs): try: changeset = request.changeset except AttributeError: changeset = ChangeSet.get_for_request(request) request.changeset = changeset return ( get_language() + ':' + changeset.raw_cache_key_by_changes + ':' + AccessPermission.cache_key_for_request(request, with_update=False) + ':' + str(request.user.pk or 0))
def etag_func(request, *args, **kwargs): try: changeset = request.changeset except AttributeError: changeset = ChangeSet.get_for_request(request) request.changeset = changeset return (get_language() + ':' + changeset.raw_cache_key_by_changes + ':' + AccessPermission.cache_key_for_request(request, with_update=False) + ':' + str(request.user.pk or 0) + ':' + str(int(request.user_permissions.can_access_base_mapdata)) + ':' + ','.join(str(i) for i in request.user_space_accesses) + ':' + str(int(request.user.is_superuser)))
def visible_locations_for_request(request) -> Mapping[int, Location]: cache_key = 'mapdata:locations:real:%s' % AccessPermission.cache_key_for_request(request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = {pk: location for pk, location in locations_for_request(request).items() if not isinstance(location, LocationRedirect) and (location.can_search or location.can_describe)} proxied_cache.set(cache_key, locations, 1800) return locations
def etag_func(request, *args, **kwargs): try: changeset = request.changeset except AttributeError: changeset = ChangeSet.get_for_request(request) request.changeset = changeset return (get_language() + ':' + changeset.raw_cache_key_by_changes + ':' + AccessPermission.cache_key_for_request( request, with_update=False) + ':' + str(request.user.pk or 0) + ':' + str(int(request.user_permissions.can_access_base_mapdata)) + ':' + ','.join(str(i) for i in request.user_space_accesses) + ':' + str(int(request.user.is_superuser)))
def levels_by_short_label_for_request(request) -> Mapping[str, Level]: cache_key = 'mapdata:levels:by_short_label:%s' % AccessPermission.cache_key_for_request(request) levels = proxied_cache.get(cache_key, None) if levels is not None: return levels levels = OrderedDict( (level.short_label, level) for level in Level.qs_for_request(request).filter(on_top_of_id__isnull=True).order_by('base_altitude') ) proxied_cache.set(cache_key, levels, 1800) return levels
def searchable_locations_for_request(request) -> List[Location]: cache_key = 'mapdata:locations:searchable:%s' % AccessPermission.cache_key_for_request(request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = (location for location in locations_for_request(request).values() if isinstance(location, Location)) locations = tuple(location for location in locations if location.can_search) locations = sorted(locations, key=operator.attrgetter('order'), reverse=True) proxied_cache.set(cache_key, locations, 1800) return locations
def locate(self, request, *args, **kwargs): try: location = Locator.load().locate( request.data, permissions=AccessPermission.get_for_request(request)) except ValidationError: return Response({ 'errors': (_('Invalid scan data.'), ), }, status=400) return Response({ 'location': None if location is None else location.serialize( simple_geometry=True) })
def _get_list(self, request): qs = optimize_query(self.get_queryset()) filters = [] if issubclass(qs.model, LevelGeometryMixin) and 'level' in request.GET: filters.append(self.qs_filter(field='level', model=Level, key='pk', value=request.GET['level'])) if issubclass(qs.model, SpaceGeometryMixin) and 'space' in request.GET: filters.append(self.qs_filter(field='space', model=Space, key='pk', value=request.GET['space'])) if issubclass(qs.model, LocationGroup) and 'category' in request.GET: filters.append(self.qs_filter(field='category', model=LocationGroupCategory, key='pk' if request.GET['category'].isdigit() else 'name', value=request.GET['category'])) if issubclass(qs.model, SpecificLocation) and 'group' in request.GET: filters.append(self.qs_filter(field='groups', model=LocationGroup, key='pk', value=request.GET['group'])) if qs.model == Level and 'on_top_of' in request.GET: value = None if request.GET['on_top_of'] == 'null' else request.GET['on_top_of'] filters.append(self.qs_filter(field='on_top_of', model=Level, key='pk', value=value)) cache_key = 'mapdata:api:%s:%s' % (qs.model.__name__, AccessPermission.cache_key_for_request(request)) for qs_filter in filters: cache_key += ';%s,%s' % (qs_filter.field, qs_filter.value) results = cache.get(cache_key, None) if results is not None: return results for qs_filter in filters: if qs_filter.key == 'pk' and not qs_filter.value.isdigit(): raise ValidationError(detail={ 'detail': _('%(field)s is not an integer.') % {'field': qs_filter.field} }) for qs_filter in filters: if qs_filter.value is not None: keys = self._get_keys_for_model(request, qs_filter.model, qs_filter.key) value = int(qs_filter.value) if qs_filter.key == 'pk' else qs_filter.value if value not in keys: raise NotFound(detail=_('%(model)s not found.') % {'model': qs_filter.model._meta.verbose_name}) results = tuple(qs.order_by(*self.order_by)) cache.set(cache_key, results, 300) return results
def _get_keys_for_model(self, request, model, key): if hasattr(model, 'qs_for_request'): cache_key = 'mapdata:api:keys:%s:%s:%s' % (model.__name__, key, AccessPermission.cache_key_for_request(request)) qs = model.qs_for_request(request) else: cache_key = 'mapdata:api:keys:%s:%s:%s' % (model.__name__, key, MapUpdate.current_cache_key()) qs = model.objects.all() result = cache.get(cache_key, None) if result is not None: return result result = set(qs.values_list(key, flat=True)) cache.set(cache_key, result, 300) return result
def _get_keys_for_model(self, request, model, key): if hasattr(model, 'qs_for_request'): cache_key = 'mapdata:api:keys:%s:%s:%s' % ( model.__name__, key, AccessPermission.cache_key_for_request(request)) qs = model.qs_for_request(request) else: cache_key = 'mapdata:api:keys:%s:%s:%s' % ( model.__name__, key, MapUpdate.current_cache_key()) qs = model.objects.all() result = cache.get(cache_key, None) if result is not None: return result result = set(qs.values_list(key, flat=True)) cache.set(cache_key, result, 300) return result
def get_user_data(request): permissions = AccessPermission.get_for_request(request) result = { 'logged_in': bool(request.user.is_authenticated), } if permissions: result.update({ 'title': _('not logged in'), 'subtitle': ungettext_lazy('%d area unlocked', '%d areas unlocked', len(permissions)) % len(permissions), 'permissions': tuple(permissions), }) else: result.update({ 'title': _('Login'), 'subtitle': None, 'permissions': (), }) if request.user.is_authenticated: result['title'] = request.user.username return result
def get_user_data(request): permissions = AccessPermission.get_for_request(request) result = { 'logged_in': bool(request.user.is_authenticated), 'allow_editor': can_access_editor(request), 'allow_control_panel': request.user_permissions.control_panel, } if permissions: result.update({ 'title': _('not logged in'), 'subtitle': ungettext_lazy('%d area unlocked', '%d areas unlocked', len(permissions)) % len(permissions), 'permissions': tuple(permissions), }) else: result.update({ 'title': _('Login'), 'subtitle': None, 'permissions': (), }) if request.user.is_authenticated: result['title'] = request.user.username return result
def get_location_by_slug_for_request( slug: str, request) -> Optional[Union[LocationSlug, Position]]: cache_key = 'mapdata:location:by_slug:%s:%s' % ( AccessPermission.cache_key_for_request(request), slug) location = proxied_cache.get(cache_key, None) if location is not None: return location if slug.startswith('c:'): location = get_custom_location_for_request(slug, request) if location is None: return None elif slug.startswith('p:'): try: # return immediately, don't cache for obvious reasons return Position.objects.get(secret=slug[2:]) except Position.DoesNotExist: return None elif ':' in slug: code, pk = slug.split(':', 1) model_name = LocationSlug.LOCATION_TYPE_BY_CODE.get(code) if model_name is None or not pk.isdigit(): return None model = apps.get_model('mapdata', model_name) location = locations_for_request(request).get(int(pk), None) if location is None or not isinstance(location, model): return None if location.slug is not None: location = LocationRedirect(slug=slug, target=location) else: location = locations_by_slug_for_request(request).get(slug, None) proxied_cache.set(cache_key, location, 1800) return location
def list(self, request, *args, **kwargs): searchable = 'searchable' in request.GET detailed = 'detailed' in request.GET geometry = 'geometry' in request.GET cache_key = 'mapdata:api:location:list:%d:%s:%d' % ( searchable + detailed*2 + geometry*4, AccessPermission.cache_key_for_request(request), request.user_permissions.can_access_base_mapdata ) result = cache.get(cache_key, None) if result is None: if searchable: locations = searchable_locations_for_request(request) else: locations = visible_locations_for_request(request).values() result = tuple(obj.serialize(include_type=True, detailed=detailed, geometry=geometry and MapdataViewSet.can_access_geometry(request, obj), simple_geometry=True) for obj in locations) cache.set(cache_key, result, 300) return Response(result)
def __init__(self, *args, space_id=None, request=None, geometry_editable=False, is_json=False, **kwargs): self.request = request super().__init__(*args, **kwargs) creating = not self.instance.pk if hasattr(self.instance, 'author_id'): if self.instance.author_id is None: self.instance.author = request.user if 'geometry' in self.fields: if not geometry_editable: # can't see this geometry in editor self.fields.pop('geometry') else: # hide geometry widget self.fields['geometry'].widget = HiddenInput() if not creating: self.initial['geometry'] = json.dumps(mapping(self.instance.geometry), separators=(',', ':')) if self._meta.model.__name__ == 'Source' and self.request.user.is_superuser: Source = self.request.changeset.wrap_model('Source') sources = {s['name']: s for s in Source.objects.all().values('name', 'access_restriction_id', 'left', 'bottom', 'right', 'top')} used_names = set(sources.keys()) all_names = set(os.listdir(settings.SOURCES_ROOT)) if not creating: used_names.remove(self.instance.name) all_names.add(self.instance.name) self.fields['name'].widget = Select(choices=tuple((s, s) for s in sorted(all_names-used_names))) if creating: for s in sources.values(): s['access_restriction'] = s['access_restriction_id'] del s['access_restriction_id'] self.fields['copy_from'] = ChoiceField( choices=tuple((('', '---------'), ))+tuple( (json.dumps(sources[name], separators=(',', ':'), cls=DjangoJSONEncoder), name) for name in sorted(used_names) ), required=False ) self.fields['fixed_x'] = DecimalField(label='fixed x', required=False, max_digits=7, decimal_places=3, initial=0) self.fields['fixed_y'] = DecimalField(label='fixed y', required=False, max_digits=7, decimal_places=3, initial=0) self.fields['scale_x'] = DecimalField(label='scale x (m/px)', required=False, max_digits=7, decimal_places=3, initial=1) self.fields['scale_y'] = DecimalField(label='scale y (m/px)', required=False, max_digits=7, decimal_places=3, initial=1) self.fields['lock_aspect'] = BooleanField(label='lock aspect ratio', required=False, initial=True) self.fields['lock_scale'] = BooleanField(label='lock scale (for moving)', required=False, initial=True) self.fields.move_to_end('lock_scale', last=False) self.fields.move_to_end('lock_aspect', last=False) self.fields.move_to_end('scale_y', last=False) self.fields.move_to_end('scale_x', last=False) self.fields.move_to_end('fixed_y', last=False) self.fields.move_to_end('fixed_x', last=False) self.fields.move_to_end('access_restriction', last=False) if creating: self.fields.move_to_end('copy_from', last=False) self.fields.move_to_end('name', last=False) if self._meta.model.__name__ == 'AccessRestriction': AccessRestrictionGroup = self.request.changeset.wrap_model('AccessRestrictionGroup') self.fields['groups'].label_from_instance = lambda obj: obj.title self.fields['groups'].queryset = AccessRestrictionGroup.qs_for_request(self.request) elif 'groups' in self.fields: LocationGroupCategory = self.request.changeset.wrap_model('LocationGroupCategory') kwargs = {'allow_'+self._meta.model._meta.default_related_name: True} categories = LocationGroupCategory.objects.filter(**kwargs).prefetch_related('groups') if self.instance.pk: instance_groups = tuple(self.instance.groups.values_list('pk', flat=True)) else: instance_groups = () self.fields.pop('groups') for category in categories: choices = tuple((str(group.pk), group.title) for group in sorted(category.groups.all(), key=self.sort_group)) category_groups = set(group.pk for group in category.groups.all()) initial = tuple(str(pk) for pk in instance_groups if pk in category_groups) if category.single: name = 'group_'+category.name initial = initial[0] if initial else '' choices = (('', '---'), )+choices field = ChoiceField(label=category.title, required=False, initial=initial, choices=choices, help_text=category.help_text) else: name = 'groups_'+category.name field = MultipleChoiceField(label=category.title_plural, required=False, initial=initial, choices=choices, help_text=category.help_text) self.fields[name] = field if 'category' in self.fields: self.fields['category'].label_from_instance = lambda obj: obj.title if 'access_restriction' in self.fields: AccessRestriction = self.request.changeset.wrap_model('AccessRestriction') self.fields['access_restriction'].label_from_instance = lambda obj: obj.title self.fields['access_restriction'].queryset = AccessRestriction.qs_for_request(self.request) if 'base_mapdata_accessible' in self.fields: if not request.user.is_superuser: self.fields['base_mapdata_accessible'].disabled = True if space_id and 'target_space' in self.fields: Space = self.request.changeset.wrap_model('Space') GraphNode = self.request.changeset.wrap_model('GraphNode') GraphEdge = self.request.changeset.wrap_model('GraphEdge') cache_key = 'editor:neighbor_spaces:%s:%s%d' % ( self.request.changeset.raw_cache_key_by_changes, AccessPermission.cache_key_for_request(request, with_update=False), space_id ) other_spaces = cache.get(cache_key, None) if other_spaces is None: AccessPermission.cache_key_for_request(request, with_update=False) + ':' + str(request.user.pk or 0) space_nodes = set(GraphNode.objects.filter(space_id=space_id).values_list('pk', flat=True)) space_edges = GraphEdge.objects.filter( Q(from_node_id__in=space_nodes) | Q(to_node_id__in=space_nodes) ).values_list('from_node_id', 'to_node_id') other_nodes = set(chain(*space_edges)) - space_nodes other_spaces = set(GraphNode.objects.filter(pk__in=other_nodes).values_list('space_id', flat=True)) other_spaces.discard(space_id) cache.set(cache_key, other_spaces, 900) for space_field in ('origin_space', 'target_space'): other_space_id = getattr(self.instance, space_field+'_id', None) if other_space_id: other_spaces.add(other_space_id) space_qs = Space.qs_for_request(self.request).filter(pk__in=other_spaces) for space_field in ('origin_space', 'target_space'): if space_field in self.fields: self.fields[space_field].label_from_instance = lambda obj: obj.title self.fields[space_field].queryset = space_qs self.redirect_slugs = None self.add_redirect_slugs = None self.remove_redirect_slugs = None if 'slug' in self.fields: self.redirect_slugs = sorted(self.instance.redirects.values_list('slug', flat=True)) self.fields['redirect_slugs'] = CharField(label=_('Redirecting Slugs (comma seperated)'), required=False, initial=','.join(self.redirect_slugs)) self.fields.move_to_end('redirect_slugs', last=False) self.fields.move_to_end('slug', last=False) if 'from_node' in self.fields: self.fields['from_node'].widget = HiddenInput() if 'to_node' in self.fields: self.fields['to_node'].widget = HiddenInput() if 'data' in self.fields and 'data' in self.initial: self.initial['data'] = json.dumps(self.initial['data']) self.is_json = is_json self.missing_fields = tuple((name, field) for name, field in self.fields.items() if name not in self.data and not field.required)
def locate(self, request, *args, **kwargs): if isinstance(request.data, list): stations_data = request.data data = {} else: data = request.data if 'stations' not in data: return Response({ 'errors': (_('stations is missing.'), ), }, status=400) stations_data = data['stations'] try: location = Locator.load().locate( stations_data, permissions=AccessPermission.get_for_request(request)) if location is not None: increment_cache_key('apistats__locate__%s' % location.pk) except ValidationError: return Response({ 'errors': (_('Invalid scan data.'), ), }, status=400) if 'set_position' in data and location: set_position = data['set_position'] if not set_position.startswith('p:'): return Response({ 'errors': (_('Invalid set_position.'), ), }, status=400) try: position = Position.objects.get(secret=set_position[2:]) except Position.DoesNotExist: return Response({ 'errors': (_('Invalid set_position.'), ), }, status=400) form_data = { **data, 'coordinates_id': None if location is None else location.pk, } form = PositionAPIUpdateForm(instance=position, data=form_data, request=request) if not form.is_valid(): return Response({ 'errors': form.errors, }, status=400) form.save() return Response({ 'location': None if location is None else location.serialize( simple_geometry=True) })
def _get_list(self, request): qs = optimize_query(self.get_queryset()) filters = [] if issubclass(qs.model, LevelGeometryMixin) and 'level' in request.GET: filters.append( self.qs_filter(field='level', model=Level, key='pk', value=request.GET['level'])) if issubclass(qs.model, SpaceGeometryMixin) and 'space' in request.GET: filters.append( self.qs_filter(field='space', model=Space, key='pk', value=request.GET['space'])) if issubclass(qs.model, LocationGroup) and 'category' in request.GET: filters.append( self.qs_filter( field='category', model=LocationGroupCategory, key='pk' if request.GET['category'].isdigit() else 'name', value=request.GET['category'])) if issubclass(qs.model, SpecificLocation) and 'group' in request.GET: filters.append( self.qs_filter(field='groups', model=LocationGroup, key='pk', value=request.GET['group'])) if qs.model == Level and 'on_top_of' in request.GET: value = None if request.GET[ 'on_top_of'] == 'null' else request.GET['on_top_of'] filters.append( self.qs_filter(field='on_top_of', model=Level, key='pk', value=value)) cache_key = 'mapdata:api:%s:%s' % ( qs.model.__name__, AccessPermission.cache_key_for_request(request)) for qs_filter in filters: cache_key += ';%s,%s' % (qs_filter.field, qs_filter.value) results = cache.get(cache_key, None) if results is not None: return results for qs_filter in filters: if qs_filter.key == 'pk' and not qs_filter.value.isdigit(): raise ValidationError( detail={ 'detail': _('%(field)s is not an integer.') % { 'field': qs_filter.field } }) for qs_filter in filters: if qs_filter.value is not None: keys = self._get_keys_for_model(request, qs_filter.model, qs_filter.key) value = int(qs_filter.value ) if qs_filter.key == 'pk' else qs_filter.value if value not in keys: raise NotFound( detail=_('%(model)s not found.') % {'model': qs_filter.model._meta.verbose_name}) results = tuple(qs.order_by(*self.order_by)) cache.set(cache_key, results, 300) return results
def locations_for_request(request) -> Mapping[int, LocationSlug]: cache_key = 'mapdata:locations:%s' % AccessPermission.cache_key_for_request(request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = LocationSlug.objects.all().order_by('id') conditions = [] for model in get_submodels(Location): related_name = model._meta.default_related_name condition = Q(**{related_name + '__isnull': False}) # noinspection PyUnresolvedReferences condition &= model.q_for_request(request, prefix=related_name + '__') conditions.append(condition) locations = locations.filter(reduce(operator.or_, conditions)) locations.select_related('redirect', 'locationgroups__category') # prefetch locationgroups base_qs = LocationGroup.qs_for_request(request).select_related('category') for model in get_submodels(SpecificLocation): locations = locations.prefetch_related(Prefetch(model._meta.default_related_name + '__groups', queryset=base_qs)) locations = {obj.pk: obj.get_child() for obj in locations} # add locations to groups locationgroups = {pk: obj for pk, obj in locations.items() if isinstance(obj, LocationGroup)} for group in locationgroups.values(): group.locations = [] for obj in locations.values(): if not isinstance(obj, SpecificLocation): continue for group in obj.groups.all(): group = locationgroups.get(group.pk, None) if group is not None: group.locations.append(obj) # add levels to spaces remove_pks = set() levels = {pk: obj for pk, obj in locations.items() if isinstance(obj, Level)} for pk, obj in locations.items(): if isinstance(obj, LevelGeometryMixin): level = levels.get(obj.level_id, None) if level is None: remove_pks.add(pk) continue obj._level_cache = level # hide spaces on hidden levels for pk in remove_pks: locations.pop(pk) # add spaces to areas and POIs remove_pks = set() spaces = {pk: obj for pk, obj in locations.items() if isinstance(obj, Space)} for pk, obj in locations.items(): if isinstance(obj, SpaceGeometryMixin): space = spaces.get(obj.space_id, None) if space is None: remove_pks.add(pk) continue obj._space_cache = space # hide locations on hidden spaces for pk in remove_pks: locations.pop(pk) # add targets to LocationRedirects levels = {pk: obj for pk, obj in locations.items() if isinstance(obj, Level)} for obj in locations.values(): if isinstance(obj, LocationRedirect): obj._target_cache = locations.get(obj.target_id, None) # apply better space geometries for pk, geometry in get_better_space_geometries().items(): if pk in locations: locations[pk].geometry = geometry # precache cached properties for obj in locations.values(): # noinspection PyStatementEffect obj.subtitle, obj.order if isinstance(obj, GeometryMixin): # noinspection PyStatementEffect obj.point proxied_cache.set(cache_key, locations, 1800) return locations
def __init__(self, request=None, author=None, expire_date=None, *args, **kwargs): super().__init__(*args, **kwargs) # remember author if this form is saved self.author = author or request.user author_permissions = request.user_permissions if request else author.permissions self.expire_date = expire_date # determine which access permissions the author can grant self.author_access_permissions = AccessPermission.get_for_request_with_expire_date( request, can_grant=True) access_restrictions = AccessRestriction.objects.filter( pk__in=self.author_access_permissions.keys()) self.access_restrictions = { access_restriction.pk: access_restriction for access_restriction in access_restrictions } access_restrictions_ids = set(self.access_restrictions.keys()) self.access_restriction_choices = { 'all': self.access_restrictions.values(), **{ str(pk): (access_restriction, ) for pk, access_restriction in self.access_restrictions.items() } } # get access permission groups groups = AccessRestrictionGroup.qs_for_request( request).prefetch_related( Prefetch('accessrestrictions', AccessRestriction.objects.only('pk'))) group_contents = { group.pk: set(r.pk for r in group.accessrestrictions.all()) for group in groups } group_contents = { pk: restrictions for pk, restrictions in group_contents.items() if not (restrictions - access_restrictions_ids) } self.access_restriction_choices.update({ ('g%d' % pk): tuple(self.access_restrictions[restriction] for restriction in restrictions) for pk, restrictions in group_contents.items() }) # construct choice field for access permissions choices = [('', _('choose permissions…')), ('all', ungettext_lazy('everything possible (%d permission)', 'everything possible (%d permissions)', len(access_restrictions)) % len(access_restrictions))] choices.append( (_('Access Permission Groups'), tuple(('g%d' % group.pk, group.title) for group in groups))) choices.append((_('Access Permissions'), tuple((str(pk), access_restriction.title) for pk, access_restriction in self.access_restrictions.items()))) self.fields['access_restrictions'] = ChoiceField(choices=choices, required=True) # construct choices for the expire field expire_choices = [ ('', _('never')), ] for minutes in range(15, 60, 15): expire_choices.append( (str(minutes), ungettext_lazy('in %d minute', 'in %d minutes', minutes) % minutes)) for hours in chain(range(1, 6), range(6, 24, 6)): expire_choices.append( (str(hours * 60), ungettext_lazy('in %d hour', 'in %d hours', hours) % hours)) expire_choices.insert(5, (str(90), _('in 1½ hour'))) for days in range(1, 14): expire_choices.append( (str(days * 24 * 60), ungettext_lazy('in %d day', 'in %d days', days) % days)) self.fields['expires'] = ChoiceField(required=False, initial='60', choices=expire_choices) # if applicable, add field to grant pass on permissions if author_permissions.grant_all_access: choices = [('0', '---')] * 6 + [('1', _('can pass on')) ] + [('0', '---')] * 3 self.fields['can_grant'] = ChoiceField(required=False, initial='60', choices=choices)
def graph_edit(request, level=None, space=None): Level = request.changeset.wrap_model('Level') Space = request.changeset.wrap_model('Space') GraphNode = request.changeset.wrap_model('GraphNode') GraphEdge = request.changeset.wrap_model('GraphEdge') can_edit = request.changeset.can_edit(request) ctx = { 'path': request.path, 'can_edit': can_edit, 'levels': Level.objects.filter(Level.q_for_request(request), on_top_of__isnull=True), 'level_url': 'editor.levels.graph', } create_nodes = False if level is not None: level = get_object_or_404(Level.objects.filter( Level.q_for_request(request)), pk=level) ctx.update({ 'back_url': reverse('editor.levels.detail', kwargs={'pk': level.pk}), 'back_title': _('back to level'), 'level': level, 'geometry_url': '/api/editor/geometries/?level=' + str(level.primary_level_pk), }) elif space is not None: queryset = Space.objects.filter(Space.q_for_request( request)).select_related('level').defer('geometry') space = get_object_or_404(queryset, pk=space) level = space.level ctx.update({ 'space': space, 'level': space.level, 'back_url': reverse('editor.spaces.detail', kwargs={ 'level': level.pk, 'pk': space.pk }), 'back_title': _('back to space'), 'parent_url': reverse('editor.levels.graph', kwargs={'level': level.pk}), 'parent_title': _('to level graph'), 'geometry_url': '/api/editor/geometries/?space=' + str(space.pk), }) create_nodes = True if request.method == 'POST': changeset_exceeded = get_changeset_exceeded(request) graphnode_changes = {} if changeset_exceeded: graphnode_changes = request.changeset.get_changed_objects_by_model( 'GraphNode') if request.POST.get('delete') == '1': # Delete this graphnode! node = get_object_or_404(GraphNode, pk=request.POST.get('pk')) if changeset_exceeded and node.pk not in graphnode_changes: messages.error( request, _('You can not delete this graph node because your changeset is full.' )) return redirect(request.path) if request.POST.get('delete_confirm') == '1': with request.changeset.lock_to_edit(request) as changeset: if changeset.can_edit(request): node.edges_from_here.all().delete() node.edges_to_here.all().delete() node.delete() else: messages.error( request, _('You can not edit changes on this changeset.')) return redirect(request.path) messages.success(request, _('Graph Node was successfully deleted.')) return redirect(request.path) return render( request, 'editor/delete.html', { 'model_title': GraphNode._meta.verbose_name, 'pk': node.pk, 'obj_title': node.title }) permissions = AccessPermission.get_for_request(request) | set([None]) edge_settings_form = GraphEdgeSettingsForm(instance=GraphEdge(), request=request, data=request.POST) graph_action_form = GraphEditorActionForm( request=request, allow_clicked_position=create_nodes, data=request.POST) if edge_settings_form.is_valid() and graph_action_form.is_valid(): goto_space = graph_action_form.cleaned_data['goto_space'] if goto_space is not None: return redirect( reverse('editor.spaces.graph', kwargs={'space': goto_space.pk})) set_active_node = False active_node = graph_action_form.cleaned_data['active_node'] clicked_node = graph_action_form.cleaned_data['clicked_node'] clicked_position = graph_action_form.cleaned_data.get( 'clicked_position') if clicked_node is not None and clicked_position is None: if active_node is None: active_node = clicked_node set_active_node = True elif active_node == clicked_node: active_node = None set_active_node = True else: with request.changeset.lock_to_edit(request) as changeset: if changeset.can_edit(request): connect_nodes(request, active_node, clicked_node, edge_settings_form) active_node = clicked_node if edge_settings_form.cleaned_data[ 'activate_next'] else None set_active_node = True else: messages.error( request, _('You can not edit changes on this changeset.' )) elif (clicked_node is None and clicked_position is not None and active_node is None and space.geometry.contains(clicked_position)): if changeset_exceeded: messages.error( request, _('You can not add graph nodes because your changeset is full.' )) return redirect(request.path) with request.changeset.lock_to_edit(request) as changeset: if changeset.can_edit(request): node = GraphNode(space=space, geometry=clicked_position) node.save() messages.success(request, _('New graph node created.')) active_node = None set_active_node = True else: messages.error( request, _('You can not edit changes on this changeset.')) if set_active_node: connections = {} if active_node: for self_node, other_node in (('from_node', 'to_node'), ('to_node', 'from_node')): conn_qs = GraphEdge.objects.filter( Q(**{self_node + '__pk': active_node.pk})) conn_qs = conn_qs.select_related( other_node + '__space', other_node + '__space__level', 'waytype', 'access_restriction') for edge in conn_qs: edge.other_node = getattr(edge, other_node) if (edge.other_node.space.access_restriction_id not in permissions or edge.other_node.space.level. access_restriction_id not in permissions): continue connections.setdefault(edge.other_node.space_id, []).append(edge) connections = sorted( connections.values(), key=lambda c: (c[0].other_node.space.level == level, c[ 0].other_node.space == space, c[0].other_node.space .level.base_altitude)) ctx.update({ 'set_active_node': set_active_node, 'active_node': active_node, 'active_node_connections': connections, }) else: edge_settings_form = GraphEdgeSettingsForm(request=request) graph_action_form = GraphEditorActionForm( request=request, allow_clicked_position=create_nodes) ctx.update({ 'edge_settings_form': edge_settings_form, 'graph_action_form': graph_action_form, 'create_nodes': create_nodes, }) return render(request, 'editor/graph.html', ctx)
def __init__(self, *args, space_id=None, request=None, geometry_editable=False, is_json=False, **kwargs): self.request = request super().__init__(*args, **kwargs) creating = not self.instance.pk if hasattr(self.instance, 'author_id'): if self.instance.author_id is None: self.instance.author = request.user if 'geometry' in self.fields: if not geometry_editable: # can't see this geometry in editor self.fields.pop('geometry') else: # hide geometry widget self.fields['geometry'].widget = HiddenInput() if not creating: self.initial['geometry'] = json.dumps( mapping(self.instance.geometry), separators=(',', ':')) if self._meta.model.__name__ == 'Source' and self.request.user.is_superuser: Source = self.request.changeset.wrap_model('Source') sources = { s['name']: s for s in Source.objects.all().values('name', 'access_restriction_id', 'left', 'bottom', 'right', 'top') } used_names = set(sources.keys()) all_names = set(os.listdir(settings.SOURCES_ROOT)) if not creating: used_names.remove(self.instance.name) all_names.add(self.instance.name) self.fields['name'].widget = Select(choices=tuple( (s, s) for s in sorted(all_names - used_names))) if creating: for s in sources.values(): s['access_restriction'] = s['access_restriction_id'] del s['access_restriction_id'] self.fields['copy_from'] = ChoiceField(choices=tuple( (('', '---------'), )) + tuple( (json.dumps(sources[name], separators=(',', ':'), cls=DjangoJSONEncoder), name) for name in sorted(used_names)), required=False) self.fields['fixed_x'] = DecimalField(label='fixed x', required=False, max_digits=7, decimal_places=3, initial=0) self.fields['fixed_y'] = DecimalField(label='fixed y', required=False, max_digits=7, decimal_places=3, initial=0) self.fields['scale_x'] = DecimalField(label='scale x (m/px)', required=False, max_digits=7, decimal_places=3, initial=1) self.fields['scale_y'] = DecimalField(label='scale y (m/px)', required=False, max_digits=7, decimal_places=3, initial=1) self.fields['lock_aspect'] = BooleanField( label='lock aspect ratio', required=False, initial=True) self.fields['lock_scale'] = BooleanField( label='lock scale (for moving)', required=False, initial=True) self.fields.move_to_end('lock_scale', last=False) self.fields.move_to_end('lock_aspect', last=False) self.fields.move_to_end('scale_y', last=False) self.fields.move_to_end('scale_x', last=False) self.fields.move_to_end('fixed_y', last=False) self.fields.move_to_end('fixed_x', last=False) self.fields.move_to_end('access_restriction', last=False) if creating: self.fields.move_to_end('copy_from', last=False) self.fields.move_to_end('name', last=False) if self._meta.model.__name__ == 'AccessRestriction': AccessRestrictionGroup = self.request.changeset.wrap_model( 'AccessRestrictionGroup') self.fields['groups'].label_from_instance = lambda obj: obj.title self.fields[ 'groups'].queryset = AccessRestrictionGroup.qs_for_request( self.request) elif 'groups' in self.fields: LocationGroupCategory = self.request.changeset.wrap_model( 'LocationGroupCategory') kwargs = { 'allow_' + self._meta.model._meta.default_related_name: True } categories = LocationGroupCategory.objects.filter( **kwargs).prefetch_related('groups') if self.instance.pk: instance_groups = tuple( self.instance.groups.values_list('pk', flat=True)) else: instance_groups = () self.fields.pop('groups') for category in categories: choices = tuple((str(group.pk), group.title) for group in sorted(category.groups.all(), key=self.sort_group)) category_groups = set(group.pk for group in category.groups.all()) initial = tuple( str(pk) for pk in instance_groups if pk in category_groups) if category.single: name = 'group_' + category.name initial = initial[0] if initial else '' choices = (('', '---'), ) + choices field = ChoiceField(label=category.title, required=False, initial=initial, choices=choices, help_text=category.help_text) else: name = 'groups_' + category.name field = MultipleChoiceField(label=category.title_plural, required=False, initial=initial, choices=choices, help_text=category.help_text) self.fields[name] = field if 'category' in self.fields: self.fields['category'].label_from_instance = lambda obj: obj.title if 'access_restriction' in self.fields: AccessRestriction = self.request.changeset.wrap_model( 'AccessRestriction') self.fields[ 'access_restriction'].label_from_instance = lambda obj: obj.title self.fields[ 'access_restriction'].queryset = AccessRestriction.qs_for_request( self.request) if 'base_mapdata_accessible' in self.fields: if not request.user.is_superuser: self.fields['base_mapdata_accessible'].disabled = True if space_id and 'target_space' in self.fields: Space = self.request.changeset.wrap_model('Space') GraphNode = self.request.changeset.wrap_model('GraphNode') GraphEdge = self.request.changeset.wrap_model('GraphEdge') cache_key = 'editor:neighbor_spaces:%s:%s%d' % ( self.request.changeset.raw_cache_key_by_changes, AccessPermission.cache_key_for_request( request, with_update=False), space_id) other_spaces = cache.get(cache_key, None) if other_spaces is None: AccessPermission.cache_key_for_request( request, with_update=False) + ':' + str(request.user.pk or 0) space_nodes = set( GraphNode.objects.filter(space_id=space_id).values_list( 'pk', flat=True)) space_edges = GraphEdge.objects.filter( Q(from_node_id__in=space_nodes) | Q(to_node_id__in=space_nodes)).values_list( 'from_node_id', 'to_node_id') other_nodes = set(chain(*space_edges)) - space_nodes other_spaces = set( GraphNode.objects.filter(pk__in=other_nodes).values_list( 'space_id', flat=True)) other_spaces.discard(space_id) cache.set(cache_key, other_spaces, 900) for space_field in ('origin_space', 'target_space'): other_space_id = getattr(self.instance, space_field + '_id', None) if other_space_id: other_spaces.add(other_space_id) space_qs = Space.qs_for_request( self.request).filter(pk__in=other_spaces) for space_field in ('origin_space', 'target_space'): if space_field in self.fields: self.fields[ space_field].label_from_instance = lambda obj: obj.title self.fields[space_field].queryset = space_qs self.redirect_slugs = None self.add_redirect_slugs = None self.remove_redirect_slugs = None if 'slug' in self.fields: self.redirect_slugs = sorted( self.instance.redirects.values_list('slug', flat=True)) self.fields['redirect_slugs'] = CharField( label=_('Redirecting Slugs (comma seperated)'), required=False, initial=','.join(self.redirect_slugs)) self.fields.move_to_end('redirect_slugs', last=False) self.fields.move_to_end('slug', last=False) if 'from_node' in self.fields: self.fields['from_node'].widget = HiddenInput() if 'to_node' in self.fields: self.fields['to_node'].widget = HiddenInput() if 'data' in self.fields and 'data' in self.initial: self.initial['data'] = json.dumps(self.initial['data']) self.is_json = is_json self.missing_fields = tuple( (name, field) for name, field in self.fields.items() if name not in self.data and not field.required)
def __init__(self, request=None, author=None, expire_date=None, *args, **kwargs): super().__init__(*args, **kwargs) # remember author if this form is saved self.author = author or request.user author_permissions = request.user_permissions if request else author.permissions self.expire_date = expire_date # determine which access permissions the author can grant self.author_access_permissions = AccessPermission.get_for_request_with_expire_date(request, can_grant=True) access_restrictions = AccessRestriction.objects.filter( pk__in=self.author_access_permissions.keys() ) self.access_restrictions = { access_restriction.pk: access_restriction for access_restriction in access_restrictions } access_restrictions_ids = set(self.access_restrictions.keys()) self.access_restriction_choices = { 'all': self.access_restrictions.values(), **{str(pk): (access_restriction, ) for pk, access_restriction in self.access_restrictions.items()} } # get access permission groups groups = AccessRestrictionGroup.qs_for_request(request).prefetch_related( Prefetch('accessrestrictions', AccessRestriction.objects.only('pk')) ) group_contents = { group.pk: set(r.pk for r in group.accessrestrictions.all()) for group in groups } group_contents = { pk: restrictions for pk, restrictions in group_contents.items() if not (restrictions - access_restrictions_ids) } self.access_restriction_choices.update({ ('g%d' % pk): tuple( self.access_restrictions[restriction] for restriction in restrictions ) for pk, restrictions in group_contents.items() }) # construct choice field for access permissions choices = [('', _('choose permissions…')), ('all', ungettext_lazy('everything possible (%d permission)', 'everything possible (%d permissions)', len(access_restrictions)) % len(access_restrictions))] choices.append((_('Access Permission Groups'), tuple( ('g%d' % group.pk, group.title) for group in groups ))) choices.append((_('Access Permissions'), tuple( (str(pk), access_restriction.title) for pk, access_restriction in self.access_restrictions.items() ))) self.fields['access_restrictions'] = ChoiceField(choices=choices, required=True) # construct choices for the expire field expire_choices = [ ('', _('never')), ] for minutes in range(15, 60, 15): expire_choices.append( (str(minutes), ungettext_lazy('in %d minute', 'in %d minutes', minutes) % minutes)) for hours in chain(range(1, 6), range(6, 24, 6)): expire_choices.append( (str(hours*60), ungettext_lazy('in %d hour', 'in %d hours', hours) % hours) ) expire_choices.insert( 5, (str(90), _('in 1½ hour')) ) for days in range(1, 14): expire_choices.append( (str(days*24*60), ungettext_lazy('in %d day', 'in %d days', days) % days) ) self.fields['expires'] = ChoiceField(required=False, initial='60', choices=expire_choices) # if applicable, add field to grant pass on permissions if author_permissions.grant_all_access: choices = [('0', '---')]*6 + [('1', _('can pass on'))] + [('0', '---')]*3 self.fields['can_grant'] = ChoiceField(required=False, initial='60', choices=choices)
def graph_edit(request, level=None, space=None): if not request.user_permissions.can_access_base_mapdata: raise PermissionDenied Level = request.changeset.wrap_model('Level') Space = request.changeset.wrap_model('Space') GraphNode = request.changeset.wrap_model('GraphNode') GraphEdge = request.changeset.wrap_model('GraphEdge') can_edit = request.changeset.can_edit(request) ctx = { 'path': request.path, 'can_edit': can_edit, 'levels': Level.objects.filter(Level.q_for_request(request), on_top_of__isnull=True), 'level_url': 'editor.levels.graph', } create_nodes = False if level is not None: level = get_object_or_404(Level.objects.filter(Level.q_for_request(request)), pk=level) ctx.update({ 'back_url': reverse('editor.levels.detail', kwargs={'pk': level.pk}), 'back_title': _('back to level'), 'level': level, 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), }) elif space is not None: queryset = Space.objects.filter(Space.q_for_request(request)).select_related('level').defer('geometry') space = get_object_or_404(queryset, pk=space) level = space.level ctx.update({ 'space': space, 'level': space.level, 'back_url': reverse('editor.spaces.detail', kwargs={'level': level.pk, 'pk': space.pk}), 'back_title': _('back to space'), 'parent_url': reverse('editor.levels.graph', kwargs={'level': level.pk}), 'parent_title': _('to level graph'), 'geometry_url': '/api/editor/geometries/?space='+str(space.pk), }) create_nodes = True if request.method == 'POST': changeset_exceeded = get_changeset_exceeded(request) graphnode_changes = {} if changeset_exceeded: graphnode_changes = request.changeset.get_changed_objects_by_model('GraphNode') if request.POST.get('delete') == '1': # Delete this graphnode! node = get_object_or_404(GraphNode, pk=request.POST.get('pk')) if changeset_exceeded and node.pk not in graphnode_changes: messages.error(request, _('You can not delete this graph node because your changeset is full.')) return redirect(request.path) if request.POST.get('delete_confirm') == '1': with request.changeset.lock_to_edit(request) as changeset: if changeset.can_edit(request): node.edges_from_here.all().delete() node.edges_to_here.all().delete() node.delete() else: messages.error(request, _('You can not edit changes on this changeset.')) return redirect(request.path) messages.success(request, _('Graph Node was successfully deleted.')) return redirect(request.path) return render(request, 'editor/delete.html', { 'model_title': GraphNode._meta.verbose_name, 'pk': node.pk, 'obj_title': node.title }) permissions = AccessPermission.get_for_request(request) | set([None]) edge_settings_form = GraphEdgeSettingsForm(instance=GraphEdge(), request=request, data=request.POST) graph_action_form = GraphEditorActionForm(request=request, allow_clicked_position=create_nodes, data=request.POST) if edge_settings_form.is_valid() and graph_action_form.is_valid(): goto_space = graph_action_form.cleaned_data['goto_space'] if goto_space is not None: return redirect(reverse('editor.spaces.graph', kwargs={'space': goto_space.pk})) set_active_node = False active_node = graph_action_form.cleaned_data['active_node'] clicked_node = graph_action_form.cleaned_data['clicked_node'] clicked_position = graph_action_form.cleaned_data.get('clicked_position') if clicked_node is not None and clicked_position is None: if active_node is None: active_node = clicked_node set_active_node = True elif active_node == clicked_node: active_node = None set_active_node = True else: with request.changeset.lock_to_edit(request) as changeset: if changeset.can_edit(request): connect_nodes(request, active_node, clicked_node, edge_settings_form) active_node = clicked_node if edge_settings_form.cleaned_data['activate_next'] else None set_active_node = True else: messages.error(request, _('You can not edit changes on this changeset.')) elif (clicked_node is None and clicked_position is not None and active_node is None and space.geometry.contains(clicked_position)): if changeset_exceeded: messages.error(request, _('You can not add graph nodes because your changeset is full.')) return redirect(request.path) with request.changeset.lock_to_edit(request) as changeset: if changeset.can_edit(request): node = GraphNode(space=space, geometry=clicked_position) node.save() messages.success(request, _('New graph node created.')) active_node = None set_active_node = True else: messages.error(request, _('You can not edit changes on this changeset.')) if set_active_node: connections = {} if active_node: for self_node, other_node in (('from_node', 'to_node'), ('to_node', 'from_node')): conn_qs = GraphEdge.objects.filter(Q(**{self_node+'__pk': active_node.pk})) conn_qs = conn_qs.select_related(other_node+'__space', other_node+'__space__level', 'waytype', 'access_restriction') for edge in conn_qs: edge.other_node = getattr(edge, other_node) if (edge.other_node.space.access_restriction_id not in permissions or edge.other_node.space.level.access_restriction_id not in permissions): continue connections.setdefault(edge.other_node.space_id, []).append(edge) connections = sorted( connections.values(), key=lambda c: (c[0].other_node.space.level == level, c[0].other_node.space == space, c[0].other_node.space.level.base_altitude) ) ctx.update({ 'set_active_node': set_active_node, 'active_node': active_node, 'active_node_connections': connections, }) else: edge_settings_form = GraphEdgeSettingsForm(request=request) graph_action_form = GraphEditorActionForm(request=request, allow_clicked_position=create_nodes) ctx.update({ 'edge_settings_form': edge_settings_form, 'graph_action_form': graph_action_form, 'create_nodes': create_nodes, }) return render(request, 'editor/graph.html', ctx)
def locations_for_request(request) -> Mapping[int, LocationSlug]: # todo this takes a long time because it's a lot of data, we might want to change that cache_key = 'mapdata:locations:%s' % AccessPermission.cache_key_for_request( request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = LocationSlug.objects.all().order_by('id') conditions = [] for model in get_submodels(Location): related_name = model._meta.default_related_name condition = Q(**{related_name + '__isnull': False}) # noinspection PyUnresolvedReferences condition &= model.q_for_request(request, prefix=related_name + '__') conditions.append(condition) locations = locations.select_related( related_name + '__label_settings').prefetch_related(related_name + '__redirects') locations = locations.filter(reduce(operator.or_, conditions)) locations = locations.select_related('redirect', 'locationgroups__category') # prefetch locationgroups base_qs = LocationGroup.qs_for_request(request).select_related( 'category', 'label_settings') for model in get_submodels(SpecificLocation): locations = locations.prefetch_related( Prefetch(model._meta.default_related_name + '__groups', queryset=base_qs)) locations = {obj.pk: obj.get_child() for obj in locations} # add locations to groups locationgroups = { pk: obj for pk, obj in locations.items() if isinstance(obj, LocationGroup) } for group in locationgroups.values(): group.locations = [] for obj in locations.values(): if not isinstance(obj, SpecificLocation): continue for group in obj.groups.all(): group = locationgroups.get(group.pk, None) if group is not None: group.locations.append(obj) # add levels to spaces remove_pks = set() levels = { pk: obj for pk, obj in locations.items() if isinstance(obj, Level) } for pk, obj in locations.items(): if isinstance(obj, LevelGeometryMixin): level = levels.get(obj.level_id, None) if level is None: remove_pks.add(pk) continue obj._level_cache = level # hide spaces on hidden levels for pk in remove_pks: locations.pop(pk) # add spaces to areas and POIs remove_pks = set() spaces = { pk: obj for pk, obj in locations.items() if isinstance(obj, Space) } for pk, obj in locations.items(): if isinstance(obj, SpaceGeometryMixin): space = spaces.get(obj.space_id, None) if space is None: remove_pks.add(pk) continue obj._space_cache = space # hide locations on hidden spaces for pk in remove_pks: locations.pop(pk) # add targets to LocationRedirects levels = { pk: obj for pk, obj in locations.items() if isinstance(obj, Level) } for obj in locations.values(): if isinstance(obj, LocationRedirect): obj._target_cache = locations.get(obj.target_id, None) # apply better space geometries for pk, geometry in get_better_space_geometries().items(): if pk in locations: locations[pk].geometry = geometry # precache cached properties for obj in locations.values(): # noinspection PyStatementEffect obj.subtitle, obj.order if isinstance(obj, GeometryMixin): # noinspection PyStatementEffect obj.point proxied_cache.set(cache_key, locations, 1800) return locations
def __init__(self, *args, space_id=None, request=None, **kwargs): self.request = request super().__init__(*args, **kwargs) creating = not self.instance.pk if hasattr(self.instance, 'author_id'): if self.instance.author_id is None: self.instance.author = request.user if 'level' in self.fields: # hide level widget self.fields['level'].widget = HiddenInput() if 'space' in self.fields: # hide space widget self.fields['space'].widget = HiddenInput() if 'geometry' in self.fields: # hide geometry widget self.fields['geometry'].widget = HiddenInput() if not creating: self.initial['geometry'] = json.dumps(mapping( self.instance.geometry), separators=(',', ':')) if self._meta.model.__name__ == 'AccessRestriction': AccessRestrictionGroup = self.request.changeset.wrap_model( 'AccessRestrictionGroup') self.fields['groups'].label_from_instance = lambda obj: obj.title self.fields[ 'groups'].queryset = AccessRestrictionGroup.qs_for_request( self.request) elif 'groups' in self.fields: LocationGroupCategory = self.request.changeset.wrap_model( 'LocationGroupCategory') kwargs = { 'allow_' + self._meta.model._meta.default_related_name: True } categories = LocationGroupCategory.objects.filter( **kwargs).prefetch_related('groups').order_by('priority') if self.instance.pk: instance_groups = tuple( self.instance.groups.values_list('pk', flat=True)) else: instance_groups = () self.fields.pop('groups') for category in categories: choices = tuple((str(group.pk), group.title) for group in category.groups.all()) category_groups = set(group.pk for group in category.groups.all()) initial = tuple( str(pk) for pk in instance_groups if pk in category_groups) if category.single: name = 'group_' + category.name initial = initial[0] if initial else '' choices = (('', '---'), ) + choices field = ChoiceField(label=category.title, required=False, initial=initial, choices=choices) else: name = 'groups_' + category.name field = MultipleChoiceField(label=category.title_plural, required=False, initial=initial, choices=choices) self.fields[name] = field if 'category' in self.fields: self.fields['category'].label_from_instance = lambda obj: obj.title if 'access_restriction' in self.fields: AccessRestriction = self.request.changeset.wrap_model( 'AccessRestriction') self.fields[ 'access_restriction'].label_from_instance = lambda obj: obj.title self.fields[ 'access_restriction'].queryset = AccessRestriction.qs_for_request( self.request) if space_id and 'target_space' in self.fields: Space = self.request.changeset.wrap_model('Space') GraphNode = self.request.changeset.wrap_model('GraphNode') GraphEdge = self.request.changeset.wrap_model('GraphEdge') cache_key = 'editor:neighbor_spaces:%s:%s%d' % ( self.request.changeset.raw_cache_key_by_changes, AccessPermission.cache_key_for_request( request, with_update=False), space_id) other_spaces = cache.get(cache_key, None) if other_spaces is None: AccessPermission.cache_key_for_request( request, with_update=False) + ':' + str(request.user.pk or 0) space_nodes = set( GraphNode.objects.filter(space_id=space_id).values_list( 'pk', flat=True)) space_edges = GraphEdge.objects.filter( Q(from_node_id__in=space_nodes) | Q(to_node_id__in=space_nodes)).values_list( 'from_node_id', 'to_node_id') other_nodes = set(chain(*space_edges)) - space_nodes other_spaces = set( GraphNode.objects.filter(pk__in=other_nodes).values_list( 'space_id', flat=True)) other_spaces.discard(space_id) cache.set(cache_key, other_spaces, 900) for space_field in ('origin_space', 'target_space'): other_space_id = getattr(self.instance, space_field + '_id', None) if other_space_id: other_spaces.add(other_space_id) space_qs = Space.qs_for_request( self.request).filter(pk__in=other_spaces) for space_field in ('origin_space', 'target_space'): if space_field in self.fields: self.fields[ space_field].label_from_instance = lambda obj: obj.title self.fields[space_field].queryset = space_qs self.redirect_slugs = None self.add_redirect_slugs = None self.remove_redirect_slugs = None if 'slug' in self.fields: self.redirect_slugs = sorted( self.instance.redirects.values_list('slug', flat=True)) self.fields['redirect_slugs'] = CharField( label=_('Redirecting Slugs (comma seperated)'), required=False, initial=','.join(self.redirect_slugs)) self.fields.move_to_end('redirect_slugs', last=False) self.fields.move_to_end('slug', last=False) if 'from_node' in self.fields: self.fields['from_node'].widget = HiddenInput() if 'to_node' in self.fields: self.fields['to_node'].widget = HiddenInput() if 'data' in self.fields and 'data' in self.initial: self.initial['data'] = json.dumps(self.initial['data'])