more map api documentation improvements
This commit is contained in:
parent
4d57b81ad1
commit
964574e535
8 changed files with 335 additions and 121 deletions
|
@ -22,8 +22,7 @@ class SwaggerAndRedoc(DocsBase):
|
||||||
redoc_config = Redoc(settings={
|
redoc_config = Redoc(settings={
|
||||||
"hideOneOfDescription": True,
|
"hideOneOfDescription": True,
|
||||||
"expandSingleSchemaField": True,
|
"expandSingleSchemaField": True,
|
||||||
"jsonSampleExpandLevel": 4,
|
"jsonSampleExpandLevel": 5,
|
||||||
"showObjectSchemaExamples": True,
|
|
||||||
"expandResponses": "200",
|
"expandResponses": "200",
|
||||||
"hideSingleRequestSampleTab": True,
|
"hideSingleRequestSampleTab": True,
|
||||||
"nativeScrollbars": True,
|
"nativeScrollbars": True,
|
||||||
|
|
|
@ -25,6 +25,9 @@ class PolygonSchema(Schema):
|
||||||
example=[[[1.5, 1.5], [1.5, 2.5], [2.5, 2.5], [2.5, 2.5]]]
|
example=[[[1.5, 1.5], [1.5, 2.5], [2.5, 2.5], [2.5, 2.5]]]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Config(Schema.Config):
|
||||||
|
title = "GeoJSON Polygon"
|
||||||
|
|
||||||
|
|
||||||
class LineStringSchema(Schema):
|
class LineStringSchema(Schema):
|
||||||
"""
|
"""
|
||||||
|
@ -35,6 +38,9 @@ class LineStringSchema(Schema):
|
||||||
example=[[1.5, 1.5], [2.5, 2.5], [5, 8.7]]
|
example=[[1.5, 1.5], [2.5, 2.5], [5, 8.7]]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Config(Schema.Config):
|
||||||
|
title = "GeoJSON LineString"
|
||||||
|
|
||||||
|
|
||||||
class LineSchema(Schema):
|
class LineSchema(Schema):
|
||||||
"""
|
"""
|
||||||
|
@ -45,6 +51,9 @@ class LineSchema(Schema):
|
||||||
example=[[1.5, 1.5], [5, 8.7]]
|
example=[[1.5, 1.5], [5, 8.7]]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Config(Schema.Config):
|
||||||
|
title = "GeoJSON LineString (only two points)"
|
||||||
|
|
||||||
|
|
||||||
class PointSchema(Schema):
|
class PointSchema(Schema):
|
||||||
"""
|
"""
|
||||||
|
@ -55,6 +64,9 @@ class PointSchema(Schema):
|
||||||
example=[1, 2.5]
|
example=[1, 2.5]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Config(Schema.Config):
|
||||||
|
title = "GeoJSON Point"
|
||||||
|
|
||||||
|
|
||||||
GeometrySchema = Annotated[
|
GeometrySchema = Annotated[
|
||||||
Union[
|
Union[
|
||||||
|
|
|
@ -10,14 +10,14 @@ from c3nav.editor.api.schemas import EditorGeometriesElemSchema, EditorID, Geome
|
||||||
from c3nav.editor.views.base import editor_etag_func
|
from c3nav.editor.views.base import editor_etag_func
|
||||||
from c3nav.mapdata.api.base import api_etag
|
from c3nav.mapdata.api.base import api_etag
|
||||||
from c3nav.mapdata.models import Source
|
from c3nav.mapdata.models import Source
|
||||||
from c3nav.mapdata.schemas.responses import BoundsSchema
|
from c3nav.mapdata.schemas.responses import WithBoundsSchema
|
||||||
|
|
||||||
editor_api_router = APIRouter(tags=["editor"], auth=APITokenAuth(permissions={"editor_access"}))
|
editor_api_router = APIRouter(tags=["editor"], auth=APITokenAuth(permissions={"editor_access"}))
|
||||||
|
|
||||||
|
|
||||||
@editor_api_router.get('/bounds/', summary="boundaries",
|
@editor_api_router.get('/bounds/', summary="boundaries",
|
||||||
description="get maximum boundaries of everything on the map",
|
description="get maximum boundaries of everything on the map",
|
||||||
response={200: BoundsSchema, **auth_permission_responses},
|
response={200: WithBoundsSchema, **auth_permission_responses},
|
||||||
openapi_extra={"security": [{"APITokenAuth": ["editor_access"]}]})
|
openapi_extra={"security": [{"APITokenAuth": ["editor_access"]}]})
|
||||||
@api_etag()
|
@api_etag()
|
||||||
def bounds(request):
|
def bounds(request):
|
||||||
|
|
|
@ -17,10 +17,11 @@ from c3nav.mapdata.api.base import api_etag, api_stats
|
||||||
from c3nav.mapdata.models import Source
|
from c3nav.mapdata.models import Source
|
||||||
from c3nav.mapdata.models.locations import DynamicLocation, LocationRedirect, Position
|
from c3nav.mapdata.models.locations import DynamicLocation, LocationRedirect, Position
|
||||||
from c3nav.mapdata.schemas.filters import BySearchableFilter, RemoveGeometryFilter
|
from c3nav.mapdata.schemas.filters import BySearchableFilter, RemoveGeometryFilter
|
||||||
from c3nav.mapdata.schemas.model_base import AnyLocationID, AnyPositionID, CustomLocationID
|
from c3nav.mapdata.schemas.model_base import AnyLocationID, AnyPositionID, CustomLocationID, schema_definition
|
||||||
from c3nav.mapdata.schemas.models import (AnyPositionStatusSchema, FullListableLocationSchema, FullLocationSchema,
|
from c3nav.mapdata.schemas.models import (AnyPositionStatusSchema, FullListableLocationSchema, FullLocationSchema,
|
||||||
LocationDisplay, SlimListableLocationSchema, SlimLocationSchema)
|
LevelSchema, LocationDisplay, SlimListableLocationSchema, SlimLocationSchema,
|
||||||
from c3nav.mapdata.schemas.responses import BoundsSchema, LocationGeometry
|
all_location_definitions, listable_location_definitions)
|
||||||
|
from c3nav.mapdata.schemas.responses import LocationGeometry, WithBoundsSchema
|
||||||
from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, get_location_by_slug_for_request,
|
from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, get_location_by_slug_for_request,
|
||||||
searchable_locations_for_request, visible_locations_for_request)
|
searchable_locations_for_request, visible_locations_for_request)
|
||||||
from c3nav.mapdata.utils.user import can_access_editor
|
from c3nav.mapdata.utils.user import can_access_editor
|
||||||
|
@ -30,7 +31,7 @@ map_api_router = APIRouter(tags=["map"])
|
||||||
|
|
||||||
@map_api_router.get('/bounds/', summary="get boundaries",
|
@map_api_router.get('/bounds/', summary="get boundaries",
|
||||||
description="get maximum boundaries of everything on the map",
|
description="get maximum boundaries of everything on the map",
|
||||||
response={200: BoundsSchema, **auth_responses})
|
response={200: WithBoundsSchema, **auth_responses})
|
||||||
@api_etag(permissions=False)
|
@api_etag(permissions=False)
|
||||||
def bounds(request):
|
def bounds(request):
|
||||||
return {
|
return {
|
||||||
|
@ -69,7 +70,8 @@ def _location_list(request, detailed: bool, filters: LocationListFilters):
|
||||||
|
|
||||||
|
|
||||||
@map_api_router.get('/locations/', summary="list locations (slim)",
|
@map_api_router.get('/locations/', summary="list locations (slim)",
|
||||||
description="Get locations (with most important attributes set)",
|
description=("Get locations (with most important attributes set)\n\n"
|
||||||
|
"Possible location types:\n"+listable_location_definitions),
|
||||||
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]):
|
||||||
|
@ -77,7 +79,8 @@ def location_list(request, filters: Query[LocationListFilters]):
|
||||||
|
|
||||||
|
|
||||||
@map_api_router.get('/locations/full/', summary="list locations (full)",
|
@map_api_router.get('/locations/full/', summary="list locations (full)",
|
||||||
description="Get locations (with all attributes set)",
|
description=("Get locations (with all attributes set)\n\n"
|
||||||
|
"Possible location types:\n"+listable_location_definitions),
|
||||||
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]):
|
||||||
|
@ -150,7 +153,8 @@ class ShowRedirects(Schema):
|
||||||
|
|
||||||
|
|
||||||
@map_api_router.get('/locations/{location_id}/', summary="location by ID (slim)",
|
@map_api_router.get('/locations/{location_id}/', summary="location by ID (slim)",
|
||||||
description="Get locations by ID (with all attributes set)",
|
description=("Get locations by ID (with all attributes set)\n\n"
|
||||||
|
"Possible location types:\n"+all_location_definitions),
|
||||||
response={200: SlimLocationSchema, **API404.dict(), **validate_responses, **auth_responses})
|
response={200: SlimLocationSchema, **API404.dict(), **validate_responses, **auth_responses})
|
||||||
@api_stats('location_get')
|
@api_stats('location_get')
|
||||||
@api_etag(base_mapdata=True)
|
@api_etag(base_mapdata=True)
|
||||||
|
@ -164,7 +168,8 @@ def location_by_id(request, location_id: AnyLocationID, filters: Query[RemoveGeo
|
||||||
|
|
||||||
|
|
||||||
@map_api_router.get('/locations/{location_id}/full/', summary="location by ID (full)",
|
@map_api_router.get('/locations/{location_id}/full/', summary="location by ID (full)",
|
||||||
description="Get location by ID (with all attributes set)",
|
description=("Get location by ID (with all attributes set)\n\n"
|
||||||
|
"Possible location types:\n"+all_location_definitions),
|
||||||
response={200: FullLocationSchema, **API404.dict(), **validate_responses, **auth_responses})
|
response={200: FullLocationSchema, **API404.dict(), **validate_responses, **auth_responses})
|
||||||
@api_stats('location_get')
|
@api_stats('location_get')
|
||||||
@api_etag(base_mapdata=True)
|
@api_etag(base_mapdata=True)
|
||||||
|
@ -202,7 +207,8 @@ def location_by_id_geometry(request, location_id: AnyLocationID):
|
||||||
|
|
||||||
|
|
||||||
@map_api_router.get('/locations/by-slug/{location_slug}/', summary="location by slug (slim)",
|
@map_api_router.get('/locations/by-slug/{location_slug}/', summary="location by slug (slim)",
|
||||||
description="Get location by slug (with most important attributes set)",
|
description=("Get location by slug (with most important attributes set)\n\n"
|
||||||
|
"Possible location types:\n"+all_location_definitions),
|
||||||
response={200: SlimLocationSchema, **API404.dict(), **validate_responses, **auth_responses})
|
response={200: SlimLocationSchema, **API404.dict(), **validate_responses, **auth_responses})
|
||||||
@api_stats('location_get')
|
@api_stats('location_get')
|
||||||
@api_etag(base_mapdata=True)
|
@api_etag(base_mapdata=True)
|
||||||
|
@ -216,7 +222,8 @@ def location_by_slug(request, location_slug: NonEmptyStr, filters: Query[RemoveG
|
||||||
|
|
||||||
|
|
||||||
@map_api_router.get('/locations/by-slug/{location_slug}/full/', summary="location by slug (full)",
|
@map_api_router.get('/locations/by-slug/{location_slug}/full/', summary="location by slug (full)",
|
||||||
description="Get location by slug (with all attributes set)",
|
description=("Get location by slug (with all attributes set)\n\n"
|
||||||
|
"Possible location types:\n"+all_location_definitions),
|
||||||
response={200: FullLocationSchema, **API404.dict(), **validate_responses, **auth_responses})
|
response={200: FullLocationSchema, **API404.dict(), **validate_responses, **auth_responses})
|
||||||
@api_stats('location_get')
|
@api_stats('location_get')
|
||||||
@api_etag(base_mapdata=True)
|
@api_etag(base_mapdata=True)
|
||||||
|
|
|
@ -15,6 +15,7 @@ from c3nav.mapdata.models.geometry.space import (POI, Column, CrossDescription,
|
||||||
from c3nav.mapdata.models.locations import DynamicLocation
|
from c3nav.mapdata.models.locations import DynamicLocation
|
||||||
from c3nav.mapdata.schemas.filters import (ByCategoryFilter, ByGroupFilter, ByOnTopOfFilter, FilterSchema,
|
from c3nav.mapdata.schemas.filters import (ByCategoryFilter, ByGroupFilter, ByOnTopOfFilter, FilterSchema,
|
||||||
LevelGeometryFilter, SpaceGeometryFilter)
|
LevelGeometryFilter, SpaceGeometryFilter)
|
||||||
|
from c3nav.mapdata.schemas.model_base import schema_description
|
||||||
from c3nav.mapdata.schemas.models import (AccessRestrictionGroupSchema, AccessRestrictionSchema, AreaSchema,
|
from c3nav.mapdata.schemas.models import (AccessRestrictionGroupSchema, AccessRestrictionSchema, AreaSchema,
|
||||||
BuildingSchema, ColumnSchema, CrossDescriptionSchema, DoorSchema,
|
BuildingSchema, ColumnSchema, CrossDescriptionSchema, DoorSchema,
|
||||||
DynamicLocationSchema, HoleSchema, LeaveDescriptionSchema, LevelSchema,
|
DynamicLocationSchema, HoleSchema, LeaveDescriptionSchema, LevelSchema,
|
||||||
|
@ -58,10 +59,6 @@ def mapdata_retrieve_endpoint(request, model: Type[Model], **lookups):
|
||||||
raise API404("%s not found" % model.__name__.lower())
|
raise API404("%s not found" % model.__name__.lower())
|
||||||
|
|
||||||
|
|
||||||
def schema_description(schema):
|
|
||||||
return schema.__doc__.replace("\n ", "\n")
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Levels
|
Levels
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from typing import Annotated, Any, Optional, Union
|
import re
|
||||||
|
from typing import Annotated, Any, Union
|
||||||
|
|
||||||
from ninja import Schema
|
from ninja import Schema
|
||||||
from pydantic import Field as APIField
|
from pydantic import Field as APIField
|
||||||
|
@ -10,6 +11,31 @@ from c3nav.api.schema import LineStringSchema, PointSchema, PolygonSchema
|
||||||
from c3nav.api.utils import NonEmptyStr
|
from c3nav.api.utils import NonEmptyStr
|
||||||
|
|
||||||
|
|
||||||
|
def schema_description(schema):
|
||||||
|
return schema.__doc__.replace("\n ", "\n").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def schema_definition(schema):
|
||||||
|
return ("- **"+re.sub(r"([a-z])([A-Z])", r"\1 \2", schema.__name__.removesuffix("Schema")) +"**: " +
|
||||||
|
schema_description(schema).split("\n")[0].strip())
|
||||||
|
|
||||||
|
|
||||||
|
def schema_definitions(schemas):
|
||||||
|
return "\n".join(schema_definition(schema) for schema in schemas)
|
||||||
|
|
||||||
|
|
||||||
|
BoundsSchema = tuple[
|
||||||
|
Annotated[tuple[
|
||||||
|
Annotated[float, APIField(title="left", description="lowest X coordindate")],
|
||||||
|
Annotated[float, APIField(title="bottom", description="lowest Y coordindate")]
|
||||||
|
], APIField(title="(left, bottom)", description="lowest coordinates", example=(-10, -20))],
|
||||||
|
Annotated[tuple[
|
||||||
|
Annotated[float, APIField(title="right", description="highest X coordindate")],
|
||||||
|
Annotated[float, APIField(title="top", description="highest Y coordindate")]
|
||||||
|
], APIField(title="(right, top)", description="highest coordinates", example=(20, 30))]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class SerializableSchema(Schema):
|
class SerializableSchema(Schema):
|
||||||
@model_validator(mode="wrap") # noqa
|
@model_validator(mode="wrap") # noqa
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -23,62 +49,79 @@ class SerializableSchema(Schema):
|
||||||
class DjangoModelSchema(SerializableSchema):
|
class DjangoModelSchema(SerializableSchema):
|
||||||
id: PositiveInt = APIField(
|
id: PositiveInt = APIField(
|
||||||
title="ID",
|
title="ID",
|
||||||
|
example=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LocationSlugSchema(Schema):
|
class LocationSlugSchema(Schema):
|
||||||
slug: NonEmptyStr = APIField(
|
slug: NonEmptyStr = APIField(
|
||||||
title="location slug",
|
title="location slug",
|
||||||
description="a slug is a unique way to refer to a location across all location types. "
|
description="a slug is a unique way to refer to a location. while locations have a shared ID space, slugs"
|
||||||
"locations can have a human-readable slug. "
|
"are meants to be human-readable and easy to remember.\n\n"
|
||||||
"if it doesn't, this field holds a slug generated based from the location type and ID. "
|
"if a location doesn't have a slug defined, this field holds a slug generated from the "
|
||||||
"this slug will work even if a human-readable slug is defined later. "
|
"location type and ID, which will work even if a human-readable slug is defined later.\n\n"
|
||||||
"even dynamic locations like coordinates have a slug.",
|
"even dynamic locations like coordinates have an (auto-generated) slug.",
|
||||||
|
example="entrance",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WithAccessRestrictionSchema(Schema):
|
class WithAccessRestrictionSchema(Schema):
|
||||||
access_restriction: Optional[PositiveInt] = APIField(
|
access_restriction: Union[
|
||||||
|
Annotated[PositiveInt, APIField(title="access restriction ID")],
|
||||||
|
Annotated[None, APIField(title="null", description="no access restriction")],
|
||||||
|
] = APIField(
|
||||||
default=None,
|
default=None,
|
||||||
title="access restriction ID",
|
title="access restriction ID",
|
||||||
|
description="access restriction that this object belongs to",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TitledSchema(Schema):
|
class TitledSchema(Schema):
|
||||||
titles: dict[NonEmptyStr, NonEmptyStr] = APIField(
|
titles: dict[NonEmptyStr, NonEmptyStr] = APIField(
|
||||||
title="title (all languages)",
|
title="title (all languages)",
|
||||||
description="property names are the ISO-language code. languages may be missing.",
|
description="title in all available languages. property names are the ISO-language code. "
|
||||||
|
"languages may be missing.",
|
||||||
example={
|
example={
|
||||||
"en": "Title",
|
"en": "Entrance",
|
||||||
"de": "Titel",
|
"de": "Eingang",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
title: NonEmptyStr = APIField(
|
title: NonEmptyStr = APIField(
|
||||||
title="title (preferred language)",
|
title="title (preferred language)",
|
||||||
description="preferred language based on the Accept-Language header."
|
description="title in the preferred language based on the Accept-Language header.",
|
||||||
|
example="Entrance",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LocationSchema(WithAccessRestrictionSchema, TitledSchema, LocationSlugSchema):
|
class LocationSchema(WithAccessRestrictionSchema, TitledSchema, LocationSlugSchema):
|
||||||
subtitle: NonEmptyStr = APIField(
|
subtitle: NonEmptyStr = APIField(
|
||||||
title="subtitle (preferred language)",
|
title="subtitle (preferred language)",
|
||||||
description="an automatically generated short description for this location. "
|
description="an automatically generated short description for this location in the "
|
||||||
"preferred language based on the Accept-Language header."
|
"preferred language based on the Accept-Language header.",
|
||||||
|
example="near Area 51",
|
||||||
)
|
)
|
||||||
icon: Optional[NonEmptyStr] = APIField(
|
icon: NonEmptyStr = APIField(
|
||||||
default=None,
|
|
||||||
title="icon name",
|
title="icon name",
|
||||||
description="any material design icon name"
|
description="any material design icon name",
|
||||||
|
example="pin_drop",
|
||||||
)
|
)
|
||||||
can_search: bool = APIField(
|
can_search: bool = APIField(
|
||||||
title="can be searched",
|
title="can be searched",
|
||||||
|
description="if `true`, this object can show up in search results",
|
||||||
)
|
)
|
||||||
can_describe: bool = APIField(
|
can_describe: bool = APIField(
|
||||||
title="can describe locations",
|
title="can describe locations",
|
||||||
|
description="if `true`, this object can be used to describe other locations (e.g. in their subtitle)",
|
||||||
)
|
)
|
||||||
add_search: Optional[str] = APIField(
|
add_search: Union[
|
||||||
|
Annotated[str, APIField(title="search terms", description="set when looking for searchable locations")],
|
||||||
|
Annotated[None, APIField(title="null", description="when not looking for searchable locations")],
|
||||||
|
] = APIField(
|
||||||
None,
|
None,
|
||||||
title="more data for search index, only set when looking for searchable locations"
|
title="additional search terms",
|
||||||
|
description="more data for the search index separated by spaces, "
|
||||||
|
"only set when looking for searchable locations",
|
||||||
|
example="more search terms",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,49 +133,91 @@ class LabelSettingsSchema(DjangoModelSchema): # todo: add titles back in here
|
||||||
min_zoom: float = APIField(
|
min_zoom: float = APIField(
|
||||||
-10,
|
-10,
|
||||||
title="min zoom",
|
title="min zoom",
|
||||||
|
description="minimum zoom to display the label at",
|
||||||
)
|
)
|
||||||
max_zoom: float = APIField(
|
max_zoom: float = APIField(
|
||||||
10,
|
10,
|
||||||
title="max zoom",
|
title="max zoom",
|
||||||
|
description="maximum, zoom to display the label at",
|
||||||
)
|
)
|
||||||
font_size: PositiveInt = APIField(
|
font_size: PositiveInt = APIField(
|
||||||
title="font size",
|
title="font size",
|
||||||
|
description="font size of the label",
|
||||||
|
example=12,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SpecificLocationSchema(LocationSchema):
|
class SpecificLocationSchema(LocationSchema):
|
||||||
grid_square: Optional[NonEmptyStr] = APIField(
|
grid_square: Union[
|
||||||
|
Annotated[NonEmptyStr, APIField(title="grid square", description="grid square(s) that this location is in")],
|
||||||
|
Annotated[None, APIField(title="null", description="no grid defined or outside of grid")],
|
||||||
|
] = APIField(
|
||||||
default=None,
|
default=None,
|
||||||
title="grid square",
|
title="grid square",
|
||||||
description="if a grid is defined and this location is within it",
|
description="grid cell(s) that this location is in, if a grid is defined and the location is within it",
|
||||||
|
example="C3",
|
||||||
)
|
)
|
||||||
groups: dict[NonEmptyStr, list[PositiveInt] | Optional[PositiveInt]] = APIField(
|
groups: dict[
|
||||||
|
Annotated[NonEmptyStr, APIField(title="location group category name")],
|
||||||
|
Union[
|
||||||
|
Annotated[list[PositiveInt], APIField(
|
||||||
|
title="array of location IDs",
|
||||||
|
description="for categories that have `single` set to `false`. can be an empty array.",
|
||||||
|
example=[1,4,5],
|
||||||
|
)],
|
||||||
|
Annotated[PositiveInt, APIField(
|
||||||
|
title="one location ID",
|
||||||
|
description="for categories that have `single` set to `true`.",
|
||||||
|
example=1,
|
||||||
|
)],
|
||||||
|
Annotated[None, APIField(
|
||||||
|
title="null",
|
||||||
|
description="for categories that have `single` set to `true`."
|
||||||
|
)],
|
||||||
|
]
|
||||||
|
] = APIField(
|
||||||
title="location groups",
|
title="location groups",
|
||||||
description="grouped by location group categories. "
|
description="location group(s) that this specific location belongs to, grouped by categories.\n\n"
|
||||||
"property names are the names of location groupes. "
|
"keys are location group category names. see location group category endpoint for details.\n\n"
|
||||||
"property values are integer, None or a list of integers, see example."
|
|
||||||
"see location group category endpoint for currently available possibilities."
|
|
||||||
"categories may be missing if no groups apply.",
|
"categories may be missing if no groups apply.",
|
||||||
example={
|
example={
|
||||||
"category_with_single_true": 5,
|
"category_with_single_true": 5,
|
||||||
"other_category_with_single_true": None,
|
"other_category_with_single_true": None,
|
||||||
"categoryother_category_with_single_false": [1, 3, 7],
|
"category_with_single_false": [1, 3, 7],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
label_settings: Optional[LabelSettingsSchema] = APIField(
|
label_settings: Union[
|
||||||
|
Annotated[LabelSettingsSchema, APIField(
|
||||||
|
title="label settings",
|
||||||
|
description="label settings to use",
|
||||||
|
)],
|
||||||
|
Annotated[None, APIField(
|
||||||
|
title="null",
|
||||||
|
description="label settings from location group will be used, if available"
|
||||||
|
)],
|
||||||
|
] = APIField(
|
||||||
default=None,
|
default=None,
|
||||||
title="label settings",
|
title="label settings",
|
||||||
description="if not set, it may be taken from location groups"
|
description=(
|
||||||
|
schema_description(LabelSettingsSchema) +
|
||||||
|
"\n\nif not set, label settings of location groups might be used"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
label_override: Optional[NonEmptyStr] = APIField(
|
label_override: Union[
|
||||||
|
Annotated[NonEmptyStr, APIField(title="label override", description="text to use for label")],
|
||||||
|
Annotated[None, APIField(title="null", description="title will be used")],
|
||||||
|
] = APIField(
|
||||||
default=None,
|
default=None,
|
||||||
title="label override (preferred language)",
|
title="label override (preferred language)",
|
||||||
description="preferred language based on the Accept-Language header."
|
description="text to use for the label. by default (null), the title would be used."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WithPolygonGeometrySchema(Schema):
|
class WithPolygonGeometrySchema(Schema):
|
||||||
geometry: Optional[PolygonSchema] = APIField(
|
geometry: Union[
|
||||||
|
PolygonSchema,
|
||||||
|
Annotated[None, APIField(title="null", description="geometry not available of excluded from endpoint")]
|
||||||
|
] = APIField(
|
||||||
None,
|
None,
|
||||||
title="geometry",
|
title="geometry",
|
||||||
description="can be null if not available or excluded from endpoint",
|
description="can be null if not available or excluded from endpoint",
|
||||||
|
@ -140,7 +225,10 @@ class WithPolygonGeometrySchema(Schema):
|
||||||
|
|
||||||
|
|
||||||
class WithLineStringGeometrySchema(Schema):
|
class WithLineStringGeometrySchema(Schema):
|
||||||
geometry: Optional[LineStringSchema] = APIField(
|
geometry: Union[
|
||||||
|
LineStringSchema,
|
||||||
|
Annotated[None, APIField(title="null", description="geometry not available of excluded from endpoint")]
|
||||||
|
] = APIField(
|
||||||
None,
|
None,
|
||||||
title="geometry",
|
title="geometry",
|
||||||
description="can be null if not available or excluded from endpoint",
|
description="can be null if not available or excluded from endpoint",
|
||||||
|
@ -148,7 +236,10 @@ class WithLineStringGeometrySchema(Schema):
|
||||||
|
|
||||||
|
|
||||||
class WithPointGeometrySchema(Schema):
|
class WithPointGeometrySchema(Schema):
|
||||||
geometry: Optional[PointSchema] = APIField(
|
geometry: Union[
|
||||||
|
PointSchema,
|
||||||
|
Annotated[None, APIField(title="null", description="geometry not available of excluded from endpoint")]
|
||||||
|
] = APIField(
|
||||||
None,
|
None,
|
||||||
title="geometry",
|
title="geometry",
|
||||||
description="can be null if not available or excluded from endpoint",
|
description="can be null if not available or excluded from endpoint",
|
||||||
|
@ -159,6 +250,7 @@ class WithLevelSchema(SerializableSchema):
|
||||||
level: PositiveInt = APIField(
|
level: PositiveInt = APIField(
|
||||||
title="level",
|
title="level",
|
||||||
description="level id this object belongs to.",
|
description="level id this object belongs to.",
|
||||||
|
example=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -166,6 +258,7 @@ class WithSpaceSchema(SerializableSchema):
|
||||||
space: PositiveInt = APIField(
|
space: PositiveInt = APIField(
|
||||||
title="space",
|
title="space",
|
||||||
description="space id this object belongs to.",
|
description="space id this object belongs to.",
|
||||||
|
example=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -182,8 +275,8 @@ class SimpleGeometryPointSchema(Schema):
|
||||||
|
|
||||||
|
|
||||||
class SimpleGeometryPointAndBoundsSchema(SimpleGeometryPointSchema):
|
class SimpleGeometryPointAndBoundsSchema(SimpleGeometryPointSchema):
|
||||||
bounds: tuple[tuple[float, float], tuple[float, float]] = APIField(
|
bounds: BoundsSchema = APIField(
|
||||||
description="location bounding box from (x, y) to (x, y)",
|
description="location bounding box",
|
||||||
example=((-10, -20), (20, 30)),
|
example=((-10, -20), (20, 30)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -212,7 +305,7 @@ Coordinates3D = tuple[float, float, float]
|
||||||
AnyLocationID = Union[
|
AnyLocationID = Union[
|
||||||
Annotated[PositiveInt, APIField(
|
Annotated[PositiveInt, APIField(
|
||||||
title="location ID",
|
title="location ID",
|
||||||
description="numeric ID of any lcation"
|
description="numeric ID of any lcation – all locations have a shared ID space"
|
||||||
)],
|
)],
|
||||||
CustomLocationID,
|
CustomLocationID,
|
||||||
PositionID,
|
PositionID,
|
||||||
|
|
|
@ -13,7 +13,8 @@ from c3nav.mapdata.schemas.model_base import (AnyLocationID, AnyPositionID, Cust
|
||||||
SimpleGeometryPointSchema, SpecificLocationSchema, TitledSchema,
|
SimpleGeometryPointSchema, SpecificLocationSchema, TitledSchema,
|
||||||
WithAccessRestrictionSchema, WithLevelSchema,
|
WithAccessRestrictionSchema, WithLevelSchema,
|
||||||
WithLineStringGeometrySchema, WithPointGeometrySchema,
|
WithLineStringGeometrySchema, WithPointGeometrySchema,
|
||||||
WithPolygonGeometrySchema, WithSpaceSchema)
|
WithPolygonGeometrySchema, WithSpaceSchema, schema_definitions,
|
||||||
|
schema_description)
|
||||||
|
|
||||||
|
|
||||||
class LevelSchema(SpecificLocationSchema, DjangoModelSchema):
|
class LevelSchema(SpecificLocationSchema, DjangoModelSchema):
|
||||||
|
@ -26,18 +27,26 @@ class LevelSchema(SpecificLocationSchema, DjangoModelSchema):
|
||||||
title="short label (for level selector)",
|
title="short label (for level selector)",
|
||||||
description="unique among levels",
|
description="unique among levels",
|
||||||
)
|
)
|
||||||
on_top_of: Optional[PositiveInt] = APIField(
|
on_top_of: Union[
|
||||||
|
Annotated[PositiveInt, APIField(title="level ID", description="level this level is on top of", example=1)],
|
||||||
|
Annotated[None, APIField(title="null", description="this is a main level, not on top of any other")]
|
||||||
|
] = APIField(
|
||||||
title="on top of level ID",
|
title="on top of level ID",
|
||||||
description="if set, this is not a main level, but it's on top of this other level"
|
description="if set, this is not a main level, but it's on top of this other level"
|
||||||
)
|
)
|
||||||
base_altitude: float = APIField(
|
base_altitude: float = APIField(
|
||||||
title="base/default altitude",
|
title="base/default altitude",
|
||||||
|
description="default ground altitude for this level, if it can't be determined using altitude markers.",
|
||||||
)
|
)
|
||||||
default_height: PositiveFloat = APIField(
|
default_height: PositiveFloat = APIField(
|
||||||
title="default ceiling height",
|
title="default ceiling height",
|
||||||
|
description="default ceiling height for all spaces that don't set their own",
|
||||||
|
example=2.5
|
||||||
)
|
)
|
||||||
door_height: PositiveFloat = APIField(
|
door_height: PositiveFloat = APIField(
|
||||||
title="door height",
|
title="door height",
|
||||||
|
description="height for all doors on this level",
|
||||||
|
example="2.0",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,7 +95,8 @@ class AreaSchema(WithPolygonGeometrySchema, SpecificLocationSchema, WithSpaceSch
|
||||||
"""
|
"""
|
||||||
slow_down_factor: PositiveFloat = APIField(
|
slow_down_factor: PositiveFloat = APIField(
|
||||||
title="slow-down factor",
|
title="slow-down factor",
|
||||||
description="how much walking in this area is slowed down, overlapping areas are multiplied"
|
description="how much walking in this area is slowed down, overlapping areas are multiplied",
|
||||||
|
example=1.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -212,16 +222,31 @@ class LocationGroupSchema(LocationSchema, DjangoModelSchema):
|
||||||
)
|
)
|
||||||
priority: int = APIField() # todo: ???
|
priority: int = APIField() # todo: ???
|
||||||
hierarchy: int = APIField() # todo: ???
|
hierarchy: int = APIField() # todo: ???
|
||||||
label_settings: Optional[LabelSettingsSchema] = APIField(
|
label_settings: Union[
|
||||||
|
Annotated[LabelSettingsSchema, APIField(
|
||||||
|
title="label settings",
|
||||||
|
description="label settings to use for gruop members that don't have their own set",
|
||||||
|
)],
|
||||||
|
Annotated[None, APIField(
|
||||||
|
title="null",
|
||||||
|
description="no label settings set"
|
||||||
|
)],
|
||||||
|
] = APIField(
|
||||||
default=None,
|
default=None,
|
||||||
title="label settings",
|
title="label settings",
|
||||||
description="for locations with this group, can be overwritten by specific locations"
|
description=(
|
||||||
|
schema_description(LabelSettingsSchema) +
|
||||||
|
"\n\nlocations can override this setting"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
can_report_missing: bool = APIField(
|
can_report_missing: bool = APIField(
|
||||||
title="report missing locations",
|
title="report missing locations",
|
||||||
description="can be used in form for reporting missing locations",
|
description="can be used in form for reporting missing locations",
|
||||||
)
|
)
|
||||||
color: Optional[NonEmptyStr] = APIField(
|
color: Union[
|
||||||
|
Annotated[NonEmptyStr, APIField(title="color", description="a valid CSS color expression")],
|
||||||
|
Annotated[None, APIField(title="null", description="default/no color will be used")],
|
||||||
|
] = APIField(
|
||||||
title="color",
|
title="color",
|
||||||
description="an optional color for spaces and areas with this group"
|
description="an optional color for spaces and areas with this group"
|
||||||
)
|
)
|
||||||
|
@ -285,7 +310,7 @@ class LocationGroupCategorySchema(TitledSchema, DjangoModelSchema):
|
||||||
|
|
||||||
class DynamicLocationSchema(SpecificLocationSchema, DjangoModelSchema):
|
class DynamicLocationSchema(SpecificLocationSchema, DjangoModelSchema):
|
||||||
"""
|
"""
|
||||||
A dynamic location represents a moving object. Its position has to be separately queries through the position API.
|
Represents a moving object. Its position has to be separately queried through the position API.
|
||||||
|
|
||||||
A dynamic location is a specific location, and can therefore be routed to and from,
|
A dynamic location is a specific location, and can therefore be routed to and from,
|
||||||
as well as belong to location groups.
|
as well as belong to location groups.
|
||||||
|
@ -344,10 +369,10 @@ class CustomLocationSchema(SerializableSchema):
|
||||||
slug: CustomLocationID = APIField(
|
slug: CustomLocationID = APIField(
|
||||||
description="slug, identical to ID"
|
description="slug, identical to ID"
|
||||||
)
|
)
|
||||||
icon: Optional[NonEmptyStr] = APIField(
|
icon: NonEmptyStr = APIField(
|
||||||
default=None,
|
|
||||||
title="icon name",
|
title="icon name",
|
||||||
description="any material design icon name"
|
description="any material design icon name",
|
||||||
|
example="pin_drop",
|
||||||
)
|
)
|
||||||
title: NonEmptyStr = APIField(
|
title: NonEmptyStr = APIField(
|
||||||
title="title (preferred language)",
|
title="title (preferred language)",
|
||||||
|
@ -359,32 +384,56 @@ class CustomLocationSchema(SerializableSchema):
|
||||||
"preferred language based on the Accept-Language header."
|
"preferred language based on the Accept-Language header."
|
||||||
)
|
)
|
||||||
level: PositiveInt = APIField(
|
level: PositiveInt = APIField(
|
||||||
description="level ID this custom location is located on"
|
description="level ID this custom location is located on",
|
||||||
|
example=1,
|
||||||
)
|
)
|
||||||
space: Optional[PositiveInt] = APIField(
|
space: Union[
|
||||||
description="space ID this custom location is located in, if applicable"
|
Annotated[PositiveInt, APIField(title="space ID", example=1)],
|
||||||
|
Annotated[None, APIField(title="null", description="the location is not inside a space")],
|
||||||
|
] = APIField(
|
||||||
|
default=None,
|
||||||
|
description="space ID this custom location is located in"
|
||||||
)
|
)
|
||||||
areas: list[PositiveInt] = APIField(
|
areas: list[PositiveInt] = APIField(
|
||||||
description="IDs of areas this custom location is located in"
|
description="IDs of areas this custom location is located in"
|
||||||
)
|
)
|
||||||
grid_square: Optional[NonEmptyStr] = APIField(
|
grid_square: Union[
|
||||||
|
Annotated[NonEmptyStr, APIField(title="grid square", description="grid square(s) that this location is in")],
|
||||||
|
Annotated[None, APIField(title="null", description="no grid defined or outside of grid")],
|
||||||
|
] = APIField(
|
||||||
default=None,
|
default=None,
|
||||||
title="grid square",
|
title="grid square",
|
||||||
description="if a grid is defined and this custom location is within it",
|
description="grid cell(s) that this location is in, if a grid is defined and the location is within it",
|
||||||
|
example="C3",
|
||||||
)
|
)
|
||||||
near_area: Optional[PositiveInt] = APIField(
|
near_area: Union[
|
||||||
description="the ID of an area near this custom location, if there is one"
|
Annotated[PositiveInt, APIField(title="area ID", example=1)],
|
||||||
|
Annotated[None, APIField(title="null", description="the location is not near any areas")],
|
||||||
|
] = APIField(
|
||||||
|
near="nearby area",
|
||||||
|
description="the ID of an area near this custom location"
|
||||||
)
|
)
|
||||||
near_poi: Optional[PositiveInt] = APIField(
|
near_poi: Union[
|
||||||
description="the ID of a POI near this custom location, if there is one"
|
Annotated[PositiveInt, APIField(title="POI ID", example=1)],
|
||||||
|
Annotated[None, APIField(title="null", description="the location is not near any POIs")],
|
||||||
|
] = APIField(
|
||||||
|
title="nearby POI",
|
||||||
|
description="the ID of a POI near this custom location"
|
||||||
)
|
)
|
||||||
nearby: list[PositiveInt] = APIField(
|
nearby: list[PositiveInt] = APIField(
|
||||||
description="list of IDs of nearby locations"
|
description="list of IDs of nearby locations"
|
||||||
)
|
)
|
||||||
altitude: Optional[float] = APIField(
|
altitude: Union[
|
||||||
description="ground altitude (in the map-wide coordinate system), if it can be determined"
|
Annotated[float, APIField(title="ground altitude", example=1)],
|
||||||
|
Annotated[None, APIField(title="null", description="could not be determined (outside of space?)")],
|
||||||
|
] = APIField(
|
||||||
|
title="ground altitude",
|
||||||
|
description="ground altitude (in the map-wide coordinate system)"
|
||||||
)
|
)
|
||||||
geometry: Optional[PointSchema] = APIField(
|
geometry: Union[
|
||||||
|
PointSchema,
|
||||||
|
Annotated[None, APIField(title="null", description="geometry excluded from endpoint")]
|
||||||
|
] = APIField(
|
||||||
None,
|
None,
|
||||||
description="point geometry for this custom location",
|
description="point geometry for this custom location",
|
||||||
)
|
)
|
||||||
|
@ -392,37 +441,43 @@ class CustomLocationSchema(SerializableSchema):
|
||||||
|
|
||||||
class TrackablePositionSchema(Schema):
|
class TrackablePositionSchema(Schema):
|
||||||
"""
|
"""
|
||||||
A trackable position. It's position can be set or reset.
|
A trackable position. Its position can be set or reset.
|
||||||
"""
|
"""
|
||||||
id: PositionID = APIField(
|
id: PositionID = APIField(
|
||||||
description="ID representing the position"
|
description="ID representing the position",
|
||||||
|
example="p:adskjfalskdj",
|
||||||
)
|
)
|
||||||
slug: PositionID = APIField(
|
slug: PositionID = APIField(
|
||||||
description="slug representing the position"
|
description="slug representing the position",
|
||||||
|
example="p:adskjfalskdj",
|
||||||
)
|
)
|
||||||
icon: Optional[NonEmptyStr] = APIField(
|
icon: NonEmptyStr = APIField(
|
||||||
default=None,
|
|
||||||
title="icon name",
|
title="icon name",
|
||||||
description="any material design icon name"
|
description="any material design icon name",
|
||||||
|
example="pin_drop",
|
||||||
)
|
)
|
||||||
title: NonEmptyStr = APIField(
|
title: NonEmptyStr = APIField(
|
||||||
title="title of the position",
|
title="title of the position",
|
||||||
|
example="My position"
|
||||||
)
|
)
|
||||||
subtitle: NonEmptyStr = APIField(
|
subtitle: NonEmptyStr = APIField(
|
||||||
title="subtitle (preferred language)",
|
title="subtitle (preferred language)",
|
||||||
description="an automatically generated short description, which might change when the position changes. "
|
description="an automatically generated short description, which might change when the position changes. "
|
||||||
"preferred language based on the Accept-Language header."
|
"preferred language based on the Accept-Language header.",
|
||||||
|
example="Near Bällebad"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def put_locationtype_first(schema):
|
|
||||||
fields = schema.__fields__.copy()
|
|
||||||
schema.__fields__ = {"locationtype": fields.pop("locationtype"), **fields}
|
|
||||||
return schema
|
|
||||||
|
|
||||||
|
|
||||||
class LocationTypeSchema(Schema):
|
class LocationTypeSchema(Schema):
|
||||||
locationtype: str
|
locationtype: str = APIField(title="location type",
|
||||||
|
description="indicates what kind of location is included. "
|
||||||
|
"different location types have different fields.")
|
||||||
|
|
||||||
|
|
||||||
|
def LocationTypeAPIField():
|
||||||
|
return APIField(title="location type",
|
||||||
|
description="indicates what kind of location is included. "
|
||||||
|
"different location types have different fields.")
|
||||||
|
|
||||||
|
|
||||||
class FullLevelLocationSchema(LevelSchema, LocationTypeSchema):
|
class FullLevelLocationSchema(LevelSchema, LocationTypeSchema):
|
||||||
|
@ -430,7 +485,7 @@ class FullLevelLocationSchema(LevelSchema, LocationTypeSchema):
|
||||||
A level for the location API.
|
A level for the location API.
|
||||||
See Level schema for details.
|
See Level schema for details.
|
||||||
"""
|
"""
|
||||||
locationtype: Literal["level"]
|
locationtype: Literal["level"] = LocationTypeAPIField()
|
||||||
|
|
||||||
|
|
||||||
class FullSpaceLocationSchema(SimpleGeometryPointAndBoundsSchema, SpaceSchema, LocationTypeSchema):
|
class FullSpaceLocationSchema(SimpleGeometryPointAndBoundsSchema, SpaceSchema, LocationTypeSchema):
|
||||||
|
@ -438,7 +493,7 @@ class FullSpaceLocationSchema(SimpleGeometryPointAndBoundsSchema, SpaceSchema, L
|
||||||
A space with some additional information for the location API.
|
A space with some additional information for the location API.
|
||||||
See Space schema for details.
|
See Space schema for details.
|
||||||
"""
|
"""
|
||||||
locationtype: Literal["space"]
|
locationtype: Literal["space"] = LocationTypeAPIField()
|
||||||
|
|
||||||
|
|
||||||
class FullAreaLocationSchema(SimpleGeometryPointAndBoundsSchema, AreaSchema, LocationTypeSchema):
|
class FullAreaLocationSchema(SimpleGeometryPointAndBoundsSchema, AreaSchema, LocationTypeSchema):
|
||||||
|
@ -446,7 +501,7 @@ class FullAreaLocationSchema(SimpleGeometryPointAndBoundsSchema, AreaSchema, Loc
|
||||||
An area with some additional information for the location API.
|
An area with some additional information for the location API.
|
||||||
See Area schema for details.
|
See Area schema for details.
|
||||||
"""
|
"""
|
||||||
locationtype: Literal["area"]
|
locationtype: Literal["area"] = LocationTypeAPIField()
|
||||||
|
|
||||||
|
|
||||||
class FullPOILocationSchema(SimpleGeometryPointSchema, POISchema, LocationTypeSchema):
|
class FullPOILocationSchema(SimpleGeometryPointSchema, POISchema, LocationTypeSchema):
|
||||||
|
@ -454,7 +509,7 @@ class FullPOILocationSchema(SimpleGeometryPointSchema, POISchema, LocationTypeSc
|
||||||
A point of interest with some additional information for the location API.
|
A point of interest with some additional information for the location API.
|
||||||
See POI schema for details.
|
See POI schema for details.
|
||||||
"""
|
"""
|
||||||
locationtype: Literal["poi"]
|
locationtype: Literal["poi"] = LocationTypeAPIField()
|
||||||
|
|
||||||
|
|
||||||
class FullLocationGroupLocationSchema(SimpleGeometryLocationsSchema, LocationGroupSchema, LocationTypeSchema):
|
class FullLocationGroupLocationSchema(SimpleGeometryLocationsSchema, LocationGroupSchema, LocationTypeSchema):
|
||||||
|
@ -462,7 +517,7 @@ class FullLocationGroupLocationSchema(SimpleGeometryLocationsSchema, LocationGro
|
||||||
A location 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.
|
See LocationGroup schema for details.
|
||||||
"""
|
"""
|
||||||
locationtype: Literal["locationgroup"]
|
locationtype: Literal["locationgroup"] = LocationTypeAPIField()
|
||||||
|
|
||||||
|
|
||||||
class FullDynamicLocationLocationSchema(DynamicLocationSchema, LocationTypeSchema):
|
class FullDynamicLocationLocationSchema(DynamicLocationSchema, LocationTypeSchema):
|
||||||
|
@ -470,7 +525,7 @@ class FullDynamicLocationLocationSchema(DynamicLocationSchema, LocationTypeSchem
|
||||||
A dynamic location for the location API.
|
A dynamic location for the location API.
|
||||||
See DynamicLocation schema for details.
|
See DynamicLocation schema for details.
|
||||||
"""
|
"""
|
||||||
locationtype: Literal["dynamiclocation"]
|
locationtype: Literal["dynamiclocation"] = LocationTypeAPIField()
|
||||||
|
|
||||||
|
|
||||||
class CustomLocationLocationSchema(SimpleGeometryPointAndBoundsSchema, CustomLocationSchema, LocationTypeSchema):
|
class CustomLocationLocationSchema(SimpleGeometryPointAndBoundsSchema, CustomLocationSchema, LocationTypeSchema):
|
||||||
|
@ -478,7 +533,7 @@ class CustomLocationLocationSchema(SimpleGeometryPointAndBoundsSchema, CustomLoc
|
||||||
A custom location for the location API.
|
A custom location for the location API.
|
||||||
See CustomLocation schema for details.
|
See CustomLocation schema for details.
|
||||||
"""
|
"""
|
||||||
locationtype: Literal["customlocation"]
|
locationtype: Literal["customlocation"] = LocationTypeAPIField()
|
||||||
|
|
||||||
|
|
||||||
class TrackablePositionLocationSchema(TrackablePositionSchema, LocationTypeSchema):
|
class TrackablePositionLocationSchema(TrackablePositionSchema, LocationTypeSchema):
|
||||||
|
@ -486,7 +541,7 @@ class TrackablePositionLocationSchema(TrackablePositionSchema, LocationTypeSchem
|
||||||
A trackable position for the location API.
|
A trackable position for the location API.
|
||||||
See TrackablePosition schema for details.
|
See TrackablePosition schema for details.
|
||||||
"""
|
"""
|
||||||
locationtype: Literal["position"]
|
locationtype: Literal["position"] = LocationTypeAPIField()
|
||||||
|
|
||||||
|
|
||||||
class SlimLocationMixin(Schema):
|
class SlimLocationMixin(Schema):
|
||||||
|
@ -599,6 +654,14 @@ SlimLocationSchema = Annotated[
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
listable_location_definitions = schema_definitions(
|
||||||
|
(LevelSchema, SpaceSchema, AreaSchema, POISchema, DynamicLocationSchema, LocationGroupSchema)
|
||||||
|
)
|
||||||
|
all_location_definitions = listable_location_definitions + "\n" + schema_definitions(
|
||||||
|
(CustomLocationSchema, TrackablePositionSchema)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DisplayLink(Schema):
|
class DisplayLink(Schema):
|
||||||
"""
|
"""
|
||||||
A link for the location display
|
A link for the location display
|
||||||
|
@ -612,14 +675,24 @@ class DisplayLink(Schema):
|
||||||
class LocationDisplay(SerializableSchema):
|
class LocationDisplay(SerializableSchema):
|
||||||
id: AnyLocationID = APIField(
|
id: AnyLocationID = APIField(
|
||||||
description="a numeric ID for a map location or a string ID for generated locations",
|
description="a numeric ID for a map location or a string ID for generated locations",
|
||||||
|
example=1,
|
||||||
)
|
)
|
||||||
level: Optional[PositiveInt] = APIField(
|
|
||||||
|
level: Union[
|
||||||
|
Annotated[PositiveInt, APIField(title="level ID", description="ID of relevant level")],
|
||||||
|
Annotated[None, APIField(title="null", description="no relevant level")],
|
||||||
|
] = APIField(
|
||||||
None,
|
None,
|
||||||
description="level ID, if applicable"
|
title="level",
|
||||||
|
example=2,
|
||||||
)
|
)
|
||||||
space: Optional[PositiveInt] = APIField(
|
space: Union[
|
||||||
|
Annotated[PositiveInt, APIField(title="level ID", description="ID of relevant level")],
|
||||||
|
Annotated[None, APIField(title="null", description="no relevant level")],
|
||||||
|
] = APIField(
|
||||||
None,
|
None,
|
||||||
description="space ID, if applicable"
|
description="space",
|
||||||
|
example=3,
|
||||||
)
|
)
|
||||||
display: list[
|
display: list[
|
||||||
tuple[
|
tuple[
|
||||||
|
@ -631,12 +704,48 @@ class LocationDisplay(SerializableSchema):
|
||||||
Annotated[None, APIField(title="no value")]
|
Annotated[None, APIField(title="no value")]
|
||||||
], APIField(title="field value", union_mode='left_to_right')]
|
], APIField(title="field value", union_mode='left_to_right')]
|
||||||
]
|
]
|
||||||
] = APIField(description="a list of human-readable display values")
|
] = APIField(
|
||||||
geometry: Optional[GeometrySchema] = APIField(
|
title="display fields",
|
||||||
|
description="a list of human-readable display values",
|
||||||
|
example=[
|
||||||
|
("Title", "Awesome location"),
|
||||||
|
("Access Restriction", None),
|
||||||
|
("Level", {
|
||||||
|
"id": 2,
|
||||||
|
"slug": "level0",
|
||||||
|
"title": "Ground Floor",
|
||||||
|
"can_search": True,
|
||||||
|
}),
|
||||||
|
("Groups", [
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"slug": "entrances",
|
||||||
|
"title": "Entrances",
|
||||||
|
"can_search": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"slug": "startswithe",
|
||||||
|
"title": "Locations that Start with E",
|
||||||
|
"can_search": False,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
geometry: Union[
|
||||||
|
GeometrySchema,
|
||||||
|
Annotated[None, APIField(title="null", description="no geometry available")]
|
||||||
|
] = APIField(
|
||||||
None, description="representative geometry, if available"
|
None, description="representative geometry, if available"
|
||||||
)
|
)
|
||||||
editor_url: Optional[NonEmptyStr] = APIField(
|
editor_url: Union[
|
||||||
None, description="path to edit this object in the editor, if the user has access to it",
|
Annotated[NonEmptyStr, APIField(title="path to editor")],
|
||||||
|
Annotated[None, APIField(title="null", description="no editor access or object is not editable")],
|
||||||
|
] = APIField(
|
||||||
|
None,
|
||||||
|
title="editor URL",
|
||||||
|
description="path to edit this object in the editor",
|
||||||
|
example="/editor/spaces/2/pois/1/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,18 @@
|
||||||
from typing import Annotated, Optional
|
from typing import Annotated, Union
|
||||||
|
|
||||||
from ninja import Schema
|
from ninja import Schema
|
||||||
from pydantic import Field as APIField
|
from pydantic import Field as APIField
|
||||||
from pydantic import PositiveInt
|
from pydantic import PositiveInt
|
||||||
|
|
||||||
from c3nav.api.schema import GeometrySchema
|
from c3nav.api.schema import GeometrySchema
|
||||||
from c3nav.mapdata.schemas.model_base import AnyLocationID
|
from c3nav.mapdata.schemas.model_base import AnyLocationID, BoundsSchema
|
||||||
|
|
||||||
|
|
||||||
class BoundsSchema(Schema):
|
class WithBoundsSchema(Schema):
|
||||||
"""
|
"""
|
||||||
Describing a bounding box
|
Describing a bounding box
|
||||||
"""
|
"""
|
||||||
bounds: tuple[
|
bounds: BoundsSchema = APIField(
|
||||||
Annotated[tuple[
|
|
||||||
Annotated[float, APIField(title="left", description="lowest X coordindate")],
|
|
||||||
Annotated[float, APIField(title="bottom", description="lowest Y coordindate")]
|
|
||||||
], APIField(title="(left, bottom)", description="lowest coordinates", example=(-10, -20))],
|
|
||||||
Annotated[tuple[
|
|
||||||
Annotated[float, APIField(title="right", description="highest X coordindate")],
|
|
||||||
Annotated[float, APIField(title="top", description="highest Y coordindate")]
|
|
||||||
], APIField(title="(right, top)", description="highest coordinates", example=(20, 30))]
|
|
||||||
] = APIField(
|
|
||||||
title="boundaries",
|
title="boundaries",
|
||||||
description="(left, bottom) to (top, right)",
|
description="(left, bottom) to (top, right)",
|
||||||
)
|
)
|
||||||
|
@ -31,9 +22,15 @@ class LocationGeometry(Schema):
|
||||||
id: AnyLocationID = APIField(
|
id: AnyLocationID = APIField(
|
||||||
description="ID of the location that the geometry is being queried for",
|
description="ID of the location that the geometry is being queried for",
|
||||||
)
|
)
|
||||||
level: Optional[PositiveInt] = APIField(
|
level: Union[
|
||||||
|
Annotated[PositiveInt, APIField(title="level ID")],
|
||||||
|
Annotated[None, APIField(title="null", description="geometry is not on any level")], # todo: possible?
|
||||||
|
] = APIField(
|
||||||
description="ID of the level the geometry is on",
|
description="ID of the level the geometry is on",
|
||||||
)
|
)
|
||||||
geometry: Optional[GeometrySchema] = APIField(
|
geometry: Union[
|
||||||
|
GeometrySchema,
|
||||||
|
Annotated[None, APIField(title="null", description="no geometry available")]
|
||||||
|
] = APIField(
|
||||||
description="geometry, if available"
|
description="geometry, if available"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue