def get_location_by_slug_for_request(slug: str, request) -> Optional[LocationSlug]: cache_key = 'mapdata:location:by_slug:%s:%s' % ( AccessPermission.cache_key_for_request(request), slug) location = cache.get(cache_key, None) if location is not None: return location if slug.startswith('c:'): location = get_custom_location_for_request(slug, request) if location is None: return None elif ':' in slug: code, pk = slug.split(':', 1) model_name = LocationSlug.LOCATION_TYPE_BY_CODE.get(code) if model_name is None or not pk.isdigit(): return None model = apps.get_model('mapdata', model_name) location = locations_for_request(request).get(int(pk), None) if location is None or not isinstance(location, model): return None if location.slug is not None: location = LocationRedirect(slug=slug, target=location) else: location = locations_by_slug_for_request(request).get(slug, None) cache.set(cache_key, location, 300) return location
def list(self, request, *args, **kwargs): searchable = 'searchable' in request.GET detailed = 'detailed' in request.GET geometry = 'geometry' in request.GET cache_key = 'mapdata:api:location:list:%d:%s:%d' % ( searchable + detailed * 2 + geometry * 4, AccessPermission.cache_key_for_request(request), request.user_permissions.can_access_base_mapdata) result = cache.get(cache_key, None) if result is None: if searchable: locations = searchable_locations_for_request(request) else: locations = visible_locations_for_request(request).values() result = tuple( obj.serialize(include_type=True, detailed=detailed, search=searchable, geometry=geometry and MapdataViewSet.can_access_geometry(request, obj), simple_geometry=True) for obj in locations) cache.set(cache_key, result, 300) return Response(result)
def get_location_by_slug_for_request(slug: str, request) -> Optional[LocationSlug]: cache_key = 'mapdata:location:by_slug:%s:%s' % (AccessPermission.cache_key_for_request(request), slug) location = proxied_cache.get(cache_key, None) if location is not None: return location if slug.startswith('c:'): location = get_custom_location_for_request(slug, request) if location is None: return None elif ':' in slug: code, pk = slug.split(':', 1) model_name = LocationSlug.LOCATION_TYPE_BY_CODE.get(code) if model_name is None or not pk.isdigit(): return None model = apps.get_model('mapdata', model_name) location = locations_for_request(request).get(int(pk), None) if location is None or not isinstance(location, model): return None if location.slug is not None: location = LocationRedirect(slug=slug, target=location) else: location = locations_by_slug_for_request(request).get(slug, None) proxied_cache.set(cache_key, location, 1800) return location
def get_visible_spaces(request): cache_key = 'editor:visible_spaces:%s:%s' % ( request.changeset.raw_cache_key_by_changes, AccessPermission.cache_key_for_request(request, with_update=False) ) visible_spaces = cache.get(cache_key, None) if visible_spaces is None: Space = request.changeset.wrap_model('Space') visible_spaces = tuple(Space.qs_for_request(request).values_list('pk', flat=True)) cache.set(cache_key, visible_spaces, 900) return visible_spaces
def get_visible_spaces(request): cache_key = 'editor:visible_spaces:%s:%s' % ( request.changeset.raw_cache_key_by_changes, AccessPermission.cache_key_for_request(request, with_update=False)) visible_spaces = cache.get(cache_key, None) if visible_spaces is None: Space = request.changeset.wrap_model('Space') visible_spaces = tuple( Space.qs_for_request(request).values_list('pk', flat=True)) cache.set(cache_key, visible_spaces, 900) return visible_spaces
def etag_func(request, *args, **kwargs): try: changeset = request.changeset except AttributeError: changeset = ChangeSet.get_for_request(request) request.changeset = changeset return ( get_language() + ':' + changeset.raw_cache_key_by_changes + ':' + AccessPermission.cache_key_for_request(request, with_update=False) + ':' + str(request.user.pk or 0))
def locations_by_slug_for_request(request) -> Mapping[str, LocationSlug]: cache_key = 'mapdata:locations:by_slug:%s' % AccessPermission.cache_key_for_request(request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = {location.slug: location for location in locations_for_request(request).values() if location.slug} proxied_cache.set(cache_key, locations, 1800) return locations
def etag_func(request, *args, **kwargs): try: changeset = request.changeset except AttributeError: changeset = ChangeSet.get_for_request(request) request.changeset = changeset return (get_language() + ':' + changeset.raw_cache_key_by_changes + ':' + AccessPermission.cache_key_for_request(request, with_update=False) + ':' + str(request.user.pk or 0) + ':' + str(int(request.user_permissions.can_access_base_mapdata)) + ':' + ','.join(str(i) for i in request.user_space_accesses) + ':' + str(int(request.user.is_superuser)))
def visible_locations_for_request(request) -> Mapping[int, Location]: cache_key = 'mapdata:locations:real:%s' % AccessPermission.cache_key_for_request(request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = {pk: location for pk, location in locations_for_request(request).items() if not isinstance(location, LocationRedirect) and (location.can_search or location.can_describe)} proxied_cache.set(cache_key, locations, 1800) return locations
def etag_func(request, *args, **kwargs): try: changeset = request.changeset except AttributeError: changeset = ChangeSet.get_for_request(request) request.changeset = changeset return (get_language() + ':' + changeset.raw_cache_key_by_changes + ':' + AccessPermission.cache_key_for_request( request, with_update=False) + ':' + str(request.user.pk or 0) + ':' + str(int(request.user_permissions.can_access_base_mapdata)) + ':' + ','.join(str(i) for i in request.user_space_accesses) + ':' + str(int(request.user.is_superuser)))
def levels_by_short_label_for_request(request) -> Mapping[str, Level]: cache_key = 'mapdata:levels:by_short_label:%s' % AccessPermission.cache_key_for_request(request) levels = proxied_cache.get(cache_key, None) if levels is not None: return levels levels = OrderedDict( (level.short_label, level) for level in Level.qs_for_request(request).filter(on_top_of_id__isnull=True).order_by('base_altitude') ) proxied_cache.set(cache_key, levels, 1800) return levels
def searchable_locations_for_request(request) -> List[Location]: cache_key = 'mapdata:locations:searchable:%s' % AccessPermission.cache_key_for_request(request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = (location for location in locations_for_request(request).values() if isinstance(location, Location)) locations = tuple(location for location in locations if location.can_search) locations = sorted(locations, key=operator.attrgetter('order'), reverse=True) proxied_cache.set(cache_key, locations, 1800) return locations
def _get_list(self, request): qs = optimize_query(self.get_queryset()) filters = [] if issubclass(qs.model, LevelGeometryMixin) and 'level' in request.GET: filters.append(self.qs_filter(field='level', model=Level, key='pk', value=request.GET['level'])) if issubclass(qs.model, SpaceGeometryMixin) and 'space' in request.GET: filters.append(self.qs_filter(field='space', model=Space, key='pk', value=request.GET['space'])) if issubclass(qs.model, LocationGroup) and 'category' in request.GET: filters.append(self.qs_filter(field='category', model=LocationGroupCategory, key='pk' if request.GET['category'].isdigit() else 'name', value=request.GET['category'])) if issubclass(qs.model, SpecificLocation) and 'group' in request.GET: filters.append(self.qs_filter(field='groups', model=LocationGroup, key='pk', value=request.GET['group'])) if qs.model == Level and 'on_top_of' in request.GET: value = None if request.GET['on_top_of'] == 'null' else request.GET['on_top_of'] filters.append(self.qs_filter(field='on_top_of', model=Level, key='pk', value=value)) cache_key = 'mapdata:api:%s:%s' % (qs.model.__name__, AccessPermission.cache_key_for_request(request)) for qs_filter in filters: cache_key += ';%s,%s' % (qs_filter.field, qs_filter.value) results = cache.get(cache_key, None) if results is not None: return results for qs_filter in filters: if qs_filter.key == 'pk' and not qs_filter.value.isdigit(): raise ValidationError(detail={ 'detail': _('%(field)s is not an integer.') % {'field': qs_filter.field} }) for qs_filter in filters: if qs_filter.value is not None: keys = self._get_keys_for_model(request, qs_filter.model, qs_filter.key) value = int(qs_filter.value) if qs_filter.key == 'pk' else qs_filter.value if value not in keys: raise NotFound(detail=_('%(model)s not found.') % {'model': qs_filter.model._meta.verbose_name}) results = tuple(qs.order_by(*self.order_by)) cache.set(cache_key, results, 300) return results
def _get_keys_for_model(self, request, model, key): if hasattr(model, 'qs_for_request'): cache_key = 'mapdata:api:keys:%s:%s:%s' % (model.__name__, key, AccessPermission.cache_key_for_request(request)) qs = model.qs_for_request(request) else: cache_key = 'mapdata:api:keys:%s:%s:%s' % (model.__name__, key, MapUpdate.current_cache_key()) qs = model.objects.all() result = cache.get(cache_key, None) if result is not None: return result result = set(qs.values_list(key, flat=True)) cache.set(cache_key, result, 300) return result
def _get_keys_for_model(self, request, model, key): if hasattr(model, 'qs_for_request'): cache_key = 'mapdata:api:keys:%s:%s:%s' % ( model.__name__, key, AccessPermission.cache_key_for_request(request)) qs = model.qs_for_request(request) else: cache_key = 'mapdata:api:keys:%s:%s:%s' % ( model.__name__, key, MapUpdate.current_cache_key()) qs = model.objects.all() result = cache.get(cache_key, None) if result is not None: return result result = set(qs.values_list(key, flat=True)) cache.set(cache_key, result, 300) return result
def get_location_by_slug_for_request( slug: str, request) -> Optional[Union[LocationSlug, Position]]: cache_key = 'mapdata:location:by_slug:%s:%s' % ( AccessPermission.cache_key_for_request(request), slug) location = proxied_cache.get(cache_key, None) if location is not None: return location if slug.startswith('c:'): location = get_custom_location_for_request(slug, request) if location is None: return None elif slug.startswith('p:'): try: # return immediately, don't cache for obvious reasons return Position.objects.get(secret=slug[2:]) except Position.DoesNotExist: return None elif ':' in slug: code, pk = slug.split(':', 1) model_name = LocationSlug.LOCATION_TYPE_BY_CODE.get(code) if model_name is None or not pk.isdigit(): return None model = apps.get_model('mapdata', model_name) location = locations_for_request(request).get(int(pk), None) if location is None or not isinstance(location, model): return None if location.slug is not None: location = LocationRedirect(slug=slug, target=location) else: location = locations_by_slug_for_request(request).get(slug, None) proxied_cache.set(cache_key, location, 1800) return location
def list(self, request, *args, **kwargs): searchable = 'searchable' in request.GET detailed = 'detailed' in request.GET geometry = 'geometry' in request.GET cache_key = 'mapdata:api:location:list:%d:%s:%d' % ( searchable + detailed*2 + geometry*4, AccessPermission.cache_key_for_request(request), request.user_permissions.can_access_base_mapdata ) result = cache.get(cache_key, None) if result is None: if searchable: locations = searchable_locations_for_request(request) else: locations = visible_locations_for_request(request).values() result = tuple(obj.serialize(include_type=True, detailed=detailed, geometry=geometry and MapdataViewSet.can_access_geometry(request, obj), simple_geometry=True) for obj in locations) cache.set(cache_key, result, 300) return Response(result)
def __init__(self, *args, space_id=None, request=None, geometry_editable=False, is_json=False, **kwargs): self.request = request super().__init__(*args, **kwargs) creating = not self.instance.pk if hasattr(self.instance, 'author_id'): if self.instance.author_id is None: self.instance.author = request.user if 'geometry' in self.fields: if not geometry_editable: # can't see this geometry in editor self.fields.pop('geometry') else: # hide geometry widget self.fields['geometry'].widget = HiddenInput() if not creating: self.initial['geometry'] = json.dumps(mapping(self.instance.geometry), separators=(',', ':')) if self._meta.model.__name__ == 'Source' and self.request.user.is_superuser: Source = self.request.changeset.wrap_model('Source') sources = {s['name']: s for s in Source.objects.all().values('name', 'access_restriction_id', 'left', 'bottom', 'right', 'top')} used_names = set(sources.keys()) all_names = set(os.listdir(settings.SOURCES_ROOT)) if not creating: used_names.remove(self.instance.name) all_names.add(self.instance.name) self.fields['name'].widget = Select(choices=tuple((s, s) for s in sorted(all_names-used_names))) if creating: for s in sources.values(): s['access_restriction'] = s['access_restriction_id'] del s['access_restriction_id'] self.fields['copy_from'] = ChoiceField( choices=tuple((('', '---------'), ))+tuple( (json.dumps(sources[name], separators=(',', ':'), cls=DjangoJSONEncoder), name) for name in sorted(used_names) ), required=False ) self.fields['fixed_x'] = DecimalField(label='fixed x', required=False, max_digits=7, decimal_places=3, initial=0) self.fields['fixed_y'] = DecimalField(label='fixed y', required=False, max_digits=7, decimal_places=3, initial=0) self.fields['scale_x'] = DecimalField(label='scale x (m/px)', required=False, max_digits=7, decimal_places=3, initial=1) self.fields['scale_y'] = DecimalField(label='scale y (m/px)', required=False, max_digits=7, decimal_places=3, initial=1) self.fields['lock_aspect'] = BooleanField(label='lock aspect ratio', required=False, initial=True) self.fields['lock_scale'] = BooleanField(label='lock scale (for moving)', required=False, initial=True) self.fields.move_to_end('lock_scale', last=False) self.fields.move_to_end('lock_aspect', last=False) self.fields.move_to_end('scale_y', last=False) self.fields.move_to_end('scale_x', last=False) self.fields.move_to_end('fixed_y', last=False) self.fields.move_to_end('fixed_x', last=False) self.fields.move_to_end('access_restriction', last=False) if creating: self.fields.move_to_end('copy_from', last=False) self.fields.move_to_end('name', last=False) if self._meta.model.__name__ == 'AccessRestriction': AccessRestrictionGroup = self.request.changeset.wrap_model('AccessRestrictionGroup') self.fields['groups'].label_from_instance = lambda obj: obj.title self.fields['groups'].queryset = AccessRestrictionGroup.qs_for_request(self.request) elif 'groups' in self.fields: LocationGroupCategory = self.request.changeset.wrap_model('LocationGroupCategory') kwargs = {'allow_'+self._meta.model._meta.default_related_name: True} categories = LocationGroupCategory.objects.filter(**kwargs).prefetch_related('groups') if self.instance.pk: instance_groups = tuple(self.instance.groups.values_list('pk', flat=True)) else: instance_groups = () self.fields.pop('groups') for category in categories: choices = tuple((str(group.pk), group.title) for group in sorted(category.groups.all(), key=self.sort_group)) category_groups = set(group.pk for group in category.groups.all()) initial = tuple(str(pk) for pk in instance_groups if pk in category_groups) if category.single: name = 'group_'+category.name initial = initial[0] if initial else '' choices = (('', '---'), )+choices field = ChoiceField(label=category.title, required=False, initial=initial, choices=choices, help_text=category.help_text) else: name = 'groups_'+category.name field = MultipleChoiceField(label=category.title_plural, required=False, initial=initial, choices=choices, help_text=category.help_text) self.fields[name] = field if 'category' in self.fields: self.fields['category'].label_from_instance = lambda obj: obj.title if 'access_restriction' in self.fields: AccessRestriction = self.request.changeset.wrap_model('AccessRestriction') self.fields['access_restriction'].label_from_instance = lambda obj: obj.title self.fields['access_restriction'].queryset = AccessRestriction.qs_for_request(self.request) if 'base_mapdata_accessible' in self.fields: if not request.user.is_superuser: self.fields['base_mapdata_accessible'].disabled = True if space_id and 'target_space' in self.fields: Space = self.request.changeset.wrap_model('Space') GraphNode = self.request.changeset.wrap_model('GraphNode') GraphEdge = self.request.changeset.wrap_model('GraphEdge') cache_key = 'editor:neighbor_spaces:%s:%s%d' % ( self.request.changeset.raw_cache_key_by_changes, AccessPermission.cache_key_for_request(request, with_update=False), space_id ) other_spaces = cache.get(cache_key, None) if other_spaces is None: AccessPermission.cache_key_for_request(request, with_update=False) + ':' + str(request.user.pk or 0) space_nodes = set(GraphNode.objects.filter(space_id=space_id).values_list('pk', flat=True)) space_edges = GraphEdge.objects.filter( Q(from_node_id__in=space_nodes) | Q(to_node_id__in=space_nodes) ).values_list('from_node_id', 'to_node_id') other_nodes = set(chain(*space_edges)) - space_nodes other_spaces = set(GraphNode.objects.filter(pk__in=other_nodes).values_list('space_id', flat=True)) other_spaces.discard(space_id) cache.set(cache_key, other_spaces, 900) for space_field in ('origin_space', 'target_space'): other_space_id = getattr(self.instance, space_field+'_id', None) if other_space_id: other_spaces.add(other_space_id) space_qs = Space.qs_for_request(self.request).filter(pk__in=other_spaces) for space_field in ('origin_space', 'target_space'): if space_field in self.fields: self.fields[space_field].label_from_instance = lambda obj: obj.title self.fields[space_field].queryset = space_qs self.redirect_slugs = None self.add_redirect_slugs = None self.remove_redirect_slugs = None if 'slug' in self.fields: self.redirect_slugs = sorted(self.instance.redirects.values_list('slug', flat=True)) self.fields['redirect_slugs'] = CharField(label=_('Redirecting Slugs (comma seperated)'), required=False, initial=','.join(self.redirect_slugs)) self.fields.move_to_end('redirect_slugs', last=False) self.fields.move_to_end('slug', last=False) if 'from_node' in self.fields: self.fields['from_node'].widget = HiddenInput() if 'to_node' in self.fields: self.fields['to_node'].widget = HiddenInput() if 'data' in self.fields and 'data' in self.initial: self.initial['data'] = json.dumps(self.initial['data']) self.is_json = is_json self.missing_fields = tuple((name, field) for name, field in self.fields.items() if name not in self.data and not field.required)
def locations_for_request(request) -> Mapping[int, LocationSlug]: cache_key = 'mapdata:locations:%s' % AccessPermission.cache_key_for_request(request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = LocationSlug.objects.all().order_by('id') conditions = [] for model in get_submodels(Location): related_name = model._meta.default_related_name condition = Q(**{related_name + '__isnull': False}) # noinspection PyUnresolvedReferences condition &= model.q_for_request(request, prefix=related_name + '__') conditions.append(condition) locations = locations.filter(reduce(operator.or_, conditions)) locations.select_related('redirect', 'locationgroups__category') # prefetch locationgroups base_qs = LocationGroup.qs_for_request(request).select_related('category') for model in get_submodels(SpecificLocation): locations = locations.prefetch_related(Prefetch(model._meta.default_related_name + '__groups', queryset=base_qs)) locations = {obj.pk: obj.get_child() for obj in locations} # add locations to groups locationgroups = {pk: obj for pk, obj in locations.items() if isinstance(obj, LocationGroup)} for group in locationgroups.values(): group.locations = [] for obj in locations.values(): if not isinstance(obj, SpecificLocation): continue for group in obj.groups.all(): group = locationgroups.get(group.pk, None) if group is not None: group.locations.append(obj) # add levels to spaces remove_pks = set() levels = {pk: obj for pk, obj in locations.items() if isinstance(obj, Level)} for pk, obj in locations.items(): if isinstance(obj, LevelGeometryMixin): level = levels.get(obj.level_id, None) if level is None: remove_pks.add(pk) continue obj._level_cache = level # hide spaces on hidden levels for pk in remove_pks: locations.pop(pk) # add spaces to areas and POIs remove_pks = set() spaces = {pk: obj for pk, obj in locations.items() if isinstance(obj, Space)} for pk, obj in locations.items(): if isinstance(obj, SpaceGeometryMixin): space = spaces.get(obj.space_id, None) if space is None: remove_pks.add(pk) continue obj._space_cache = space # hide locations on hidden spaces for pk in remove_pks: locations.pop(pk) # add targets to LocationRedirects levels = {pk: obj for pk, obj in locations.items() if isinstance(obj, Level)} for obj in locations.values(): if isinstance(obj, LocationRedirect): obj._target_cache = locations.get(obj.target_id, None) # apply better space geometries for pk, geometry in get_better_space_geometries().items(): if pk in locations: locations[pk].geometry = geometry # precache cached properties for obj in locations.values(): # noinspection PyStatementEffect obj.subtitle, obj.order if isinstance(obj, GeometryMixin): # noinspection PyStatementEffect obj.point proxied_cache.set(cache_key, locations, 1800) return locations
def locations_for_request(request) -> Mapping[int, LocationSlug]: # todo this takes a long time because it's a lot of data, we might want to change that cache_key = 'mapdata:locations:%s' % AccessPermission.cache_key_for_request( request) locations = proxied_cache.get(cache_key, None) if locations is not None: return locations locations = LocationSlug.objects.all().order_by('id') conditions = [] for model in get_submodels(Location): related_name = model._meta.default_related_name condition = Q(**{related_name + '__isnull': False}) # noinspection PyUnresolvedReferences condition &= model.q_for_request(request, prefix=related_name + '__') conditions.append(condition) locations = locations.select_related( related_name + '__label_settings').prefetch_related(related_name + '__redirects') locations = locations.filter(reduce(operator.or_, conditions)) locations = locations.select_related('redirect', 'locationgroups__category') # prefetch locationgroups base_qs = LocationGroup.qs_for_request(request).select_related( 'category', 'label_settings') for model in get_submodels(SpecificLocation): locations = locations.prefetch_related( Prefetch(model._meta.default_related_name + '__groups', queryset=base_qs)) locations = {obj.pk: obj.get_child() for obj in locations} # add locations to groups locationgroups = { pk: obj for pk, obj in locations.items() if isinstance(obj, LocationGroup) } for group in locationgroups.values(): group.locations = [] for obj in locations.values(): if not isinstance(obj, SpecificLocation): continue for group in obj.groups.all(): group = locationgroups.get(group.pk, None) if group is not None: group.locations.append(obj) # add levels to spaces remove_pks = set() levels = { pk: obj for pk, obj in locations.items() if isinstance(obj, Level) } for pk, obj in locations.items(): if isinstance(obj, LevelGeometryMixin): level = levels.get(obj.level_id, None) if level is None: remove_pks.add(pk) continue obj._level_cache = level # hide spaces on hidden levels for pk in remove_pks: locations.pop(pk) # add spaces to areas and POIs remove_pks = set() spaces = { pk: obj for pk, obj in locations.items() if isinstance(obj, Space) } for pk, obj in locations.items(): if isinstance(obj, SpaceGeometryMixin): space = spaces.get(obj.space_id, None) if space is None: remove_pks.add(pk) continue obj._space_cache = space # hide locations on hidden spaces for pk in remove_pks: locations.pop(pk) # add targets to LocationRedirects levels = { pk: obj for pk, obj in locations.items() if isinstance(obj, Level) } for obj in locations.values(): if isinstance(obj, LocationRedirect): obj._target_cache = locations.get(obj.target_id, None) # apply better space geometries for pk, geometry in get_better_space_geometries().items(): if pk in locations: locations[pk].geometry = geometry # precache cached properties for obj in locations.values(): # noinspection PyStatementEffect obj.subtitle, obj.order if isinstance(obj, GeometryMixin): # noinspection PyStatementEffect obj.point proxied_cache.set(cache_key, locations, 1800) return locations
def _get_list(self, request): qs = optimize_query(self.get_queryset()) filters = [] if issubclass(qs.model, LevelGeometryMixin) and 'level' in request.GET: filters.append( self.qs_filter(field='level', model=Level, key='pk', value=request.GET['level'])) if issubclass(qs.model, SpaceGeometryMixin) and 'space' in request.GET: filters.append( self.qs_filter(field='space', model=Space, key='pk', value=request.GET['space'])) if issubclass(qs.model, LocationGroup) and 'category' in request.GET: filters.append( self.qs_filter( field='category', model=LocationGroupCategory, key='pk' if request.GET['category'].isdigit() else 'name', value=request.GET['category'])) if issubclass(qs.model, SpecificLocation) and 'group' in request.GET: filters.append( self.qs_filter(field='groups', model=LocationGroup, key='pk', value=request.GET['group'])) if qs.model == Level and 'on_top_of' in request.GET: value = None if request.GET[ 'on_top_of'] == 'null' else request.GET['on_top_of'] filters.append( self.qs_filter(field='on_top_of', model=Level, key='pk', value=value)) cache_key = 'mapdata:api:%s:%s' % ( qs.model.__name__, AccessPermission.cache_key_for_request(request)) for qs_filter in filters: cache_key += ';%s,%s' % (qs_filter.field, qs_filter.value) results = cache.get(cache_key, None) if results is not None: return results for qs_filter in filters: if qs_filter.key == 'pk' and not qs_filter.value.isdigit(): raise ValidationError( detail={ 'detail': _('%(field)s is not an integer.') % { 'field': qs_filter.field } }) for qs_filter in filters: if qs_filter.value is not None: keys = self._get_keys_for_model(request, qs_filter.model, qs_filter.key) value = int(qs_filter.value ) if qs_filter.key == 'pk' else qs_filter.value if value not in keys: raise NotFound( detail=_('%(model)s not found.') % {'model': qs_filter.model._meta.verbose_name}) results = tuple(qs.order_by(*self.order_by)) cache.set(cache_key, results, 300) return results
def __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'])
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)