#########################################################################
#
# Copyright (C) 2021 OSGeo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#########################################################################
import logging
from uuid import uuid4
from django.db import transaction
from drf_spectacular.utils import extend_schema
from dynamic_rest.filters import DynamicFilterBackend, DynamicSortingFilter
from dynamic_rest.viewsets import DynamicModelViewSet
from oauth2_provider.contrib.rest_framework import OAuth2Authentication
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.response import Response
from geonode.base import register_event
from geonode.base.api.filters import DynamicSearchFilter, ExtentFilter
from geonode.base.api.pagination import GeoNodeApiPagination
from geonode.base.api.permissions import UserHasPerms
from geonode.layers.api.serializers import DatasetSerializer
from geonode.maps.api.exception import GeneralMapsException
from geonode.maps.api.permissions import MapPermissionsFilter
from geonode.maps.api.serializers import MapLayerSerializer, MapSerializer
from geonode.maps.contants import _PERMISSION_MSG_SAVE
from geonode.maps.models import Map
from geonode.maps.signals import map_changed_signal
from geonode.monitoring.models import EventType
from geonode.resource.manager import resource_manager
from geonode.utils import resolve_object
[docs]
logger = logging.getLogger(__name__)
[docs]
class MapViewSet(DynamicModelViewSet):
"""
API endpoint that allows maps to be viewed or edited.
"""
[docs]
http_method_names = ["get", "patch", "post", "put"]
[docs]
authentication_classes = [SessionAuthentication, BasicAuthentication, OAuth2Authentication]
[docs]
permission_classes = [
IsAuthenticatedOrReadOnly,
UserHasPerms(perms_dict={"default": {"POST": ["base.add_resourcebase"]}}),
]
[docs]
filter_backends = [
DynamicFilterBackend,
DynamicSortingFilter,
DynamicSearchFilter,
ExtentFilter,
MapPermissionsFilter,
]
[docs]
queryset = Map.objects.all().order_by("-created")
[docs]
serializer_class = MapSerializer
[docs]
def list(self, request, *args, **kwargs):
# Avoid overfetching removing mapslayer of the list.
request.query_params.add("exclude[]", "maplayers")
return super(MapViewSet, self).list(request, *args, **kwargs)
@transaction.atomic
[docs]
def update(self, request, *args, **kwargs):
"""
Changes in the m2m `maplayers` are committed before object changes.
To protect the db, this action is done within an atomic tansation.
"""
return super(MapViewSet, self).update(request, *args, **kwargs)
@transaction.atomic
[docs]
def create(self, request, *args, **kwargs):
"""
Changes in the m2m `maplayers` are committed before object changes.
To protect the db, this action is done within an atomic tansation.
"""
return super(MapViewSet, self).create(request, *args, **kwargs)
@extend_schema(
methods=["get"],
responses={200: MapLayerSerializer(many=True)},
description="API endpoint allowing to retrieve the MapLayers list.",
)
@action(detail=True, methods=["get"])
[docs]
def maplayers(self, request, pk=None, *args, **kwargs):
map = self.get_object()
resources = map.maplayers
return Response(MapLayerSerializer(embed=True, many=True).to_representation(resources))
@extend_schema(
methods=["get"],
responses={200: DatasetSerializer(many=True)},
description="API endpoint allowing to retrieve the local MapLayers.",
)
@action(detail=True, methods=["get"])
[docs]
def datasets(self, request, pk=None, *args, **kwargs):
map = self.get_object()
resources = map.datasets
return Response(DatasetSerializer(embed=True, many=True).to_representation(resources))
[docs]
def _post_change_routines(self, instance: Map, create_action_perfomed: bool, additional_data: dict):
# Step 1: Handle Maplayers signals if this is and update action
if not create_action_perfomed:
dataset_names_before_changes = additional_data.pop("dataset_names_before_changes", [])
dataset_names_after_changes = [lyr.alternate for lyr in instance.datasets]
if dataset_names_before_changes != dataset_names_after_changes:
map_changed_signal.send_robust(sender=instance, what_changed="datasets")
# Step 2: Register Event
event_type = EventType.EVENT_CREATE if create_action_perfomed else EventType.EVENT_CHANGE
register_event(self.request, event_type, instance)
# Step 3: Resource Manager
resource_manager.update(instance.uuid, instance=instance, notify=True)