overlay feature update api
This commit is contained in:
parent
f656ad7a3a
commit
0e19ce5dac
2 changed files with 109 additions and 5 deletions
|
@ -1,18 +1,20 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional, Sequence, Type, Callable, Any
|
from typing import Optional, Sequence, Type, Callable, Any
|
||||||
|
|
||||||
from django.db.models import Model
|
from django.db import transaction
|
||||||
|
from django.db.models import Model, F
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
from ninja import Query
|
from ninja import Query
|
||||||
from ninja import Router as APIRouter
|
from ninja import Router as APIRouter
|
||||||
from pydantic import PositiveInt
|
from pydantic import PositiveInt
|
||||||
|
|
||||||
from c3nav.api.auth import auth_responses, validate_responses
|
from c3nav.api.auth import auth_responses, validate_responses, auth_permission_responses
|
||||||
from c3nav.api.exceptions import API404
|
from c3nav.api.exceptions import API404, APIPermissionDenied
|
||||||
from c3nav.api.schema import BaseSchema
|
from c3nav.api.schema import BaseSchema
|
||||||
from c3nav.mapdata.api.base import api_etag, optimize_query, can_access_geometry
|
from c3nav.mapdata.api.base import api_etag, optimize_query, can_access_geometry
|
||||||
from c3nav.mapdata.models import (Area, Building, Door, Hole, Level, LocationGroup, LocationGroupCategory, Source,
|
from c3nav.mapdata.models import (Area, Building, Door, Hole, Level, LocationGroup, LocationGroupCategory, Source,
|
||||||
Space, Stair, DataOverlay, DataOverlayFeature, WayType)
|
Space, Stair, DataOverlay, DataOverlayFeature, WayType)
|
||||||
from c3nav.mapdata.models.access import AccessRestriction, AccessRestrictionGroup
|
from c3nav.mapdata.models.access import AccessRestriction, AccessRestrictionGroup, AccessPermission
|
||||||
from c3nav.mapdata.models.geometry.space import (POI, Column, CrossDescription, LeaveDescription, LineObstacle,
|
from c3nav.mapdata.models.geometry.space import (POI, Column, CrossDescription, LeaveDescription, LineObstacle,
|
||||||
Obstacle, Ramp)
|
Obstacle, Ramp)
|
||||||
from c3nav.mapdata.models.locations import DynamicLocation, LabelSettings, LocationRedirect
|
from c3nav.mapdata.models.locations import DynamicLocation, LabelSettings, LocationRedirect
|
||||||
|
@ -25,7 +27,8 @@ from c3nav.mapdata.schemas.models import (AccessRestrictionGroupSchema, AccessRe
|
||||||
LineObstacleSchema, LocationGroupCategorySchema, LocationGroupSchema,
|
LineObstacleSchema, LocationGroupCategorySchema, LocationGroupSchema,
|
||||||
ObstacleSchema, POISchema, RampSchema, SourceSchema, SpaceSchema, StairSchema,
|
ObstacleSchema, POISchema, RampSchema, SourceSchema, SpaceSchema, StairSchema,
|
||||||
DataOverlaySchema, DataOverlayFeatureSchema, LocationRedirectSchema,
|
DataOverlaySchema, DataOverlayFeatureSchema, LocationRedirectSchema,
|
||||||
WayTypeSchema)
|
WayTypeSchema,
|
||||||
|
DataOverlayFeatureUpdateSchema, DataOverlayFeatureBulkUpdateSchema)
|
||||||
|
|
||||||
mapdata_api_router = APIRouter(tags=["mapdata"])
|
mapdata_api_router = APIRouter(tags=["mapdata"])
|
||||||
|
|
||||||
|
@ -303,3 +306,60 @@ mapdata_endpoints: dict[str, list[MapdataEndpoint]] = {
|
||||||
|
|
||||||
|
|
||||||
MapdataAPIBuilder(router=mapdata_api_router).build_all_endpoints(mapdata_endpoints)
|
MapdataAPIBuilder(router=mapdata_api_router).build_all_endpoints(mapdata_endpoints)
|
||||||
|
|
||||||
|
|
||||||
|
@mapdata_api_router.post('/dataoverlayfeatures/{id}', summary="update a data overlay feature (including geometries)",
|
||||||
|
response={204: None, **API404.dict(), **auth_permission_responses})
|
||||||
|
def update_data_overlay_feature(request, id: int, parameters: DataOverlayFeatureUpdateSchema):
|
||||||
|
"""
|
||||||
|
update the data overlay feature
|
||||||
|
"""
|
||||||
|
|
||||||
|
feature = get_object_or_404(DataOverlayFeature, id=id)
|
||||||
|
|
||||||
|
if feature.overlay.edit_access_restriction_id is None or feature.overlay.edit_access_restriction_id not in AccessPermission.get_for_request(request):
|
||||||
|
raise APIPermissionDenied('You are not allowed to edit this object.')
|
||||||
|
|
||||||
|
updates = parameters.dict(exclude_unset=True)
|
||||||
|
|
||||||
|
for key, value in updates.items():
|
||||||
|
setattr(feature, key, value)
|
||||||
|
|
||||||
|
feature.save()
|
||||||
|
|
||||||
|
return 204, None
|
||||||
|
|
||||||
|
@mapdata_api_router.post('/dataoverlayfeatures-bulk', summary="bulk-update data overlays (including geometries)",
|
||||||
|
response={204: None, **API404.dict(), **auth_permission_responses})
|
||||||
|
def update_data_overlay_features_bulk(request, parameters: DataOverlayFeatureBulkUpdateSchema):
|
||||||
|
|
||||||
|
permissions = AccessPermission.get_for_request(request)
|
||||||
|
|
||||||
|
updates = {
|
||||||
|
u.id: u
|
||||||
|
for u in parameters.updates
|
||||||
|
}
|
||||||
|
|
||||||
|
forbidden_object_ids = []
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
features = DataOverlayFeature.objects.filter(id__in=updates.keys()).annotate(edit_access_restriction_id=F('overlay__edit_access_restriction_id'))
|
||||||
|
|
||||||
|
for feature in features:
|
||||||
|
if feature.edit_access_restriction_id is None or feature.edit_access_restriction_id not in permissions:
|
||||||
|
forbidden_object_ids.append(feature.id)
|
||||||
|
continue
|
||||||
|
|
||||||
|
changes = updates[feature.id].dict(exclude_unset=True)
|
||||||
|
|
||||||
|
for key, value in changes.items():
|
||||||
|
if key == 'id':
|
||||||
|
continue
|
||||||
|
setattr(feature, key, value)
|
||||||
|
feature.save()
|
||||||
|
|
||||||
|
if len(forbidden_object_ids) > 0:
|
||||||
|
raise APIPermissionDenied('You are not allowed to edit the objects with the following ids: %s.'
|
||||||
|
% ", ".join([str(x) for x in forbidden_object_ids]))
|
||||||
|
|
||||||
|
return 204, None
|
|
@ -397,6 +397,50 @@ class DataOverlayFeatureSchema(TitledSchema, WithGeometrySchema, DjangoModelSche
|
||||||
external_url: Optional[str]
|
external_url: Optional[str]
|
||||||
extra_data: Optional[dict[str, str | int | float]]
|
extra_data: Optional[dict[str, str | int | float]]
|
||||||
|
|
||||||
|
class DataOverlayFeatureUpdateSchema(BaseSchema):
|
||||||
|
"""
|
||||||
|
An update to a data overlay feature.
|
||||||
|
"""
|
||||||
|
level_id: Optional[PositiveInt] = None
|
||||||
|
geometry: Optional[AnyGeometrySchema] = None
|
||||||
|
stroke_color: Optional[str] = None
|
||||||
|
stroke_width: Optional[float] = None
|
||||||
|
stroke_opacity: Optional[float] = None
|
||||||
|
fill_color: Optional[str] = None
|
||||||
|
fill_opacity: Optional[float] = None
|
||||||
|
show_label: Optional[bool] = None
|
||||||
|
show_geometry: Optional[bool] = None
|
||||||
|
interactive: Optional[bool] = None
|
||||||
|
point_icon: Optional[str] = None
|
||||||
|
external_url: Optional[str] = None
|
||||||
|
extra_data: Optional[dict[str, str | int | float]] = None
|
||||||
|
|
||||||
|
class DataOverlayFeatureBulkUpdateItemSchema(BaseSchema):
|
||||||
|
"""
|
||||||
|
An item of a bulk update to data overlay features.
|
||||||
|
"""
|
||||||
|
id: PositiveInt
|
||||||
|
level_id: Optional[PositiveInt] = None
|
||||||
|
geometry: Optional[AnyGeometrySchema] = None
|
||||||
|
stroke_color: Optional[str] = None
|
||||||
|
stroke_width: Optional[float] = None
|
||||||
|
stroke_opacity: Optional[float] = None
|
||||||
|
fill_color: Optional[str] = None
|
||||||
|
fill_opacity: Optional[float] = None
|
||||||
|
show_label: Optional[bool] = None
|
||||||
|
show_geometry: Optional[bool] = None
|
||||||
|
interactive: Optional[bool] = None
|
||||||
|
point_icon: Optional[str] = None
|
||||||
|
external_url: Optional[str] = None
|
||||||
|
extra_data: Optional[dict[str, str | int | float]] = None
|
||||||
|
|
||||||
|
class DataOverlayFeatureBulkUpdateSchema(BaseSchema):
|
||||||
|
"""
|
||||||
|
A bulk update to data overlay features
|
||||||
|
"""
|
||||||
|
updates: list[DataOverlayFeatureBulkUpdateItemSchema]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WayTypeSchema(TitledSchema, DjangoModelSchema):
|
class WayTypeSchema(TitledSchema, DjangoModelSchema):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue