2017-10-31 15:22:12 +01:00
|
|
|
import operator
|
|
|
|
from functools import reduce
|
|
|
|
from typing import List, Mapping, Optional
|
|
|
|
|
|
|
|
from django.apps import apps
|
|
|
|
from django.core.cache import cache
|
|
|
|
from django.db.models import Prefetch, Q
|
|
|
|
|
|
|
|
from c3nav.mapdata.models import Level, Location, LocationGroup
|
|
|
|
from c3nav.mapdata.models.access import AccessPermission
|
|
|
|
from c3nav.mapdata.models.geometry.base import GeometryMixin
|
|
|
|
from c3nav.mapdata.models.geometry.level import LevelGeometryMixin, Space
|
|
|
|
from c3nav.mapdata.models.geometry.space import SpaceGeometryMixin
|
|
|
|
from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug, SpecificLocation
|
|
|
|
from c3nav.mapdata.utils.models import get_submodels
|
|
|
|
|
|
|
|
|
|
|
|
def locations_for_request(request) -> Mapping[int, LocationSlug]:
|
|
|
|
cache_key = 'mapdata:locations:%s' % AccessPermission.cache_key_for_request(request)
|
|
|
|
locations = 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
|
|
|
|
levels = {pk: obj for pk, obj in locations.items() if isinstance(obj, Level)}
|
|
|
|
for obj in locations.values():
|
|
|
|
if isinstance(obj, LevelGeometryMixin):
|
|
|
|
obj.level_cache = levels.get(obj.level_id, None)
|
|
|
|
|
|
|
|
# add spaces to areas and POIs
|
|
|
|
spaces = {pk: obj for pk, obj in locations.items() if isinstance(obj, Space)}
|
|
|
|
for obj in locations.values():
|
|
|
|
if isinstance(obj, SpaceGeometryMixin):
|
|
|
|
obj.space_cache = spaces.get(obj.space_id, None)
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
# precache cached properties
|
|
|
|
for obj in locations.values():
|
|
|
|
# noinspection PyStatementEffect
|
|
|
|
obj.subtitle, obj.order
|
|
|
|
if isinstance(obj, GeometryMixin):
|
|
|
|
# noinspection PyStatementEffect
|
|
|
|
obj.centroid
|
|
|
|
|
|
|
|
cache.set(cache_key, locations, 300)
|
|
|
|
|
|
|
|
return locations
|
|
|
|
|
|
|
|
|
|
|
|
def visible_locations_for_request(request) -> Mapping[int, Location]:
|
|
|
|
cache_key = 'mapdata:locations:real:%s' % AccessPermission.cache_key_for_request(request)
|
|
|
|
locations = 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)}
|
|
|
|
|
|
|
|
cache.set(cache_key, locations, 300)
|
|
|
|
|
|
|
|
return locations
|
|
|
|
|
|
|
|
|
|
|
|
def searchable_locations_for_request(request) -> List[Location]:
|
|
|
|
cache_key = 'mapdata:locations:searchable:%s' % AccessPermission.cache_key_for_request(request)
|
|
|
|
locations = 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)
|
|
|
|
|
|
|
|
cache.set(cache_key, locations, 300)
|
|
|
|
|
|
|
|
return locations
|
|
|
|
|
|
|
|
|
|
|
|
def locations_by_slug_for_request(request) -> Mapping[str, LocationSlug]:
|
|
|
|
cache_key = 'mapdata:locations:by_slug:%s' % AccessPermission.cache_key_for_request(request)
|
|
|
|
locations = 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}
|
|
|
|
|
|
|
|
cache.set(cache_key, locations, 300)
|
|
|
|
|
|
|
|
return locations
|
|
|
|
|
|
|
|
|
|
|
|
def get_location_by_slug_for_request(slug: str, request) -> Optional[LocationSlug]:
|
2017-10-31 17:32:37 +01:00
|
|
|
cache_key = 'mapdata:location:by_slug:%s:%s' % (AccessPermission.cache_key_for_request(request), slug)
|
2017-10-31 18:35:42 +01:00
|
|
|
location = cache.get(cache_key, None)
|
|
|
|
if location is not None:
|
|
|
|
return location
|
2017-10-31 15:22:12 +01:00
|
|
|
|
|
|
|
if ':' 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)
|
|
|
|
|
2017-10-31 18:35:42 +01:00
|
|
|
return location
|