integrate data overlay api into new MapdataAPIBuilder

This commit is contained in:
Laura Klünder 2024-12-03 09:59:51 +01:00
parent be76f3a8db
commit 707c81f159
5 changed files with 38 additions and 41 deletions

View file

@ -17,7 +17,7 @@ from c3nav.mapdata.models.geometry.space import (POI, Column, CrossDescription,
Obstacle, Ramp)
from c3nav.mapdata.models.locations import DynamicLocation
from c3nav.mapdata.schemas.filters import (ByCategoryFilter, ByGroupFilter, ByOnTopOfFilter, FilterSchema,
LevelGeometryFilter, SpaceGeometryFilter, BySpaceFilter)
LevelGeometryFilter, SpaceGeometryFilter, BySpaceFilter, ByOverlayFilter)
from c3nav.mapdata.schemas.model_base import schema_description
from c3nav.mapdata.schemas.models import (AccessRestrictionGroupSchema, AccessRestrictionSchema, AreaSchema,
BuildingSchema, ColumnSchema, CrossDescriptionSchema, DoorSchema,
@ -143,6 +143,7 @@ class MapdataAPIBuilder:
call_func=mapdata_retrieve_endpoint,
add_call_params={"model": endpoint.model.__name__, "pk": id_field}
)
list_func.__name__ = f"{endpoint.model_name}_by_id"
self.router.get(f'/{endpoint.model_name_plural}/{{{id_field}}}/', summary=f"{endpoint.model_name} by ID",
tags=[f"mapdata-{tag}"], description=schema_description(endpoint.schema),
@ -195,6 +196,15 @@ mapdata_endpoints: dict[str, list[MapdataEndpoint]] = {
model=DynamicLocation,
schema=DynamicLocationSchema,
),
MapdataEndpoint(
model=DataOverlay,
schema=DataOverlaySchema,
),
MapdataEndpoint(
model=DataOverlayFeature,
schema=DataOverlayFeatureSchema,
filters=ByOverlayFilter,
),
],
"level": [
MapdataEndpoint(
@ -269,35 +279,3 @@ mapdata_endpoints: dict[str, list[MapdataEndpoint]] = {
MapdataAPIBuilder(router=mapdata_api_router).build_all_endpoints(mapdata_endpoints)
"""
Data overlays
"""
# todo: this wants to move into a MapDataEndpoint
@mapdata_api_router.get('/overlays/', summary="data overlay list",
tags=["mapdata-root"], description=schema_description(DynamicLocationSchema),
response={200: list[DataOverlaySchema], **auth_responses})
@api_etag()
def dataoverlay_list(request):
return mapdata_list_endpoint(request, model=DataOverlay)
# todo: this wants to move into a MapDataEndpoint
@mapdata_api_router.get('/overlays/{overlay_id}/', summary="features for overlay by overlay ID",
tags=["mapdata-root"], description=schema_description(DynamicLocationSchema),
response={200: list[DataOverlayFeatureSchema], **API404.dict(), **auth_responses})
# @api_etag()
def dataoverlay_by_id(request, overlay_id: int):
qs = optimize_query(
DataOverlayFeature.qs_for_request(request)
)
qs = qs.filter(overlay_id=overlay_id)
# order_by
qs = qs.order_by('pk')
return qs

View file

@ -21,11 +21,11 @@ class DataOverlay(TitledMixin, models.Model):
verbose_name=_('headers for pull http request (JSON object)'))
pull_interval = models.DurationField(blank=True, null=True, verbose_name=_('pull interval'))
class Meta:
verbose_name = _('Data Overlay')
verbose_name_plural = _('Data Overlays')
default_related_name = 'data_overlays'
default_related_name = 'dataoverlays'
class DataOverlayFeature(TitledMixin, GeometryMixin, models.Model):
overlay = models.ForeignKey('mapdata.DataOverlay', on_delete=models.CASCADE, verbose_name=_('Overlay'), related_name='features')
@ -45,7 +45,6 @@ class DataOverlayFeature(TitledMixin, GeometryMixin, models.Model):
extra_data: Optional[dict[str, str]] = SchemaField(schema=dict[str, str], blank=True, null=True, default=None,
verbose_name=_('extra data (JSON object)'))
def to_geojson(self, instance=None) -> dict:
result = {
'type': 'Feature',
@ -65,7 +64,6 @@ class DataOverlayFeature(TitledMixin, GeometryMixin, models.Model):
def get_geojson_key(self):
return 'dataoverlayfeature', self.id
def _serialize(self, **kwargs):
result = super()._serialize(**kwargs)
result.update({
@ -84,4 +82,4 @@ class DataOverlayFeature(TitledMixin, GeometryMixin, models.Model):
return result
class Meta:
default_related_name = "overlayfeatures"
default_related_name = "dataoverlayfeatures"

View file

@ -136,6 +136,23 @@ class ByOnTopOfFilter(FilterSchema):
return super().filter_qs(qs)
class ByOverlayFilter(FilterSchema):
overlay: int = APIField(
title='filter by data overlay',
description='only show overlay features belonging to this overlay'
)
def validate(self, request):
super().validate(request)
if self.overlay is not None:
assert_valid_value(request, Level, "pk", {self.overlay})
def filter_qs(self, qs: QuerySet) -> QuerySet:
if self.overlay is not None:
qs = qs.filter(overlay=self.overlay)
return super().filter_qs(qs)
class BySearchableFilter(FilterSchema):
searchable: bool = APIField(
False,

View file

@ -319,11 +319,16 @@ class DynamicLocationSchema(SpecificLocationSchema, DjangoModelSchema):
class DataOverlaySchema(TitledSchema, DjangoModelSchema):
# TODO
"""
Represents a collection of geometries to be displayed as an optional overlay to the map.
"""
pass
class DataOverlayFeatureSchema(TitledSchema, DjangoModelSchema):
"""
A feature (any kind of geometry) to be displayed as part of a data overlay.
"""
geometry: AnyGeometrySchema
level_id: PositiveInt
stroke_color: Optional[str]
@ -336,7 +341,6 @@ class DataOverlayFeatureSchema(TitledSchema, DjangoModelSchema):
external_url: Optional[str]
extra_data: Optional[dict[str, str]]
# TODO
pass
class SourceSchema(WithAccessRestrictionSchema, DjangoModelSchema):

View file

@ -2841,7 +2841,7 @@ class DataOverlay {
}
async create() {
const features = await c3nav_api.get(`mapdata/overlays/${this.id}/`);
const features = await c3nav_api.get(`mapdata/dataoverlayfeatures/?overlay=${this.id}`);
const levels = {};
for (const feature of features) {