Beispiel #1
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
Beispiel #2
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)
Beispiel #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
Beispiel #4
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
Beispiel #5
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
Beispiel #6
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))
Beispiel #7
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
Beispiel #8
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)))
Beispiel #9
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
Beispiel #10
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)))
Beispiel #11
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
Beispiel #12
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
Beispiel #13
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
Beispiel #14
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
Beispiel #15
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
Beispiel #16
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
Beispiel #17
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)
Beispiel #18
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)
Beispiel #19
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
Beispiel #20
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
Beispiel #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
Beispiel #22
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'])
Beispiel #23
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)