def register_signals(): from c3nav.mapdata.models.geometry.base import GeometryMixin for model in get_submodels(GeometryMixin): post_delete.connect(geometry_deleted, sender=model) from c3nav.mapdata.models.locations import SpecificLocation for model in get_submodels(SpecificLocation): m2m_changed.connect(locationgroup_changed, sender=model.groups.through)
def select_related_target(self): if self.model != LocationSlug: raise TypeError qs = self.get_queryset() qs = qs.select_related('redirect__target', *('redirect__target__'+model._meta.default_related_name for model in get_submodels(Location) + [LocationRedirect])) return qs
def get_queryset(self): """ make sure that the database does not return objects that have been deleted in this changeset """ qs = self._wrap_queryset(self._obj.model.objects.all()) return qs.exclude(pk__in=tuple(chain(*(self._changeset.deleted_existing.get(submodel, ()) for submodel in get_submodels(self._obj.model)))))
def get_queryset(self): result = super().get_queryset() if self.model == LocationSlug: result = result.select_related( *(model._meta.default_related_name for model in get_submodels(Location) + [LocationRedirect])) return result
def get_changed_object(self, obj) -> ChangedObject: if isinstance(obj, ModelInstanceWrapper): obj = obj._obj model = obj.__class__ pk = obj.pk if pk is None: return ChangedObject(changeset=self, model_class=model) self.fill_changes_cache() objects = tuple( obj for obj in ((submodel, self.changed_objects.get(submodel, {}).get(pk, None)) for submodel in get_submodels(model)) if obj[1] is not None) if len(objects) > 1: raise model.MultipleObjectsReturned if objects: return objects[0][1] if is_created_pk(pk): raise model.DoesNotExist return ChangedObject(changeset=self, model_class=model, existing_object_pk=pk)
def register_changed_geometries(self, do_query=True): from c3nav.mapdata.models.geometry.space import SpaceGeometryMixin for model in get_submodels(SpecificLocation): query = getattr(self, model._meta.default_related_name).all() if do_query: if issubclass(model, SpaceGeometryMixin): query = query.select_related('space') for obj in query: obj.register_change(force=True)
def register_changed_geometries(self): from c3nav.mapdata.models.geometry.space import SpaceGeometryMixin query = self.groups.all() for model in get_submodels(SpecificLocation): related_name = model._meta.default_related_name subquery = model.objects.all() if issubclass(model, SpaceGeometryMixin): subquery = subquery.select_related('space') query.prefetch_related(Prefetch('groups__'+related_name, subquery)) for group in query: group.register_changed_geometries(do_query=False)
def get_queryset(self): result = super().get_queryset() if self.model == LocationSlug: for model in get_submodels(Location) + [LocationRedirect]: result = result.select_related(model._meta.default_related_name) try: model._meta.get_field('space') except FieldDoesNotExist: pass else: result = result.select_related(model._meta.default_related_name+'__space') return result
def handle(self, *args, **options): with transaction.atomic(): for model in get_submodels(GeometryMixin): for instance in model.objects.all(): old_geom = instance.geometry.wrapped_geojson if instance.geometry.is_empty: print('Deleted %s' % instance) instance.delete() continue instance.save() instance.refresh_from_db() if instance.geometry.wrapped_geojson != old_geom: print('Fixed %s' % instance)
def qs_for_request(cls, request): if request.user_permissions.review_all_reports: return cls.objects.all() elif request.user.is_authenticated: location_ids = set() review_group_ids = request.user_permissions.review_group_ids for model in get_submodels(SpecificLocation): location_ids.update(set( model.objects.filter(groups__in=review_group_ids).values_list('pk', flat=True) )) return cls.objects.filter( Q(author=request.user) | Q(location_id__in=location_ids) | Q(created_groups__in=review_group_ids) ) else: return cls.objects.none()
def get_changed_object(self, obj) -> ChangedObject: if isinstance(obj, ModelInstanceWrapper): obj = obj._obj model = obj.__class__ pk = obj.pk if pk is None: return ChangedObject(changeset=self, model_class=model) self.fill_changes_cache() objects = tuple(obj for obj in ((submodel, self.changed_objects.get(submodel, {}).get(pk, None)) for submodel in get_submodels(model)) if obj[1] is not None) if len(objects) > 1: raise model.MultipleObjectsReturned if objects: return objects[0][1] if is_created_pk(pk): raise model.DoesNotExist return ChangedObject(changeset=self, model_class=model, existing_object_pk=pk)
def get_child(self, instance=None): for model in get_submodels(Location) + [LocationRedirect]: with suppress(AttributeError): return getattr(instance or self, model._meta.default_related_name) return None
def _submodels(self): """ Get non-abstract submodels for this model including the model itself. """ return get_submodels(self._obj)
def types(self, request): return MapdataViewSet.list_types(get_submodels(Location), geomtype=False)
def geometrytypes(self, request): return self.list_types(get_submodels(SpaceGeometryMixin))
def get_child(self, instance=None): for model in get_submodels(Location)+[LocationRedirect]: with suppress(AttributeError): return getattr(instance or self, model._meta.default_related_name) return None
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