first locations endpoint might… be… something?

This commit is contained in:
Laura Klünder 2023-11-23 16:37:25 +01:00
parent d84226dc5c
commit 8b47a89865
4 changed files with 132 additions and 3 deletions

View file

@ -82,6 +82,7 @@ class LocationSlug(SerializableMixin, models.Model):
def _serialize(self, **kwargs):
result = super()._serialize(**kwargs)
result["locationtype"] = self.__class__.__name__.lower()
result['slug'] = self.get_slug()
return result

View file

@ -1,8 +1,15 @@
from django.core.cache import cache
from ninja import Query
from ninja import Router as APIRouter
from ninja import Schema
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.responses import BoundsSchema
from c3nav.mapdata.utils.locations import searchable_locations_for_request, visible_locations_for_request
map_api_router = APIRouter(tags=["map"])
@ -13,3 +20,43 @@ def bounds(request):
return {
"bounds": Source.max_bounds(),
}
class LocationEndpointParameters(Schema):
searchable: bool = APIField(
False,
title='only list searchable locations',
description='if set, only searchable locations will be listed'
)
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
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(detailed=detailed, search=searchable,
geometry=geometry and can_access_geometry(request),
simple_geometry=True)
for obj in locations)
cache.set(cache_key, result, 300)
return result

View file

@ -15,7 +15,8 @@ class SerializableSchema(Schema):
@classmethod
def _run_root_validator(cls, values: Any, handler: ModelWrapValidatorHandler[Schema], info: ValidationInfo) -> Any:
""" overwriting this, we need to call serialize to get the correct data """
values = values.serialize()
if not isinstance(values, dict):
values = values.serialize()
return handler(values)
@ -155,3 +156,24 @@ class WithSpaceSchema(SerializableSchema):
title="space",
description="space id this object belongs to.",
)
class SimpleGeometryBoundsSchema(Schema):
bounds: tuple[tuple[float, float], tuple[float, float]] = APIField(
description="location bounding box from (x, y) to (x, y)",
example=((-10, -20), (20, 30)),
)
class SimpleGeometryBoundsAndPointSchema(SimpleGeometryBoundsSchema):
point: tuple[PositiveInt, float, float] = APIField(
title="point representation of the location",
description="made from the level ID, X and Y in this order",
)
class SimpleGeometryLocationsSchema(Schema):
locations: list[PositiveInt] = APIField( # todo: this should be a set… but json serialization?
description="IDs of all locations that belong to this grouo",
example=(1, 2, 3),
)

View file

@ -1,10 +1,15 @@
from typing import Optional
from typing import Annotated, Literal, Optional, Union
from pydantic import Discriminator
from pydantic import Field as APIField
from pydantic import NonNegativeFloat, PositiveFloat, PositiveInt
from pydantic import GetJsonSchemaHandler, NonNegativeFloat, PositiveFloat, PositiveInt
from pydantic.json_schema import JsonSchemaValue
from pydantic_core import CoreSchema
from c3nav.api.utils import NonEmptyStr
from c3nav.mapdata.schemas.model_base import (DjangoModelSchema, LabelSettingsSchema, LocationSchema,
LocationSlugSchema, SimpleGeometryBoundsAndPointSchema,
SimpleGeometryBoundsSchema, SimpleGeometryLocationsSchema,
SpecificLocationSchema, TitledSchema, WithAccessRestrictionSchema,
WithLevelSchema, WithLineStringGeometrySchema, WithPointGeometrySchema,
WithPolygonGeometrySchema, WithSpaceSchema)
@ -308,3 +313,57 @@ class AccessRestrictionGroupSchema(WithAccessRestrictionSchema, DjangoModelSchem
For simplicity's sake, access restrictions can belong to groups, and you can grant permissions for the entire group.
"""
pass
class TypedLevelSchema(LevelSchema):
"""
A level for the location API.
See Level schema for details.
"""
locationtype: Literal["level"]
class TypedSpaceSchema(SimpleGeometryBoundsAndPointSchema, SpaceSchema):
"""
A space with some additional information for the location API.
See Space schema for details.
"""
locationtype: Literal["space"]
class TypedAreaSchema(SimpleGeometryBoundsAndPointSchema, AreaSchema):
"""
An area with some additional information for the location API.
See Area schema for details.
"""
locationtype: Literal["area"]
class TypedPOISchema(SimpleGeometryBoundsSchema, POISchema):
"""
A point of interest with some additional information for the location API.
See POI schema for details.
"""
locationtype: Literal["poi"]
class TypedLocationGroupSchema(SimpleGeometryLocationsSchema, LocationGroupSchema):
"""
A locagroun group with some additional information for the location API.
See LocationGroup schema for details.
"""
locationtype: Literal["locationgroup"]
AnyLocationSchema = Annotated[
Union[
TypedLevelSchema,
TypedSpaceSchema,
TypedAreaSchema,
TypedPOISchema,
TypedLocationGroupSchema,
],
Discriminator("locationtype"),
]