enjoy an awesome broken display viewpoint
This commit is contained in:
parent
389e9ee52b
commit
505a59091c
4 changed files with 170 additions and 12 deletions
|
@ -1,6 +1,7 @@
|
|||
from typing import Literal
|
||||
from typing import Annotated, Literal, Union
|
||||
|
||||
from ninja import Schema
|
||||
from pydantic import Discriminator
|
||||
from pydantic import Field as APIField
|
||||
|
||||
from c3nav.api.utils import NonEmptyStr
|
||||
|
@ -43,3 +44,13 @@ class PointSchema(Schema):
|
|||
coordinates: tuple[float, float] = APIField(
|
||||
example=[1, 2.5]
|
||||
)
|
||||
|
||||
|
||||
GeometrySchema = Annotated[
|
||||
Union[
|
||||
PolygonSchema,
|
||||
LineStringSchema,
|
||||
PointSchema,],
|
||||
Discriminator("type"),
|
||||
]
|
||||
|
||||
|
|
|
@ -1,16 +1,27 @@
|
|||
import json
|
||||
from typing import Annotated, Union
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.shortcuts import redirect
|
||||
from ninja import Query
|
||||
from ninja import Router as APIRouter
|
||||
from ninja import Schema
|
||||
from pydantic import Field as APIField
|
||||
from pydantic import PositiveInt
|
||||
|
||||
from c3nav.api.newauth import auth_responses
|
||||
from c3nav.api.exceptions import API404
|
||||
from c3nav.api.newauth import auth_responses, validate_responses
|
||||
from c3nav.mapdata.models import Source
|
||||
from c3nav.mapdata.models.access import AccessPermission
|
||||
from c3nav.mapdata.models.locations import LocationRedirect
|
||||
from c3nav.mapdata.schemas.filters import BySearchableFilter, RemoveGeometryFilter
|
||||
from c3nav.mapdata.schemas.models import FullLocationSchema, SlimLocationSchema
|
||||
from c3nav.mapdata.schemas.model_base import LocationID
|
||||
from c3nav.mapdata.schemas.models import FullLocationSchema, LocationDisplay, SlimLocationSchema
|
||||
from c3nav.mapdata.schemas.responses import BoundsSchema
|
||||
from c3nav.mapdata.utils.locations import searchable_locations_for_request, visible_locations_for_request
|
||||
from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, searchable_locations_for_request,
|
||||
visible_locations_for_request)
|
||||
from c3nav.mapdata.utils.user import can_access_editor
|
||||
|
||||
map_api_router = APIRouter(tags=["map"])
|
||||
|
||||
|
@ -61,13 +72,97 @@ def _location_list(request, detailed: bool, filters: LocationListFilters):
|
|||
return result
|
||||
|
||||
|
||||
@map_api_router.get('/locations/', response={200: list[SlimLocationSchema], **auth_responses},
|
||||
@map_api_router.get('/locations/',
|
||||
response={200: list[SlimLocationSchema], **validate_responses, **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},
|
||||
@map_api_router.get('/locations/full/',
|
||||
response={200: list[FullLocationSchema], **validate_responses, **auth_responses},
|
||||
summary="Get locations (with all attributes)")
|
||||
def location_list_full(request, filters: Query[LocationListFilters]):
|
||||
return _location_list(request, detailed=True, filters=filters)
|
||||
|
||||
|
||||
def _location_retrieve(request, location, detailed: bool, geometry: bool, show_redirects: bool):
|
||||
# todo: cache, visibility, etc…
|
||||
|
||||
if location is None:
|
||||
raise API404
|
||||
|
||||
if isinstance(location, LocationRedirect):
|
||||
if not show_redirects:
|
||||
return redirect('../' + str(location.target.slug)) # todo: use reverse, make pk and slug both work
|
||||
|
||||
return location.serialize(
|
||||
detailed=detailed,
|
||||
geometry=geometry and can_access_geometry(request),
|
||||
simple_geometry=True
|
||||
)
|
||||
|
||||
|
||||
def _location_display(request, location):
|
||||
# todo: cache, visibility, etc…
|
||||
|
||||
if location is None:
|
||||
raise API404
|
||||
|
||||
if isinstance(location, LocationRedirect):
|
||||
return redirect('../' + str(location.target.slug) + '/details/') # todo: use reverse, make pk+slug work
|
||||
|
||||
result = location.details_display(
|
||||
detailed_geometry=can_access_geometry(request),
|
||||
editor_url=can_access_editor(request)
|
||||
)
|
||||
from pprint import pprint
|
||||
pprint(result)
|
||||
return json.loads(json.dumps(result, cls=DjangoJSONEncoder)) # todo: wtf?? well we need to get rid of lazy strings
|
||||
|
||||
|
||||
class ShowRedirects(Schema):
|
||||
show_redirects: bool = APIField(
|
||||
False,
|
||||
name="show redirects",
|
||||
description="whether to show redirects instead of sending a redirect response",
|
||||
)
|
||||
|
||||
|
||||
@map_api_router.get('/locations/{location_id}/',
|
||||
response={200: SlimLocationSchema, **API404.dict(), **validate_responses, **auth_responses},
|
||||
summary="Get location by ID (with most important attributes)",
|
||||
description="a numeric ID for a map location or a string ID for generated locations can be used")
|
||||
def location_by_id(request, location_id: LocationID, filters: Query[RemoveGeometryFilter],
|
||||
redirects: Query[ShowRedirects]):
|
||||
return _location_retrieve(
|
||||
request,
|
||||
get_location_by_id_for_request(location_id, request),
|
||||
detailed=False, geometry=filters.geometry, show_redirects=redirects.show_redirects,
|
||||
)
|
||||
|
||||
|
||||
@map_api_router.get('/locations/{location_id}/full/',
|
||||
response={200: FullLocationSchema, **API404.dict(), **validate_responses, **auth_responses},
|
||||
summary="Get location by ID (with all attributes)",
|
||||
description="a numeric ID for a map location or a string ID for generated locations can be used")
|
||||
def location_by_id_full(request, location_id: LocationID, filters: Query[RemoveGeometryFilter],
|
||||
redirects: Query[ShowRedirects]):
|
||||
return _location_retrieve(
|
||||
request,
|
||||
get_location_by_id_for_request(location_id, request),
|
||||
detailed=True, geometry=filters.geometry, show_redirects=redirects.show_redirects,
|
||||
)
|
||||
|
||||
|
||||
@map_api_router.get('/locations/{location_id}/display/',
|
||||
response={200: LocationDisplay, **API404.dict(), **auth_responses},
|
||||
summary="Get location display data by ID",
|
||||
description="a numeric ID for a map location or a string ID for generated locations can be used")
|
||||
def location_by_id_display(request, location_id: LocationID):
|
||||
return _location_display(
|
||||
request,
|
||||
get_location_by_id_for_request(location_id, request),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Annotated, Any, ClassVar, Optional
|
||||
from typing import Annotated, Any, ClassVar, Optional, Union
|
||||
|
||||
from ninja import Schema
|
||||
from pydantic import Field as APIField
|
||||
|
@ -189,3 +189,14 @@ class SimpleGeometryLocationsSchema(Schema):
|
|||
example=(1, 2, 3),
|
||||
)
|
||||
|
||||
|
||||
LocationID = Union[
|
||||
Annotated[int, APIField(title="location ID",
|
||||
description="numeric ID of any lcation")],
|
||||
Annotated[str, APIField(title="custom location ID",
|
||||
pattern=r"c:[a-z0-9-_]+:(-?\d+(\.\d+)?):(-?\d+(\.\d+)?)$",
|
||||
description="level short_name and x/y coordinates form the ID of a custom location")],
|
||||
Annotated[str, APIField(title="position ID",
|
||||
pattern=r"p:[a-z0-9]+$",
|
||||
description="the ID of a user-defined tracked position is made up of its secret")],
|
||||
]
|
||||
|
|
|
@ -7,12 +7,14 @@ from pydantic import GetJsonSchemaHandler, NonNegativeFloat, PositiveFloat, Posi
|
|||
from pydantic.json_schema import JsonSchemaValue
|
||||
from pydantic_core import CoreSchema
|
||||
|
||||
from c3nav.api.schema import GeometrySchema
|
||||
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,
|
||||
from c3nav.mapdata.schemas.model_base import (DjangoModelSchema, LabelSettingsSchema, LocationID, LocationSchema,
|
||||
LocationSlugSchema, SerializableSchema,
|
||||
SimpleGeometryBoundsAndPointSchema, SimpleGeometryBoundsSchema,
|
||||
SimpleGeometryLocationsSchema, SpecificLocationSchema, TitledSchema,
|
||||
WithAccessRestrictionSchema, WithLevelSchema,
|
||||
WithLineStringGeometrySchema, WithPointGeometrySchema,
|
||||
WithPolygonGeometrySchema, WithSpaceSchema)
|
||||
|
||||
|
||||
|
@ -439,3 +441,42 @@ SlimLocationSchema = Annotated[
|
|||
]
|
||||
|
||||
|
||||
class DisplayLink(Schema):
|
||||
"""
|
||||
A link for the location display
|
||||
"""
|
||||
id: PositiveInt
|
||||
slug: NonEmptyStr
|
||||
title: NonEmptyStr
|
||||
can_search: bool
|
||||
|
||||
|
||||
class LocationDisplay(SerializableSchema):
|
||||
id: LocationID = APIField(
|
||||
description="a numeric ID for a map location or a string ID for generated locations",
|
||||
)
|
||||
level: Optional[PositiveInt] = APIField(
|
||||
None,
|
||||
description="level ID, if applicable"
|
||||
)
|
||||
space: Optional[PositiveInt] = APIField(
|
||||
None,
|
||||
description="space ID, if applicable"
|
||||
)
|
||||
display: list[
|
||||
tuple[
|
||||
Annotated[NonEmptyStr, APIField(name="field title")],
|
||||
Annotated[Union[
|
||||
Annotated[str, APIField(name="a simple string value")],
|
||||
Annotated[DisplayLink, APIField(namen="a link value")],
|
||||
Annotated[list[DisplayLink], APIField(name="a list of link values")],
|
||||
Annotated[Literal[None], APIField(name="no value")]
|
||||
], APIField(name="field value")]
|
||||
]
|
||||
] = APIField(description="a list of human-readable display values")
|
||||
geometry: Optional[GeometrySchema] = APIField(
|
||||
None, description="representative geometry, if available"
|
||||
)
|
||||
editor_url: Optional[NonEmptyStr] = APIField(
|
||||
None, description="path to edit this object in the editor, if the user has access to it",
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue