From 707c81f159b6f635e82941bf9961467165500d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Tue, 3 Dec 2024 09:59:51 +0100 Subject: [PATCH] integrate data overlay api into new MapdataAPIBuilder --- src/c3nav/mapdata/api/mapdata.py | 44 +++++++------------------- src/c3nav/mapdata/models/overlay.py | 8 ++--- src/c3nav/mapdata/schemas/filters.py | 17 ++++++++++ src/c3nav/mapdata/schemas/models.py | 8 +++-- src/c3nav/site/static/site/js/c3nav.js | 2 +- 5 files changed, 38 insertions(+), 41 deletions(-) diff --git a/src/c3nav/mapdata/api/mapdata.py b/src/c3nav/mapdata/api/mapdata.py index 6f329411..1c6a3992 100644 --- a/src/c3nav/mapdata/api/mapdata.py +++ b/src/c3nav/mapdata/api/mapdata.py @@ -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 diff --git a/src/c3nav/mapdata/models/overlay.py b/src/c3nav/mapdata/models/overlay.py index ad0b66ea..8da3c3bc 100644 --- a/src/c3nav/mapdata/models/overlay.py +++ b/src/c3nav/mapdata/models/overlay.py @@ -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" \ No newline at end of file + default_related_name = "dataoverlayfeatures" diff --git a/src/c3nav/mapdata/schemas/filters.py b/src/c3nav/mapdata/schemas/filters.py index 6395bd81..6ebe94fd 100644 --- a/src/c3nav/mapdata/schemas/filters.py +++ b/src/c3nav/mapdata/schemas/filters.py @@ -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, diff --git a/src/c3nav/mapdata/schemas/models.py b/src/c3nav/mapdata/schemas/models.py index 5539d683..6aeb9566 100644 --- a/src/c3nav/mapdata/schemas/models.py +++ b/src/c3nav/mapdata/schemas/models.py @@ -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): diff --git a/src/c3nav/site/static/site/js/c3nav.js b/src/c3nav/site/static/site/js/c3nav.js index 19102990..a1b46569 100644 --- a/src/c3nav/site/static/site/js/c3nav.js +++ b/src/c3nav/site/static/site/js/c3nav.js @@ -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) {