diff --git a/geotrek/common/filters.py b/geotrek/common/filters.py index 5b62d48ce0..78c5a0bc8f 100644 --- a/geotrek/common/filters.py +++ b/geotrek/common/filters.py @@ -1,6 +1,9 @@ from django.utils.translation import gettext_lazy as _ +from django_filters import ModelMultipleChoiceFilter, RangeFilter +from mapentity.filters import MapEntityFilterSet + +from geotrek.common.models import HDViewPoint -from django_filters import RangeFilter, ModelMultipleChoiceFilter from .fields import OneLineRangeField @@ -27,3 +30,10 @@ def get_queryset(self, request=None): if self.queryset is not None: return self.queryset return self.model.objects.all() + + +class HDViewPointFilterSet(MapEntityFilterSet): + + class Meta(MapEntityFilterSet.Meta): + model = HDViewPoint + fields = ['title'] diff --git a/geotrek/common/migrations/0029_hdviewpoint.py b/geotrek/common/migrations/0029_hdviewpoint.py index b0389f613e..1a68f39539 100644 --- a/geotrek/common/migrations/0029_hdviewpoint.py +++ b/geotrek/common/migrations/0029_hdviewpoint.py @@ -1,9 +1,9 @@ -# Generated by Django 3.2.16 on 2022-12-19 10:04 +# Generated by Django 3.2.16 on 2022-12-20 16:02 +from django.conf import settings import django.contrib.gis.db.models.fields from django.db import migrations, models import django.db.models.deletion -from django.conf import settings import uuid diff --git a/geotrek/common/models.py b/geotrek/common/models.py index 0af537f928..7c00d633d5 100755 --- a/geotrek/common/models.py +++ b/geotrek/common/models.py @@ -3,7 +3,6 @@ from colorfield.fields import ColorField from django.conf import settings -from django.contrib import auth from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.contrib.gis.db import models as gis_models @@ -13,14 +12,17 @@ from django.urls import reverse from django.utils.http import urlencode from django.utils.translation import gettext_lazy as _ +from mapentity.models import MapEntityMixin from paperclip.models import Attachment as BaseAttachment from paperclip.models import FileType as BaseFileType from paperclip.models import License as BaseLicense from PIL import Image from geotrek.authent.models import StructureOrNoneRelated + from .managers import AccessibilityAttachmentManager -from .mixins.models import OptionalPictogramMixin, PictogramMixin, TimeStampedModelMixin +from .mixins.models import (OptionalPictogramMixin, PictogramMixin, + TimeStampedModelMixin) def attachment_accessibility_upload(instance, filename): @@ -272,7 +274,7 @@ class Meta: abstract = True -class HDViewPoint(TimeStampedModelMixin): +class HDViewPoint(TimeStampedModelMixin, MapEntityMixin): picture = models.FileField(verbose_name=_("Picture"), upload_to="hdviewpoints/") geom = gis_models.PointField(verbose_name=_("Location"), srid=settings.SRID) @@ -305,28 +307,10 @@ class Meta: def __str__(self): return self.title - @property - def structure(self): - return self.content_object.structure - - def same_structure(self, user): - """ Returns True if the user is in the same structure or has - bypass_structure permission, False otherwise. """ - return (user.profile.structure == self.structure - or user.is_superuser - or user.has_perm('authent.can_bypass_structure')) - @property def full_url(self): return reverse('common:hdviewpoint_detail', kwargs={'pk': self.pk}) - def get_absolute_url(self): - return self.full_url - - @classmethod - def get_add_url(cls): - return reverse('common:hdviewpoint_add') - @classmethod def get_list_url(cls): return reverse('admin:common_hdviewpoint_changelist') @@ -339,67 +323,9 @@ def get_generic_picture_tile_url(self): url = self.get_picture_tile_url(0, 0, 0).replace("/0/0/0.png", "/{z}/{x}/{y}.png") return url - def get_layer_detail_url(self): - return reverse("{app_name}:{model_name}-drf-detail".format(app_name=self._meta.app_label.lower(), - model_name=self._meta.model_name.lower()), - kwargs={"format": "geojson", "pk": self.pk}) - - def get_detail_url(self): - return reverse('common:hdviewpoint_detail', args=[self.pk]) - @property def thumbnail_url(self): return reverse('common:hdviewpoint-thumbnail', kwargs={'pk': self.pk, 'fmt': 'png'}) - def get_update_url(self): - return reverse('common:hdviewpoint_change', args=[self.pk]) - def get_annotate_url(self): return reverse('common:hdviewpoint_annotate', args=[self.pk]) - - def get_delete_url(self): - return reverse('common:hdviewpoint_delete', args=[self.pk]) - - @classmethod - def get_permission_codename(cls, entity_kind): - operations = { - 'update': 'change', - 'update_geom': 'change_geom', - 'detail': 'read', - 'layer': 'read', - 'list': 'read', - '-drf-list': 'read', - 'markup': 'read', - } - perm = operations.get(entity_kind, entity_kind) - opts = cls._meta - appname = opts.app_label.lower() - return '%s.%s' % (appname, auth.get_permission_codename(perm, opts)) - - @classmethod - def get_content_type_id(cls): - return ContentType.objects.get_for_model(cls).pk - - def get_geom(self): - return self.geom - - def get_map_image_extent(self, srid=settings.API_SRID): - obj = self.geom - obj.transform(srid) - return obj.extent - - @classmethod - def get_create_label(cls): - return _("Add a new HD view") - - @property - def icon_small(self): - return 'images/hdviewpoint-16.png' - - @property - def icon_big(self): - return 'images/hdviewpoint-96.png' - - @property - def modelname(self): - return self._meta.model_name diff --git a/geotrek/common/serializers.py b/geotrek/common/serializers.py index 8b424bf1f4..342f9a6585 100644 --- a/geotrek/common/serializers.py +++ b/geotrek/common/serializers.py @@ -1,13 +1,16 @@ from django.conf import settings -from django.urls import reverse from django.db import models as django_db_models from django.shortcuts import get_object_or_404 +from django.urls import reverse from django.utils.translation import get_language +from mapentity.serializers import MapentityGeojsonModelSerializer from rest_framework import serializers as rest_serializers -from rest_framework_gis.fields import GeometryField +from rest_framework_gis.fields import (GeometryField, + GeometrySerializerMethodField) from rest_framework_gis.serializers import GeoFeatureModelSerializer -from .models import HDViewPoint, Theme, RecordSource, TargetPortal, FileType, Attachment, Label +from .models import (Attachment, FileType, HDViewPoint, Label, RecordSource, + TargetPortal, Theme) class TranslatedModelSerializer(rest_serializers.ModelSerializer): @@ -94,7 +97,7 @@ class Meta: fields = ('id', 'pictogram', 'name', 'advice', 'filter_rando') -class HDViewPointAPISerializer(TranslatedModelSerializer): +class HDViewPointSerializer(TranslatedModelSerializer): class Meta: model = HDViewPoint fields = ( @@ -102,6 +105,23 @@ class Meta: ) +class HDViewPointGeoJSONSerializer(MapentityGeojsonModelSerializer): + api_geom = GeometrySerializerMethodField() + + def get_api_geom(self, obj): + return obj.geom.transform(4326, clone=True) + + class Meta(MapentityGeojsonModelSerializer.Meta): + model = HDViewPoint + fields = ('id', 'title') + + +class HDViewPointAPISerializer(HDViewPointSerializer): + class Meta(HDViewPointSerializer.Meta): + id_field = 'id' + fields = HDViewPointSerializer.Meta.fields + + class HDViewPointAPIGeoJSONSerializer(GeoFeatureModelSerializer, HDViewPointAPISerializer): # Annotated geom field with API_SRID api_geom = GeometryField(read_only=True, precision=7) diff --git a/geotrek/common/static/common/style.css b/geotrek/common/static/common/style.css index 780727644b..a17ca2b29b 100644 --- a/geotrek/common/static/common/style.css +++ b/geotrek/common/static/common/style.css @@ -285,11 +285,6 @@ fieldset { visibility: visible; opacity: 1; } -li.history.hdviewpoint > a, -li.history.hdviewpoint button:first-child { - background: url(/static/images/hdviewpoint-16.png) no-repeat; - background-position: left center; -} .form-actions { z-index: 1000; } \ No newline at end of file diff --git a/geotrek/common/templates/common/hdviewpoint_detail.html b/geotrek/common/templates/common/hdviewpoint_detail.html index 01ed4650b0..bd7a8d180b 100644 --- a/geotrek/common/templates/common/hdviewpoint_detail.html +++ b/geotrek/common/templates/common/hdviewpoint_detail.html @@ -4,17 +4,6 @@ {% block head %} {% block title %}{{ object }} | {{ block.super }}{% endblock title %} {{ block.super }} - {% endblock head %} {% block mainpanel %}
diff --git a/geotrek/common/templates/common/hdviewpoint_detail_fragment.html b/geotrek/common/templates/common/hdviewpoint_detail_fragment.html index e4a0654140..94cdf1eaaf 100644 --- a/geotrek/common/templates/common/hdviewpoint_detail_fragment.html +++ b/geotrek/common/templates/common/hdviewpoint_detail_fragment.html @@ -51,7 +51,7 @@

