diff --git a/assets b/assets index 5f55b6735..6429ef4e6 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5f55b673507e6323eb10a8a76f19552d73786c5a +Subproject commit 6429ef4e62f570e2e3e6b2c8ad81225f5fc0bcd2 diff --git a/eap/serializers.py b/eap/serializers.py index 01a9b5b89..7ed2d01e6 100644 --- a/eap/serializers.py +++ b/eap/serializers.py @@ -40,7 +40,7 @@ from main.writable_nested_serializers import NestedCreateMixin, NestedUpdateMixin from utils.file_check import validate_file_type -ALLOWED_FILE_EXTENTIONS: list[str] = ["pdf", "docx", "pptx", "xlsx"] +ALLOWED_FILE_EXTENTIONS: list[str] = ["pdf", "docx", "pptx", "xlsx", "xlsm"] class BaseEAPSerializer(serializers.ModelSerializer): @@ -218,6 +218,10 @@ class EAPFileInputSerializer(serializers.Serializer): file = serializers.ListField(child=serializers.FileField(required=True)) +class EAPGlobalFilesSerializer(serializers.Serializer): + url = serializers.URLField(read_only=True) + + class EAPFileSerializer(BaseEAPSerializer): id = serializers.IntegerField(required=False) file = serializers.FileField(required=True) diff --git a/eap/test_views.py b/eap/test_views.py index 97b1d0661..6844b5272 100644 --- a/eap/test_views.py +++ b/eap/test_views.py @@ -2308,3 +2308,40 @@ def test_snapshot_full_eap(self): orig_actors[0].description, snapshot_actors[0].description, ) + + +class EAPGlobalFileTestCase(APITestCase): + def setUp(self): + super().setUp() + self.url = "/api/v2/eap/global-files/" + + def test_get_template_files_invalid_param(self): + self.authenticate() + response = self.client.get(f"{self.url}invalid_type/") + self.assert_400(response) + self.assertIn("detail", response.data) + + def test_get_budget_template(self): + self.authenticate() + response = self.client.get(f"{self.url}budget_template/") + self.assert_200(response) + self.assertIn("url", response.data) + self.assertTrue(response.data["url"].endswith("files/eap/budget_template.xlsm")) + + def test_get_forecast_table_template(self): + self.authenticate() + response = self.client.get(f"{self.url}forecast_table/") + self.assert_200(response) + self.assertIn("url", response.data) + self.assertTrue(response.data["url"].endswith("files/eap/forecasts_table.docx")) + + def test_get_theory_of_change_template(self): + self.authenticate() + response = self.client.get(f"{self.url}theory_of_change_table/") + self.assert_200(response) + self.assertIn("url", response.data) + self.assertTrue(response.data["url"].endswith("files/eap/theory_of_change_table.docx")) + + def test_get_template_files_unauthenticated(self): + response = self.client.get(f"{self.url}budget_template/") + self.assert_401(response) diff --git a/eap/views.py b/eap/views.py index 6ca759a11..dcb13db58 100644 --- a/eap/views.py +++ b/eap/views.py @@ -1,7 +1,8 @@ # Create your views here. from django.db.models import Case, F, IntegerField, When from django.db.models.query import Prefetch, QuerySet -from drf_spectacular.utils import extend_schema +from django.templatetags.static import static +from drf_spectacular.utils import OpenApiParameter, extend_schema from rest_framework import mixins, permissions, response, status, viewsets from rest_framework.decorators import action @@ -29,6 +30,7 @@ from eap.serializers import ( EAPFileInputSerializer, EAPFileSerializer, + EAPGlobalFilesSerializer, EAPRegistrationSerializer, EAPStatusSerializer, EAPValidatedBudgetFileSerializer, @@ -324,3 +326,54 @@ def multiple_file(self, request): file_serializer.save() return response.Response(file_serializer.data, status=status.HTTP_201_CREATED) return response.Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class EAPGlobalFilesViewSet( + mixins.RetrieveModelMixin, + viewsets.GenericViewSet, +): + + serializer_class = EAPGlobalFilesSerializer + permission_classes = permissions.IsAuthenticated, DenyGuestUserPermission + + lookup_field = "template_type" + lookup_url_kwarg = "template_type" + + template_map = { + "budget_template": "files/eap/budget_template.xlsm", + "forecast_table": "files/eap/forecasts_table.docx", + "theory_of_change_table": "files/eap/theory_of_change_table.docx", + } + + @extend_schema( + request=None, + responses=EAPGlobalFilesSerializer, + parameters=[ + OpenApiParameter( + name="template_type", + location=OpenApiParameter.PATH, + description="Type of EAP template to download", + required=True, + type=str, + enum=list(template_map.keys()), + ) + ], + ) + def retrieve(self, request, *args, **kwargs): + template_type = kwargs.get("template_type") + if not template_type: + return response.Response( + { + "detail": "Template file type not found.", + }, + status=400, + ) + if template_type not in self.template_map: + return response.Response( + { + "detail": f"Invalid template file type '{template_type}'.Please use one of the following values:{(self.template_map.keys())}." # noqa + }, + status=400, + ) + serializer = EAPGlobalFilesSerializer({"url": request.build_absolute_uri(static(self.template_map[template_type]))}) + return response.Response(serializer.data) diff --git a/go-static/files/eap/budget_template.xlsm b/go-static/files/eap/budget_template.xlsm new file mode 100644 index 000000000..99eacf92b Binary files /dev/null and b/go-static/files/eap/budget_template.xlsm differ diff --git a/go-static/files/eap/forecasts_table.docx b/go-static/files/eap/forecasts_table.docx new file mode 100644 index 000000000..563c49a0c Binary files /dev/null and b/go-static/files/eap/forecasts_table.docx differ diff --git a/go-static/files/eap/theory_of_change_table.docx b/go-static/files/eap/theory_of_change_table.docx new file mode 100644 index 000000000..fd249488c Binary files /dev/null and b/go-static/files/eap/theory_of_change_table.docx differ diff --git a/main/urls.py b/main/urls.py index 70a515c41..da2d1a36d 100644 --- a/main/urls.py +++ b/main/urls.py @@ -199,6 +199,7 @@ router.register(r"simplified-eap", eap_views.SimplifiedEAPViewSet, basename="simplified_eap") router.register(r"full-eap", eap_views.FullEAPViewSet, basename="full_eap") router.register(r"eap-file", eap_views.EAPFileViewSet, basename="eap_file") +router.register(r"eap/global-files", eap_views.EAPGlobalFilesViewSet, basename="eap_global_files") admin.site.site_header = "IFRC Go administration" admin.site.site_title = "IFRC Go admin"