more map api documentation improvements

This commit is contained in:
Laura Klünder 2023-12-04 18:58:49 +01:00
parent 4d57b81ad1
commit 964574e535
8 changed files with 335 additions and 121 deletions

View file

@ -13,7 +13,8 @@ from c3nav.mapdata.schemas.model_base import (AnyLocationID, AnyPositionID, Cust
SimpleGeometryPointSchema, SpecificLocationSchema, TitledSchema,
WithAccessRestrictionSchema, WithLevelSchema,
WithLineStringGeometrySchema, WithPointGeometrySchema,
WithPolygonGeometrySchema, WithSpaceSchema)
WithPolygonGeometrySchema, WithSpaceSchema, schema_definitions,
schema_description)
class LevelSchema(SpecificLocationSchema, DjangoModelSchema):
@ -26,18 +27,26 @@ class LevelSchema(SpecificLocationSchema, DjangoModelSchema):
title="short label (for level selector)",
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",
description="if set, this is not a main level, but it's on top of this other level"
)
base_altitude: float = APIField(
title="base/default altitude",
description="default ground altitude for this level, if it can't be determined using altitude markers.",
)
default_height: PositiveFloat = APIField(
title="default ceiling height",
description="default ceiling height for all spaces that don't set their own",
example=2.5
)
door_height: PositiveFloat = APIField(
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(
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: ???
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,
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(
title="report 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",
description="an optional color for spaces and areas with this group"
)
@ -285,7 +310,7 @@ class LocationGroupCategorySchema(TitledSchema, 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,
as well as belong to location groups.
@ -344,10 +369,10 @@ class CustomLocationSchema(SerializableSchema):
slug: CustomLocationID = APIField(
description="slug, identical to ID"
)
icon: Optional[NonEmptyStr] = APIField(
default=None,
icon: NonEmptyStr = APIField(
title="icon name",
description="any material design icon name"
description="any material design icon name",
example="pin_drop",
)
title: NonEmptyStr = APIField(
title="title (preferred language)",
@ -359,32 +384,56 @@ class CustomLocationSchema(SerializableSchema):
"preferred language based on the Accept-Language header."
)
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(
description="space ID this custom location is located in, if applicable"
space: Union[
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(
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,
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(
description="the ID of an area near this custom location, if there is one"
near_area: Union[
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(
description="the ID of a POI near this custom location, if there is one"
near_poi: Union[
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(
description="list of IDs of nearby locations"
)
altitude: Optional[float] = APIField(
description="ground altitude (in the map-wide coordinate system), if it can be determined"
altitude: Union[
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,
description="point geometry for this custom location",
)
@ -392,37 +441,43 @@ class CustomLocationSchema(SerializableSchema):
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(
description="ID representing the position"
description="ID representing the position",
example="p:adskjfalskdj",
)
slug: PositionID = APIField(
description="slug representing the position"
description="slug representing the position",
example="p:adskjfalskdj",
)
icon: Optional[NonEmptyStr] = APIField(
default=None,
icon: NonEmptyStr = APIField(
title="icon name",
description="any material design icon name"
description="any material design icon name",
example="pin_drop",
)
title: NonEmptyStr = APIField(
title="title of the position",
example="My position"
)
subtitle: NonEmptyStr = APIField(
title="subtitle (preferred language)",
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):
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):
@ -430,7 +485,7 @@ class FullLevelLocationSchema(LevelSchema, LocationTypeSchema):
A level for the location API.
See Level schema for details.
"""
locationtype: Literal["level"]
locationtype: Literal["level"] = LocationTypeAPIField()
class FullSpaceLocationSchema(SimpleGeometryPointAndBoundsSchema, SpaceSchema, LocationTypeSchema):
@ -438,7 +493,7 @@ class FullSpaceLocationSchema(SimpleGeometryPointAndBoundsSchema, SpaceSchema, L
A space with some additional information for the location API.
See Space schema for details.
"""
locationtype: Literal["space"]
locationtype: Literal["space"] = LocationTypeAPIField()
class FullAreaLocationSchema(SimpleGeometryPointAndBoundsSchema, AreaSchema, LocationTypeSchema):
@ -446,7 +501,7 @@ class FullAreaLocationSchema(SimpleGeometryPointAndBoundsSchema, AreaSchema, Loc
An area with some additional information for the location API.
See Area schema for details.
"""
locationtype: Literal["area"]
locationtype: Literal["area"] = LocationTypeAPIField()
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.
See POI schema for details.
"""
locationtype: Literal["poi"]
locationtype: Literal["poi"] = LocationTypeAPIField()
class FullLocationGroupLocationSchema(SimpleGeometryLocationsSchema, LocationGroupSchema, LocationTypeSchema):
@ -462,7 +517,7 @@ class FullLocationGroupLocationSchema(SimpleGeometryLocationsSchema, LocationGro
A location group with some additional information for the location API.
See LocationGroup schema for details.
"""
locationtype: Literal["locationgroup"]
locationtype: Literal["locationgroup"] = LocationTypeAPIField()
class FullDynamicLocationLocationSchema(DynamicLocationSchema, LocationTypeSchema):
@ -470,7 +525,7 @@ class FullDynamicLocationLocationSchema(DynamicLocationSchema, LocationTypeSchem
A dynamic location for the location API.
See DynamicLocation schema for details.
"""
locationtype: Literal["dynamiclocation"]
locationtype: Literal["dynamiclocation"] = LocationTypeAPIField()
class CustomLocationLocationSchema(SimpleGeometryPointAndBoundsSchema, CustomLocationSchema, LocationTypeSchema):
@ -478,7 +533,7 @@ class CustomLocationLocationSchema(SimpleGeometryPointAndBoundsSchema, CustomLoc
A custom location for the location API.
See CustomLocation schema for details.
"""
locationtype: Literal["customlocation"]
locationtype: Literal["customlocation"] = LocationTypeAPIField()
class TrackablePositionLocationSchema(TrackablePositionSchema, LocationTypeSchema):
@ -486,7 +541,7 @@ class TrackablePositionLocationSchema(TrackablePositionSchema, LocationTypeSchem
A trackable position for the location API.
See TrackablePosition schema for details.
"""
locationtype: Literal["position"]
locationtype: Literal["position"] = LocationTypeAPIField()
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):
"""
A link for the location display
@ -612,14 +675,24 @@ class DisplayLink(Schema):
class LocationDisplay(SerializableSchema):
id: AnyLocationID = APIField(
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,
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,
description="space ID, if applicable"
description="space",
example=3,
)
display: list[
tuple[
@ -631,12 +704,48 @@ class LocationDisplay(SerializableSchema):
Annotated[None, APIField(title="no value")]
], APIField(title="field value", union_mode='left_to_right')]
]
] = APIField(description="a list of human-readable display values")
geometry: Optional[GeometrySchema] = APIField(
] = 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"
)
editor_url: Optional[NonEmptyStr] = APIField(
None, description="path to edit this object in the editor, if the user has access to it",
editor_url: Union[
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/"
)