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