Skip to content

Commit

Permalink
download EntityList on endpoint `/api/v2/entity-lists/<id>.csv
Browse files Browse the repository at this point in the history
  • Loading branch information
kelvin-muchiri committed Aug 26, 2024
1 parent e40c24e commit c415a83
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 3 deletions.
8 changes: 7 additions & 1 deletion docs/entities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,14 @@ Download EntityList

<pre class="prettyprint"><b>GET</b> api/v2/entity-lists/&lt;entity_list_id&gt;/download</pre>

or

This endpoint is used to download the dataset in CSV format.
.. raw:: html

<pre class="prettyprint"><b>GET</b> api/v2/entity-lists/&lt;entity_list_id&gt;.csv</pre>


This endpoints are used to download the dataset in CSV format.

**Example**

Expand Down
44 changes: 44 additions & 0 deletions onadata/apps/api/tests/viewsets/test_entity_list_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,25 @@ def test_soft_deleted(self):
response = self.view(request, pk=self.entity_list.pk)
self.assertEqual(response.status_code, 404)

def test_render_csv(self):
"""Render in CSV format"""
request = self.factory.get("/", **self.extra)
# Using `.csv` suffix
response = self.view(request, pk=self.entity_list.pk, format="csv")
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.get("Content-Disposition"), "attachment; filename=trees.csv"
)
self.assertEqual(response["Content-Type"], "application/csv")
# Using `Accept` header
request = self.factory.get("/", HTTP_ACCEPT="text/csv", **self.extra)
response = self.view(request, pk=self.entity_list.pk)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.get("Content-Disposition"), "attachment; filename=trees.csv"
)
self.assertEqual(response["Content-Type"], "application/csv")


class DeleteEntityListTestCase(TestAbstractViewSet):
"""Tests for deleting a single EntityList"""
Expand Down Expand Up @@ -1352,6 +1371,31 @@ def test_download(self):
self.assertEqual(
response["Content-Disposition"], "attachment; filename=trees.csv"
)
self.assertEqual(response["Content-Type"], "application/csv")
# Using `.csv` suffix
request = self.factory.get("/", **self.extra)
response = self.view(request, pk=self.entity_list.pk, format="csv")
self.assertEqual(response.status_code, 200)
self.assertEqual(
response["Content-Disposition"], "attachment; filename=trees.csv"
)
self.assertEqual(response["Content-Type"], "application/csv")
# Using `Accept` header
request = self.factory.get("/", HTTP_ACCEPT="text/csv", **self.extra)
response = self.view(request, pk=self.entity_list.pk)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.get("Content-Disposition"), "attachment; filename=trees.csv"
)
self.assertEqual(response["Content-Type"], "application/csv")
# Unsupported suffix
request = self.factory.get("/", **self.extra)
response = self.view(request, pk=self.entity_list.pk, format="json")
self.assertEqual(response.status_code, 404)
# Unsupported accept header
request = self.factory.get("/", HTTP_ACCEPT="application/json", **self.extra)
response = self.view(request, pk=self.entity_list.pk)
self.assertEqual(response.status_code, 404)

def test_anonymous_user(self):
"""Anonymous user cannot download a private EntityList"""
Expand Down
28 changes: 26 additions & 2 deletions onadata/apps/api/viewsets/entity_list_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.viewsets import GenericViewSet
Expand All @@ -13,6 +14,7 @@
ListModelMixin,
)


from onadata.apps.api.permissions import DjangoObjectPermissionsIgnoreModelPerm
from onadata.apps.api.tools import get_baseviewset_class
from onadata.apps.logger.models import Entity, EntityList
Expand All @@ -23,6 +25,7 @@
StandardPageNumberPagination,
)
from onadata.libs.permissions import CAN_ADD_PROJECT_ENTITYLIST
from onadata.libs.renderers import renderers
from onadata.libs.serializers.entity_serializer import (
EntityArraySerializer,
EntitySerializer,
Expand Down Expand Up @@ -174,9 +177,30 @@ def get_queryset_entities(self, request, entity_list):

return queryset

@action(methods=["GET"], detail=True)
def download(self, request, *args, **kwargs):
@action(
methods=["GET"],
detail=True,
renderer_classes=[renderers.CSVRenderer],
)
def download(self, request, format=None, *args, **kwargs):
"""Provides `download` action for dataset"""
accept_header = request.headers.get("Accept", "")

if (
format is not None or accept_header
) and not request.accepted_renderer.format == "csv":
raise NotFound(code=status.HTTP_404_NOT_FOUND)

entity_list = self.get_object()

return get_entity_list_export_response(request, entity_list, entity_list.name)

def retrieve(self, request, format=None, *args, **kwargs):
"""Override `retrieve` method"""
instance = self.get_object()
format = format or request.accepted_renderer.format

if format == "csv":
return get_entity_list_export_response(request, instance, instance.name)

return super().retrieve(request, format, *args, **kwargs)

0 comments on commit c415a83

Please sign in to comment.