Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
    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)
Example #5
0
    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)),
        })
Example #6
0
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))
Example #7
0
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
Example #8
0
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
Example #9
0
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'
Example #10
0
    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)})
Example #11
0
    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)),
        })
Example #12
0
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
Example #13
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))
Example #14
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)))
Example #15
0
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
Example #16
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)))
Example #17
0
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
Example #18
0
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'
Example #19
0
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
Example #20
0
    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)
        })
Example #21
0
    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
Example #22
0
    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
Example #23
0
    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
Example #24
0
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
Example #25
0
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
Example #26
0
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
Example #27
0
    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)
Example #28
0
    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)
Example #29
0
File: api.py Project: c3nav/c3nav
    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)
        })
Example #30
0
    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
Example #31
0
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
Example #32
0
    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)
Example #33
0
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)
Example #34
0
    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)
Example #35
0
    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)
Example #36
0
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)
Example #37
0
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
Example #38
0
    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'])