{% trans "HD Views" %} {% if perms.common.change_hdviewpoint %}   - + {% trans "Update" %} {% endif %} diff --git a/geotrek/common/tests/test_models.py b/geotrek/common/tests/test_models.py index 188bfb7f5c..d3b1fd4416 100644 --- a/geotrek/common/tests/test_models.py +++ b/geotrek/common/tests/test_models.py @@ -60,7 +60,7 @@ class HDViewPointTestCase(TestCase): def setUpTestData(cls): structure = StructureFactory(name="MyStructure") cls.trek = TrekFactory(structure=structure) - cls.vp = HDViewPointFactory(content_object=cls.trek) + cls.vp = HDViewPointFactory(content_object=cls.trek, title='Panorama') cls.user = UserFactory() UserProfileFactory(structure=structure, user=cls.user) @@ -74,9 +74,6 @@ def test_tiles_url(self): self.vp.get_generic_picture_tile_url(), f"/api/hdviewpoint/drf/hdviewpoints/{self.vp.pk}/tiles/{{z}}/{{x}}/{{y}}.png?source=vips" ) - def test_icons(self): - self.assertIn('hdviewpoint-16.png', self.vp.icon_small) - self.assertIn('hdviewpoint-96.png', self.vp.icon_big) - - def test_same_structure(self): - self.assertTrue(self.vp.same_structure(self.user)) + def test_properties(self): + self.assertEqual(str(self.vp), 'Panorama') + self.assertIn('admin/', self.vp.get_list_url()) diff --git a/geotrek/common/tests/test_views.py b/geotrek/common/tests/test_views.py index 505c1c3c23..07d555c95f 100644 --- a/geotrek/common/tests/test_views.py +++ b/geotrek/common/tests/test_views.py @@ -412,8 +412,8 @@ def setUpTestData(cls): cls.license = LicenseFactory() # Create user with proper permissions cls.user_perm = UserFactory.create() - read_perm = Permission.objects.get(codename="read_hdviewpoint") add_perm = Permission.objects.get(codename="add_hdviewpoint") + read_perm = Permission.objects.get(codename="read_hdviewpoint") update_perm = Permission.objects.get(codename="change_hdviewpoint") delete_perm = Permission.objects.get(codename="delete_hdviewpoint") cls.user_perm.user_permissions.add(add_perm, read_perm, update_perm, delete_perm) @@ -470,7 +470,7 @@ def test_crud_view(self): 'geom': "SRID=2154;POINT(0 0)" } response = self.client.post(vp.get_update_url(), data) - self.assertRedirects(response, f"/hdviewpoint/{vp.pk}", status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True) + self.assertRedirects(response, f"/hdviewpoint/{vp.pk}/", status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True) vp = HDViewPoint.objects.first() self.assertEqual(vp.legend, "Something else") self.assertEqual(vp.license, None) diff --git a/geotrek/common/urls.py b/geotrek/common/urls.py index 0abffdd1ea..42a162678b 100644 --- a/geotrek/common/urls.py +++ b/geotrek/common/urls.py @@ -1,10 +1,10 @@ from django.urls import path, converters, register_converter -from mapentity.registry import MapEntityOptions +from geotrek.common.models import HDViewPoint +from mapentity.registry import MapEntityOptions, registry from rest_framework.routers import DefaultRouter -from .views import (HDViewPointAPIViewSet, HDViewPointAnnotate, HDViewPointCreate, HDViewPointDetail, HDViewPointUpdate, HDViewPointDelete, - TiledHDViewPointViewSet, JSSettings, DocumentPublic, DocumentBookletPublic, import_view, - import_update_json, ThemeViewSet, MarkupPublic, sync_view, sync_update_json, SyncRandoRedirect, +from .views import (HDViewPointAnnotate, TiledHDViewPointViewSet, JSSettings, DocumentPublic, DocumentBookletPublic, + import_view, import_update_json, ThemeViewSet, MarkupPublic, sync_view, sync_update_json, SyncRandoRedirect, CheckExtentsView) @@ -15,8 +15,6 @@ class LangConverter(converters.StringConverter): register_converter(LangConverter, 'lang') app_name = 'common' - - urlpatterns = [ path('api/settings.json', JSSettings.as_view(), name='settings_json'), path('tools/extents/', CheckExtentsView.as_view(), name='check_extents'), @@ -26,17 +24,12 @@ class LangConverter(converters.StringConverter): path('commands/syncview', sync_view, name='sync_randos_view'), path('commands/statesync/', sync_update_json, name='sync_randos_state'), path('api//themes.json', ThemeViewSet.as_view({'get': 'list'}), name="themes_json"), - path('hdviewpoint/add', HDViewPointCreate.as_view(), name="hdviewpoint_add"), - path('hdviewpoint/', HDViewPointDetail.as_view(), name="hdviewpoint_detail"), - path('hdviewpoint/edit/', HDViewPointUpdate.as_view(), name="hdviewpoint_change"), - path('hdviewpoint/delete/', HDViewPointDelete.as_view(), name="hdviewpoint_delete"), path('hdviewpoint/annotate/', HDViewPointAnnotate.as_view(), name="hdviewpoint_annotate"), ] rest_router = DefaultRouter(trailing_slash=False) -rest_router.register(r'api/hdviewpoint/drf/hdviewpoints', - HDViewPointAPIViewSet, basename="hdviewpoint-drf") rest_router.register(r'api/hdviewpoint/drf/hdviewpoints', TiledHDViewPointViewSet) +urlpatterns += registry.register(HDViewPoint, menu=False) urlpatterns += rest_router.urls diff --git a/geotrek/common/views.py b/geotrek/common/views.py index d556d3d6fe..d6088b23fa 100644 --- a/geotrek/common/views.py +++ b/geotrek/common/views.py @@ -10,18 +10,19 @@ from django.apps import apps from django.conf import settings from django.contrib import messages -from django.contrib.gis.db.models.functions import Transform from django.contrib.admin.models import CHANGE, LogEntry from django.contrib.auth.decorators import (login_required, permission_required, user_passes_test) from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.gis.db.models import Extent, GeometryField +from django.contrib.gis.db.models.functions import Transform from django.core.exceptions import PermissionDenied from django.db.models import Q from django.db.models.functions import Cast -from django.http import JsonResponse, Http404, HttpResponse, HttpResponseRedirect -from django.shortcuts import get_object_or_404, render, redirect +from django.http import (Http404, HttpResponse, HttpResponseRedirect, + JsonResponse) +from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone, translation from django.utils.decorators import method_decorator @@ -29,12 +30,14 @@ from django.utils.translation import gettext as _ from django.views import static from django.views.decorators.http import require_http_methods, require_POST -from django.views.generic import RedirectView, TemplateView, View, UpdateView +from django.views.generic import RedirectView, TemplateView, UpdateView, View from django_celery_results.models import TaskResult from django_large_image.rest import LargeImageFileDetailMixin +from geotrek.common.filters import HDViewPointFilterSet from mapentity import views as mapentity_views from mapentity.helpers import api_bbox from mapentity.registry import app_settings, registry +from mapentity.views import MapEntityList from paperclip import settings as settings_paperclip from paperclip.views import _handle_attachment_form from rest_framework import mixins @@ -44,22 +47,27 @@ from geotrek import __version__ from geotrek.celery import app as celery_app from geotrek.common.mixins.api import APIViewSet +from geotrek.common.viewsets import GeotrekMapentityViewSet from geotrek.feedback.parsers import SuricateParser -from .forms import (AttachmentAccessibilityForm, HDViewPointAnnotationForm, HDViewPointForm, - ImportDatasetForm, ImportDatasetFormWithFile, - ImportSuricateForm, SyncRandoForm) +from ..altimetry.models import Dem +from ..core.models import Path +from .forms import (AttachmentAccessibilityForm, HDViewPointAnnotationForm, + HDViewPointForm, ImportDatasetForm, + ImportDatasetFormWithFile, ImportSuricateForm, + SyncRandoForm) from .mixins.views import (BookletMixin, CompletenessMixin, DocumentPortalMixin, DocumentPublicMixin, MetaMixin) from .models import AccessibilityAttachment, HDViewPoint, TargetPortal, Theme from .permissions import PublicOrReadPermMixin, RelatedPublishedPermission from .serializers import (HDViewPointAPIGeoJSONSerializer, - HDViewPointAPISerializer, ThemeSerializer) + HDViewPointAPISerializer, + HDViewPointGeoJSONSerializer, HDViewPointSerializer, + ThemeSerializer) from .tasks import import_datas, import_datas_from_web, launch_sync_rando from .utils import leaflet_bounds -from .utils.import_celery import create_tmp_destination, discover_available_parsers -from ..altimetry.models import Dem -from ..core.models import Path +from .utils.import_celery import (create_tmp_destination, + discover_available_parsers) class Meta(MetaMixin, TemplateView): @@ -325,6 +333,25 @@ def get(request, *args, **kwargs): return JsonResponse(response) +class HDViewPointList(MapEntityList): + queryset = HDViewPoint.objects.all() + filterform = HDViewPointFilterSet + columns = ['id', 'title'] + + +class HDViewPointViewSet(GeotrekMapentityViewSet): + model = HDViewPoint + serializer_class = HDViewPointSerializer + geojson_serializer_class = HDViewPointGeoJSONSerializer + mapentity_list_class = HDViewPointList + + def get_queryset(self): + qs = self.model.objects.all() + if self.format_kwarg == 'geojson': + qs = qs.only('id', 'title') + return qs + + class HDViewPointAPIViewSet(APIViewSet): model = HDViewPoint serializer_class = HDViewPointAPISerializer