new serializer for all locations
This commit is contained in:
parent
38b4fbe1f0
commit
b47e97bb81
9 changed files with 87 additions and 45 deletions
|
@ -27,6 +27,8 @@ def make_serializable(values: Any):
|
|||
for key, val in values.items()
|
||||
}
|
||||
if isinstance(values, (list, tuple, set, frozenset)):
|
||||
if values and isinstance(next(iter(values)), Model):
|
||||
return type(values)(val.pk for val in values)
|
||||
return type(values)(make_serializable(val) for val in values)
|
||||
if isinstance(values, Promise):
|
||||
return str(values)
|
||||
|
|
|
@ -77,17 +77,17 @@ class LocationListFilters(BySearchableFilter, RemoveGeometryFilter):
|
|||
pass
|
||||
|
||||
|
||||
def _location_list(request, detailed: bool, filters: LocationListFilters):
|
||||
def _location_list(request, filters: LocationListFilters):
|
||||
if filters.searchable:
|
||||
locations = searchable_locations_for_request(request)
|
||||
else:
|
||||
locations = visible_locations_for_request(request).values()
|
||||
|
||||
result = [obj.serialize(detailed=detailed, search=filters.searchable,
|
||||
geometry=filters.geometry and can_access_geometry(request, obj),
|
||||
simple_geometry=True)
|
||||
for obj in locations]
|
||||
return result
|
||||
for location in locations:
|
||||
if not filters.geometry or not can_access_geometry(request, location):
|
||||
location._hide_geometry = True
|
||||
|
||||
return locations
|
||||
|
||||
|
||||
@map_api_router.get('/locations/', summary="list locations (slim)",
|
||||
|
@ -96,7 +96,7 @@ def _location_list(request, detailed: bool, filters: LocationListFilters):
|
|||
response={200: list[SlimListableLocationSchema], **validate_responses, **auth_responses})
|
||||
@api_etag(base_mapdata=True)
|
||||
def location_list(request, filters: Query[LocationListFilters]):
|
||||
return _location_list(request, detailed=False, filters=filters)
|
||||
return _location_list(request, filters=filters)
|
||||
|
||||
|
||||
@map_api_router.get('/locations/full/', summary="list locations (full)",
|
||||
|
@ -105,7 +105,7 @@ def location_list(request, filters: Query[LocationListFilters]):
|
|||
response={200: list[FullListableLocationSchema], **validate_responses, **auth_responses})
|
||||
@api_etag(base_mapdata=True)
|
||||
def location_list_full(request, filters: Query[LocationListFilters]):
|
||||
return _location_list(request, detailed=True, filters=filters)
|
||||
return _location_list(request, filters=filters)
|
||||
|
||||
|
||||
def _location_retrieve(request, location, detailed: bool, geometry: bool, show_redirects: bool):
|
||||
|
@ -120,11 +120,10 @@ def _location_retrieve(request, location, detailed: bool, geometry: bool, show_r
|
|||
request._target_etag = None
|
||||
request._target_cache_key = None
|
||||
|
||||
return location.serialize(
|
||||
detailed=detailed,
|
||||
geometry=geometry and can_access_geometry(request, location),
|
||||
simple_geometry=True
|
||||
)
|
||||
if not geometry or not can_access_geometry(request, location):
|
||||
location._hide_geometry = True
|
||||
|
||||
return location
|
||||
|
||||
|
||||
def _location_display(request, location):
|
||||
|
|
|
@ -47,9 +47,13 @@ def mapdata_list_endpoint(request,
|
|||
# order_by
|
||||
qs = qs.order_by(*order_by)
|
||||
|
||||
# todo: can access geometry… using defer?
|
||||
result = list(qs)
|
||||
|
||||
return qs
|
||||
for obj in result:
|
||||
if can_access_geometry(request, obj):
|
||||
obj._hide_geometry = True
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def mapdata_retrieve_endpoint(request, model: Type[Model], **lookups):
|
||||
|
|
|
@ -309,8 +309,10 @@ class LineObstacle(SpaceGeometryMixin, models.Model):
|
|||
|
||||
class POI(SpaceGeometryMixin, SpecificLocation, models.Model):
|
||||
"""
|
||||
An point of interest
|
||||
A point of interest
|
||||
"""
|
||||
new_serialize = True
|
||||
|
||||
geometry = GeometryField('point')
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -385,6 +385,8 @@ class LocationGroup(Location, models.Model):
|
|||
self.orig_category_id = self.category_id
|
||||
self.orig_color = self.color
|
||||
|
||||
locations = []
|
||||
|
||||
def details_display(self, editor_url=True, **kwargs):
|
||||
result = super().details_display(**kwargs)
|
||||
result['display'].insert(3, (_('Category'), self.category.title))
|
||||
|
@ -522,6 +524,8 @@ class CustomLocationProxyMixin:
|
|||
|
||||
|
||||
class DynamicLocation(CustomLocationProxyMixin, SpecificLocation, models.Model):
|
||||
new_serialize = True
|
||||
|
||||
position_secret = models.CharField(_('position secret'), max_length=32, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -176,8 +176,7 @@ class RemoveGeometryFilter(FilterSchema):
|
|||
# todo: validated true as invalid if not avaiilable for this model
|
||||
|
||||
def filter_qs(self, request, qs: QuerySet) -> QuerySet:
|
||||
if ((qs.model in (Building, Space, Door) and not request.user_permissions.can_access_base_mapdata)
|
||||
or not self.geometry):
|
||||
if not self.geometry:
|
||||
qs = qs.defer('geometry')
|
||||
return super().filter_qs(request, qs)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import math
|
||||
import re
|
||||
from typing import Annotated, Optional, Union
|
||||
from typing import Annotated, Optional, Union, Literal
|
||||
|
||||
from pydantic import Field as APIField
|
||||
from pydantic import PositiveInt
|
||||
|
@ -119,6 +119,12 @@ class LocationSchema(WithAccessRestrictionSchema, TitledSchema, LocationSlugSche
|
|||
example="more search terms",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_overrides(cls, value) -> dict:
|
||||
return {
|
||||
"locationtype": value._meta.model_name,
|
||||
}
|
||||
|
||||
|
||||
class LabelSettingsSchema(DjangoModelSchema): # todo: add titles back in here
|
||||
"""
|
||||
|
@ -145,6 +151,7 @@ class LabelSettingsSchema(DjangoModelSchema): # todo: add titles back in here
|
|||
class SpecificLocationSchema(LocationSchema):
|
||||
grid_square: Union[
|
||||
Annotated[NonEmptyStr, APIField(title="grid square", description="grid square(s) that this location is in")],
|
||||
Annotated[Literal[""], APIField(title="grid square", description="outside of grid")],
|
||||
Annotated[None, APIField(title="null", description="no grid defined or outside of grid")],
|
||||
] = APIField(
|
||||
default=None,
|
||||
|
@ -235,7 +242,10 @@ class WithGeometrySchema(BaseSchema):
|
|||
minx, miny, maxx, maxy = value.geometry.bounds
|
||||
return {
|
||||
**super().get_overrides(value),
|
||||
"geometry": format_geojson(smart_mapping(value.geometry), rounded=False),
|
||||
"geometry": (
|
||||
format_geojson(smart_mapping(value.geometry), rounded=False)
|
||||
if not getattr(value, '_hide_geometry', False) else None
|
||||
),
|
||||
"point": (value.level_id,) + tuple(round(i, 2) for i in value.point.coords[0]),
|
||||
"bounds": ((int(math.floor(minx)), int(math.floor(miny))),
|
||||
(int(math.ceil(maxx)), int(math.ceil(maxy))))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Annotated, ClassVar, Literal, Optional, Union
|
||||
from typing import Annotated, ClassVar, Literal, Optional, Union, Any
|
||||
|
||||
from pydantic import Discriminator
|
||||
from django.db.models import Model
|
||||
from pydantic import Discriminator, Tag
|
||||
from pydantic import Field as APIField
|
||||
from pydantic import NonNegativeFloat, PositiveFloat, PositiveInt
|
||||
|
||||
|
@ -415,6 +416,7 @@ class CustomLocationSchema(BaseSchema):
|
|||
)
|
||||
grid_square: Union[
|
||||
Annotated[NonEmptyStr, APIField(title="grid square", description="grid square(s) that this location is in")],
|
||||
Annotated[Literal[""], APIField(title="grid square", description="outside of grid")],
|
||||
Annotated[None, APIField(title="null", description="no grid defined or outside of grid")],
|
||||
] = APIField(
|
||||
default=None,
|
||||
|
@ -569,6 +571,8 @@ class SlimLocationMixin(BaseSchema):
|
|||
can_describe: ClassVar[None]
|
||||
groups: ClassVar[None]
|
||||
groups_by_category: ClassVar[None]
|
||||
geometry: ClassVar[None]
|
||||
point: ClassVar[None]
|
||||
|
||||
|
||||
class SlimLevelLocationSchema(SlimLocationMixin, FullLevelLocationSchema):
|
||||
|
@ -628,46 +632,62 @@ class SlimDynamicLocationLocationSchema(SlimLocationMixin, FullDynamicLocationLo
|
|||
pass
|
||||
|
||||
|
||||
def get_locationtype(v: Any):
|
||||
if isinstance(v, Model):
|
||||
return v._meta.model_name
|
||||
return v["locationtype"]
|
||||
|
||||
|
||||
FullListableLocationSchema = Annotated[
|
||||
Union[
|
||||
FullLevelLocationSchema,
|
||||
FullSpaceLocationSchema,
|
||||
FullAreaLocationSchema,
|
||||
FullPOILocationSchema,
|
||||
FullLocationGroupLocationSchema,
|
||||
FullDynamicLocationLocationSchema,
|
||||
Annotated[FullLevelLocationSchema, Tag("level")],
|
||||
Annotated[FullSpaceLocationSchema, Tag("space")],
|
||||
Annotated[FullAreaLocationSchema, Tag("area")],
|
||||
Annotated[FullPOILocationSchema, Tag("poi")],
|
||||
Annotated[FullLocationGroupLocationSchema, Tag("locationgroup")],
|
||||
Annotated[FullDynamicLocationLocationSchema, Tag("dynamiclocation")],
|
||||
],
|
||||
Discriminator("locationtype"),
|
||||
Discriminator(get_locationtype),
|
||||
]
|
||||
|
||||
FullLocationSchema = Annotated[
|
||||
Union[
|
||||
FullListableLocationSchema,
|
||||
CustomLocationLocationSchema,
|
||||
TrackablePositionLocationSchema,
|
||||
Annotated[FullLevelLocationSchema, Tag("level")],
|
||||
Annotated[FullSpaceLocationSchema, Tag("space")],
|
||||
Annotated[FullAreaLocationSchema, Tag("area")],
|
||||
Annotated[FullPOILocationSchema, Tag("poi")],
|
||||
Annotated[FullLocationGroupLocationSchema, Tag("locationgroup")],
|
||||
Annotated[FullDynamicLocationLocationSchema, Tag("dynamiclocation")],
|
||||
Annotated[CustomLocationLocationSchema, Tag("customlocation")],
|
||||
Annotated[TrackablePositionLocationSchema, Tag("position")],
|
||||
],
|
||||
Discriminator("locationtype"),
|
||||
Discriminator(get_locationtype),
|
||||
]
|
||||
|
||||
SlimListableLocationSchema = Annotated[
|
||||
Union[
|
||||
SlimLevelLocationSchema,
|
||||
SlimSpaceLocationSchema,
|
||||
SlimAreaLocationSchema,
|
||||
SlimPOILocationSchema,
|
||||
SlimLocationGroupLocationSchema,
|
||||
SlimDynamicLocationLocationSchema,
|
||||
Annotated[SlimLevelLocationSchema, Tag("level")],
|
||||
Annotated[SlimSpaceLocationSchema, Tag("space")],
|
||||
Annotated[SlimAreaLocationSchema, Tag("area")],
|
||||
Annotated[SlimPOILocationSchema, Tag("poi")],
|
||||
Annotated[SlimLocationGroupLocationSchema, Tag("locationgroup")],
|
||||
Annotated[SlimDynamicLocationLocationSchema, Tag("dynamiclocation")],
|
||||
],
|
||||
Discriminator("locationtype"),
|
||||
Discriminator(get_locationtype),
|
||||
]
|
||||
|
||||
SlimLocationSchema = Annotated[
|
||||
Union[
|
||||
SlimListableLocationSchema,
|
||||
CustomLocationLocationSchema,
|
||||
TrackablePositionLocationSchema,
|
||||
Annotated[SlimLevelLocationSchema, Tag("level")],
|
||||
Annotated[SlimSpaceLocationSchema, Tag("space")],
|
||||
Annotated[SlimAreaLocationSchema, Tag("area")],
|
||||
Annotated[SlimPOILocationSchema, Tag("poi")],
|
||||
Annotated[SlimLocationGroupLocationSchema, Tag("locationgroup")],
|
||||
Annotated[SlimDynamicLocationLocationSchema, Tag("dynamiclocation")],
|
||||
Annotated[CustomLocationLocationSchema, Tag("customlocation")],
|
||||
Annotated[TrackablePositionLocationSchema, Tag("position")],
|
||||
],
|
||||
Discriminator("locationtype"),
|
||||
Discriminator(get_locationtype),
|
||||
]
|
||||
|
||||
listable_location_definitions = schema_definitions(
|
||||
|
|
|
@ -5,7 +5,7 @@ from collections import OrderedDict
|
|||
from dataclasses import dataclass, field
|
||||
from functools import reduce
|
||||
from itertools import chain
|
||||
from typing import Any, List, Mapping, Optional, Union
|
||||
from typing import Any, List, Mapping, Optional, Union, ClassVar
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
|
@ -275,6 +275,8 @@ def get_custom_location_for_request(slug: str, request):
|
|||
|
||||
@dataclass
|
||||
class CustomLocation:
|
||||
locationtype: ClassVar = "customlocation"
|
||||
|
||||
can_search = True
|
||||
can_describe = True
|
||||
access_restriction_id = None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue