add slim and full location list endpoint, geometry optional
This commit is contained in:
parent
8b47a89865
commit
87e5e56ea1
6 changed files with 148 additions and 31 deletions
|
@ -82,7 +82,7 @@ class GeometryMixin(SerializableMixin):
|
|||
|
||||
def _serialize(self, geometry=True, simple_geometry=False, **kwargs):
|
||||
result = super()._serialize(simple_geometry=simple_geometry, **kwargs)
|
||||
if geometry:
|
||||
if geometry and "geometry" not in self.get_deferred_fields():
|
||||
result['geometry'] = format_geojson(smart_mapping(self.geometry), rounded=False)
|
||||
if simple_geometry:
|
||||
result['point'] = (self.level_id, ) + tuple(round(i, 2) for i in self.point.coords[0])
|
||||
|
|
|
@ -113,7 +113,8 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model):
|
|||
result = super().serialize(detailed=detailed, **kwargs)
|
||||
if not detailed:
|
||||
fields = ('id', 'type', 'slug', 'title', 'subtitle', 'icon', 'point', 'bounds', 'grid_square',
|
||||
'locations', 'on_top_of', 'label_settings', 'label_override', 'add_search', 'dynamic')
|
||||
'locations', 'on_top_of', 'label_settings', 'label_override', 'add_search', 'dynamic',
|
||||
'locationtype', 'geometry')
|
||||
result = {name: result[name] for name in fields if name in result}
|
||||
return result
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ from pydantic import Field as APIField
|
|||
from c3nav.api.newauth import auth_responses
|
||||
from c3nav.mapdata.models import Source
|
||||
from c3nav.mapdata.models.access import AccessPermission
|
||||
from c3nav.mapdata.schemas.models import AnyLocationSchema
|
||||
from c3nav.mapdata.schemas.filters import BySearchableFilter, RemoveGeometryFilter
|
||||
from c3nav.mapdata.schemas.models import FullLocationSchema, SlimLocationSchema
|
||||
from c3nav.mapdata.schemas.responses import BoundsSchema
|
||||
from c3nav.mapdata.utils.locations import searchable_locations_for_request, visible_locations_for_request
|
||||
|
||||
|
@ -34,29 +35,39 @@ def can_access_geometry(request):
|
|||
return True # todo: implementFd
|
||||
|
||||
|
||||
@map_api_router.get('/locations/', response={200: list[AnyLocationSchema], **auth_responses},
|
||||
summary="Get map boundaries")
|
||||
def location_list(request, params: Query[LocationEndpointParameters]):
|
||||
# todo: cache, visibility, etc…
|
||||
searchable = params.searchable
|
||||
detailed = True # todo: configurable?
|
||||
geometry = True # todo: configurable
|
||||
class LocationListFilters(BySearchableFilter, RemoveGeometryFilter):
|
||||
pass
|
||||
|
||||
|
||||
def _location_list(request, detailed: bool, filters: LocationListFilters):
|
||||
# todo: cache, visibility, etc…
|
||||
cache_key = 'mapdata:api:location:list:%d:%s:%d' % (
|
||||
searchable + detailed*2 + geometry*4,
|
||||
filters.searchable + detailed*2 + filters.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:
|
||||
if filters.searchable:
|
||||
locations = searchable_locations_for_request(request)
|
||||
else:
|
||||
locations = visible_locations_for_request(request).values()
|
||||
|
||||
result = tuple(obj.serialize(detailed=detailed, search=searchable,
|
||||
geometry=geometry and can_access_geometry(request),
|
||||
result = tuple(obj.serialize(detailed=detailed, search=filters.searchable,
|
||||
geometry=filters.geometry and can_access_geometry(request),
|
||||
simple_geometry=True)
|
||||
for obj in locations)
|
||||
cache.set(cache_key, result, 300)
|
||||
return result
|
||||
|
||||
|
||||
@map_api_router.get('/locations/', response={200: list[SlimLocationSchema], **auth_responses},
|
||||
summary="Get locations (with most important attributes)")
|
||||
def location_list(request, filters: Query[LocationListFilters]):
|
||||
return _location_list(request, detailed=False, filters=filters)
|
||||
|
||||
|
||||
@map_api_router.get('/locations/full/', response={200: list[FullLocationSchema], **auth_responses},
|
||||
summary="Get locations (with all attributes)")
|
||||
def location_list_full(request, filters: Query[LocationListFilters]):
|
||||
return _location_list(request, detailed=True, filters=filters)
|
||||
|
|
|
@ -134,3 +134,29 @@ class ByOnTopOfFilter(FilterSchema):
|
|||
if self.on_top_of is not None:
|
||||
qs = qs.filter(on_top_of_id=None if self.on_top_of == "null" else self.on_top_of)
|
||||
return super().filter_qs(qs)
|
||||
|
||||
|
||||
class BySearchableFilter(FilterSchema):
|
||||
searchable: bool = APIField(
|
||||
False,
|
||||
title='searchable locations only',
|
||||
description='only show locations that should show up in search'
|
||||
)
|
||||
|
||||
def filter_qs(self, qs: QuerySet) -> QuerySet:
|
||||
if self.searchable is not None:
|
||||
qs = qs.filter(can_search=True)
|
||||
return super().filter_qs(qs)
|
||||
|
||||
|
||||
class RemoveGeometryFilter(FilterSchema):
|
||||
geometry: bool = APIField(
|
||||
False, # todo: should be false
|
||||
title='include geometry',
|
||||
description='by default, geometry will be ommited. set to true to include it (if available)'
|
||||
)
|
||||
|
||||
def filter_qs(self, qs: QuerySet) -> QuerySet:
|
||||
if not self.geometry:
|
||||
qs = qs.defer('geometry')
|
||||
return super().filter_qs(qs)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Optional
|
||||
from typing import Any, ClassVar, Optional
|
||||
|
||||
from ninja import Schema
|
||||
from pydantic import Field as APIField
|
||||
|
@ -127,20 +127,26 @@ class SpecificLocationSchema(LocationSchema):
|
|||
|
||||
|
||||
class WithPolygonGeometrySchema(Schema):
|
||||
geometry: PolygonSchema = APIField(
|
||||
geometry: Optional[PolygonSchema] = APIField(
|
||||
None,
|
||||
title="geometry",
|
||||
description="can be null if not available or excluded from endpoint",
|
||||
)
|
||||
|
||||
|
||||
class WithLineStringGeometrySchema(Schema):
|
||||
geometry: LineStringSchema = APIField(
|
||||
geometry: Optional[LineStringSchema] = APIField(
|
||||
None,
|
||||
title="geometry",
|
||||
description="can be null if not available or excluded from endpoint",
|
||||
)
|
||||
|
||||
|
||||
class WithPointGeometrySchema(Schema):
|
||||
geometry: PointSchema = APIField(
|
||||
geometry: Optional[PointSchema] = APIField(
|
||||
None,
|
||||
title="geometry",
|
||||
description="can be null if not available or excluded from endpoint",
|
||||
)
|
||||
|
||||
|
||||
|
@ -177,3 +183,4 @@ class SimpleGeometryLocationsSchema(Schema):
|
|||
description="IDs of all locations that belong to this grouo",
|
||||
example=(1, 2, 3),
|
||||
)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from typing import Annotated, Literal, Optional, Union
|
||||
from typing import Annotated, ClassVar, Literal, Optional, Union
|
||||
|
||||
from ninja import Schema
|
||||
from pydantic import Discriminator
|
||||
from pydantic import Field as APIField
|
||||
from pydantic import GetJsonSchemaHandler, NonNegativeFloat, PositiveFloat, PositiveInt
|
||||
|
@ -315,7 +316,7 @@ class AccessRestrictionGroupSchema(WithAccessRestrictionSchema, DjangoModelSchem
|
|||
pass
|
||||
|
||||
|
||||
class TypedLevelSchema(LevelSchema):
|
||||
class FullLevelLocationSchema(LevelSchema):
|
||||
"""
|
||||
A level for the location API.
|
||||
See Level schema for details.
|
||||
|
@ -323,7 +324,7 @@ class TypedLevelSchema(LevelSchema):
|
|||
locationtype: Literal["level"]
|
||||
|
||||
|
||||
class TypedSpaceSchema(SimpleGeometryBoundsAndPointSchema, SpaceSchema):
|
||||
class FullSpaceLocationSchema(SimpleGeometryBoundsAndPointSchema, SpaceSchema):
|
||||
"""
|
||||
A space with some additional information for the location API.
|
||||
See Space schema for details.
|
||||
|
@ -331,7 +332,7 @@ class TypedSpaceSchema(SimpleGeometryBoundsAndPointSchema, SpaceSchema):
|
|||
locationtype: Literal["space"]
|
||||
|
||||
|
||||
class TypedAreaSchema(SimpleGeometryBoundsAndPointSchema, AreaSchema):
|
||||
class FullAreaLocationSchema(SimpleGeometryBoundsAndPointSchema, AreaSchema):
|
||||
"""
|
||||
An area with some additional information for the location API.
|
||||
See Area schema for details.
|
||||
|
@ -339,7 +340,7 @@ class TypedAreaSchema(SimpleGeometryBoundsAndPointSchema, AreaSchema):
|
|||
locationtype: Literal["area"]
|
||||
|
||||
|
||||
class TypedPOISchema(SimpleGeometryBoundsSchema, POISchema):
|
||||
class FullPOILocationSchema(SimpleGeometryBoundsSchema, POISchema):
|
||||
"""
|
||||
A point of interest with some additional information for the location API.
|
||||
See POI schema for details.
|
||||
|
@ -347,21 +348,92 @@ class TypedPOISchema(SimpleGeometryBoundsSchema, POISchema):
|
|||
locationtype: Literal["poi"]
|
||||
|
||||
|
||||
class TypedLocationGroupSchema(SimpleGeometryLocationsSchema, LocationGroupSchema):
|
||||
class FullLocationGroupLocationSchema(SimpleGeometryLocationsSchema, LocationGroupSchema):
|
||||
"""
|
||||
A locagroun group with some additional information for the location API.
|
||||
A location group with some additional information for the location API.
|
||||
See LocationGroup schema for details.
|
||||
"""
|
||||
locationtype: Literal["locationgroup"]
|
||||
|
||||
|
||||
AnyLocationSchema = Annotated[
|
||||
class SlimLocationMixin(Schema):
|
||||
level: ClassVar[None]
|
||||
space: ClassVar[None]
|
||||
titles: ClassVar[None]
|
||||
access_restriction: ClassVar[None]
|
||||
can_search: ClassVar[None]
|
||||
can_describe: ClassVar[None]
|
||||
groups: ClassVar[None]
|
||||
|
||||
|
||||
class SlimLevelLocationSchema(SlimLocationMixin, FullLevelLocationSchema):
|
||||
"""
|
||||
A level for the location API with some rarely needed fields removed.
|
||||
See Level schema for details.
|
||||
"""
|
||||
short_label: ClassVar[None]
|
||||
on_top_of: ClassVar[None]
|
||||
base_altitude: ClassVar[None]
|
||||
default_height: ClassVar[None]
|
||||
door_height: ClassVar[None]
|
||||
|
||||
|
||||
class SlimSpaceLocationSchema(SlimLocationMixin, FullSpaceLocationSchema):
|
||||
"""
|
||||
A space with some rarely needed fields removed and some additional information for the location API.
|
||||
See Space schema for details.
|
||||
"""
|
||||
outside: ClassVar[None]
|
||||
height: ClassVar[None]
|
||||
|
||||
|
||||
class SlimAreaLocationSchema(SlimLocationMixin, FullAreaLocationSchema):
|
||||
"""
|
||||
An area with some rarely needed fields removed and some additional information for the location API.
|
||||
See Area schema for details.
|
||||
"""
|
||||
slow_down_factor: ClassVar[None]
|
||||
|
||||
|
||||
class SlimPOILocationSchema(SlimLocationMixin, FullPOILocationSchema):
|
||||
"""
|
||||
A point of interest with some rarely needed fields removed and some additional information for the location API.
|
||||
See POI schema for details.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SlimLocationGroupLocationSchema(SlimLocationMixin, FullLocationGroupLocationSchema):
|
||||
"""
|
||||
A locagroun group with some rarely needed fields removed and some additional information for the location API.
|
||||
See LocationGroup schema for details.
|
||||
"""
|
||||
category: ClassVar[None]
|
||||
priority: ClassVar[None]
|
||||
hierarchy: ClassVar[None]
|
||||
color: ClassVar[None]
|
||||
can_report_missing: ClassVar[None]
|
||||
|
||||
|
||||
|
||||
FullLocationSchema = Annotated[
|
||||
Union[
|
||||
TypedLevelSchema,
|
||||
TypedSpaceSchema,
|
||||
TypedAreaSchema,
|
||||
TypedPOISchema,
|
||||
TypedLocationGroupSchema,
|
||||
FullLevelLocationSchema,
|
||||
FullSpaceLocationSchema,
|
||||
FullAreaLocationSchema,
|
||||
FullPOILocationSchema,
|
||||
FullLocationGroupLocationSchema,
|
||||
],
|
||||
Discriminator("locationtype"),
|
||||
]
|
||||
|
||||
SlimLocationSchema = Annotated[
|
||||
Union[
|
||||
SlimLevelLocationSchema,
|
||||
SlimSpaceLocationSchema,
|
||||
SlimAreaLocationSchema,
|
||||
SlimPOILocationSchema,
|
||||
SlimLocationGroupLocationSchema,
|
||||
],
|
||||
Discriminator("locationtype"),
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue