diff --git a/mvj/urls.py b/mvj/urls.py index 4e06a653..10bff833 100755 --- a/mvj/urls.py +++ b/mvj/urls.py @@ -112,6 +112,7 @@ AreaSearchViewSet, DirectReservationLinkViewSet, DirectReservationToFavourite, + FAQViewSet, FavouriteViewSet, GeneratePDF, InformationCheckViewSet, @@ -227,6 +228,7 @@ AreaSearchAttachmentViewset, basename="pub_area_search_attachment", ) +pub_router.register(r"faq", FAQViewSet, basename="pub_faq") pub_router.register(r"favourite", FavouriteViewSet, basename="pub_favourite") pub_router.register(r"form", FormViewSet, basename="pub_form") pub_router.register(r"intended_use", IntendedUsePSViewSet, basename="pub_intended_use") diff --git a/plotsearch/admin.py b/plotsearch/admin.py index 84cb3206..c15eeef3 100755 --- a/plotsearch/admin.py +++ b/plotsearch/admin.py @@ -12,6 +12,7 @@ PlotSearchSubtype, PlotSearchType, ) +from plotsearch.models.plot_search import FAQ class PlotSearchAdmin(FieldPermissionsAdminMixin, admin.ModelAdmin): @@ -32,9 +33,17 @@ class FavouriteAdmin(FieldPermissionsAdminMixin, admin.ModelAdmin): inlines = [FavouriteTargetInline] +class FAQAdmin(admin.ModelAdmin): + list_display = ( + "answer", + "question", + ) + + admin.site.register(PlotSearch, PlotSearchAdmin) admin.site.register(PlotSearchStage, NameAdmin) admin.site.register(Favourite, FavouriteAdmin) admin.site.register(PlotSearchType, NameAdmin) admin.site.register(AreaSearchIntendedUse, NameAdmin) admin.site.register(PlotSearchSubtype, NameAdmin) +admin.site.register(FAQ, FAQAdmin) diff --git a/plotsearch/migrations/0027_faq.py b/plotsearch/migrations/0027_faq.py new file mode 100644 index 00000000..a6573c65 --- /dev/null +++ b/plotsearch/migrations/0027_faq.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.13 on 2023-07-28 06:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("plotsearch", "0026_areasearchstatus"), + ] + + operations = [ + migrations.CreateModel( + name="FAQ", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("question", models.TextField(unique=True)), + ("answer", models.TextField()), + ], + ), + ] diff --git a/plotsearch/models/plot_search.py b/plotsearch/models/plot_search.py index 8f2883b8..8d115647 100755 --- a/plotsearch/models/plot_search.py +++ b/plotsearch/models/plot_search.py @@ -502,5 +502,10 @@ def get_external_url(self): return "{}/direct-reservation/{}".format(settings.PUBLIC_UI_URL, self.uuid) +class FAQ(models.Model): + question = models.TextField(unique=True) + answer = models.TextField() + + auditlog.register(PlotSearch) auditlog.register(InformationCheck) diff --git a/plotsearch/serializers/plot_search.py b/plotsearch/serializers/plot_search.py index e332bd65..d5dccb2f 100755 --- a/plotsearch/serializers/plot_search.py +++ b/plotsearch/serializers/plot_search.py @@ -39,6 +39,7 @@ ) from plotsearch.models.info_links import TargetInfoLink from plotsearch.models.plot_search import ( + FAQ, AreaSearchAttachment, AreaSearchStatus, AreaSearchStatusNote, @@ -1085,3 +1086,12 @@ def create(self, validated_data): ) return instance + + +class FAQSerializer(serializers.ModelSerializer): + class Meta: + model = FAQ + fields = ( + "question", + "answer", + ) diff --git a/plotsearch/tests/api/test_public.py b/plotsearch/tests/api/test_public.py new file mode 100644 index 00000000..e50cf29b --- /dev/null +++ b/plotsearch/tests/api/test_public.py @@ -0,0 +1,18 @@ +import pytest +from faker import Faker +from rest_framework.reverse import reverse + +from plotsearch.models.plot_search import FAQ + +fake = Faker("fi_FI") + + +@pytest.mark.django_db +def test_faq_list(client): + FAQ.objects.create(question=fake.name(), answer=fake.name()) + + url = reverse("pub_faq-list") + + response = client.get(path=url, content_type="application/json") + + assert response.status_code == 200 diff --git a/plotsearch/views/plot_search.py b/plotsearch/views/plot_search.py index 42ef2bf2..b13b3968 100755 --- a/plotsearch/views/plot_search.py +++ b/plotsearch/views/plot_search.py @@ -43,13 +43,18 @@ PlotSearchType, TargetStatus, ) -from plotsearch.models.plot_search import AreaSearchAttachment, DirectReservationLink +from plotsearch.models.plot_search import ( + FAQ, + AreaSearchAttachment, + DirectReservationLink, +) from plotsearch.permissions import AreaSearchAttachmentPermissions from plotsearch.serializers.plot_search import ( AreaSearchAttachmentSerializer, AreaSearchDetailSerializer, AreaSearchSerializer, DirectReservationLinkSerializer, + FAQSerializer, FavouriteSerializer, InformationCheckSerializer, IntendedUseSerializer, @@ -287,7 +292,6 @@ def render_to_response( class DirectReservationToFavourite(APIView): - permission_classes = (IsAuthenticated,) def get(self, request, *args, **kwargs): @@ -305,3 +309,9 @@ def get(self, request, *args, **kwargs): target.refresh_from_db() return Response(FavouriteSerializer(favourite).data, status=200) + + +class FAQViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): + queryset = FAQ.objects.all() + permission_classes = (MvjDjangoModelPermissionsOrAnonReadOnly,) + serializer_class = FAQSerializer