Skip to content

Commit

Permalink
✨(backend) add order export api
Browse files Browse the repository at this point in the history
As we want to export order csv, an api endpoint is needed.
  • Loading branch information
kernicPanel committed Dec 11, 2024
1 parent 53bd3f3 commit d08d562
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 1 deletion.
29 changes: 28 additions & 1 deletion src/backend/joanie/core/api/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
Admin API Endpoints
"""

import csv
from http import HTTPStatus

from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.http import JsonResponse
from django.http import HttpResponse, JsonResponse

from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.types import OpenApiTypes
Expand Down Expand Up @@ -614,6 +615,7 @@ class OrderViewSet(
permission_classes = [permissions.IsAdminUser & permissions.DjangoModelPermissions]
serializer_classes = {
"list": serializers.AdminOrderLightSerializer,
"export": serializers.AdminOrderExportSerializer,
}
default_serializer_class = serializers.AdminOrderSerializer
filterset_class = filters.OrderAdminFilterSet
Expand Down Expand Up @@ -689,6 +691,31 @@ def refund(self, request, pk=None): # pylint:disable=unused-argument

return Response(status=HTTPStatus.ACCEPTED)

@extend_schema(
request=None,
responses={
(200, "text/csv"): OpenApiTypes.OBJECT,
404: serializers.ErrorResponseSerializer,
},
)
@action(methods=["GET"], detail=False)
def export(self, request):
"""
Export orders to a CSV file.
"""
queryset = self.filter_queryset(self.get_queryset()).order_by("created_on")
serializer = self.get_serializer(queryset, many=True)
data = serializer.data

response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="orders.csv"'
writer = csv.writer(response)

writer.writerow(data[0].keys())
writer.writerows([row.values() for row in data])

return response


class OrganizationAddressViewSet(
mixins.CreateModelMixin,
Expand Down
25 changes: 25 additions & 0 deletions src/backend/joanie/core/serializers/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,31 @@ def get_owner_name(self, instance) -> str:
return instance.owner.get_full_name() or instance.owner.username


class AdminOrderExportSerializer(serializers.ModelSerializer):
"""
Read only light serializer for Order export.
"""

owner = serializers.SerializerMethodField(read_only=True)

class Meta:
model = models.Order
fields = (
"id",
"created_on",
"owner",
"total",
)
read_only_fields = fields

def get_owner(self, instance) -> str:
"""
Return the full name of the order's owner if available,
otherwise fallback to the username
"""
return instance.owner.get_full_name() or instance.owner.username


class AdminEnrollmentLightSerializer(serializers.ModelSerializer):
"""
Light Serializer for Enrollment model
Expand Down
68 changes: 68 additions & 0 deletions src/backend/joanie/tests/core/api/admin/orders/test_export.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""Test suite for the admin orders API export endpoint."""

from http import HTTPStatus

from django.test import TestCase

from joanie.core import factories
from joanie.tests import format_date


class OrdersAdminApiExportTestCase(TestCase):
"""Test suite for the admin orders API export endpoint."""

maxDiff = None

def test_api_admin_orders_export_csv_anonymous_user(self):
"""
Anonymous users should not be able to export orders as CSV.
"""
response = self.client.get("/api/v1.0/admin/orders/export/")

self.assertEqual(response.status_code, HTTPStatus.UNAUTHORIZED)

def test_api_admin_orders_export_csv_lambda_user(self):
"""
Lambda users should not be able to export orders as CSV.
"""
admin = factories.UserFactory(is_staff=False, is_superuser=False)
self.client.login(username=admin.username, password="password")

response = self.client.get("/api/v1.0/admin/orders/export/")

self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN)

def test_api_admin_orders_export_csv(self):
"""
Admin users should be able to export orders as CSV.
"""
orders = factories.OrderFactory.create_batch(3)
admin = factories.UserFactory(is_staff=True, is_superuser=True)
self.client.login(username=admin.username, password="password")

response = self.client.get("/api/v1.0/admin/orders/export/")

self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertEqual(response["Content-Type"], "text/csv")
self.assertEqual(
response["Content-Disposition"],
'attachment; filename="orders.csv"',
)
csv_content = response.content.decode().splitlines()
csv_header = csv_content.pop(0)
self.assertEqual(
csv_header.split(","),
[
"id",
"created_on",
"owner",
"total",
],
)

for order, csv_line in zip(orders, csv_content, strict=False):
csv_row = csv_line.split(",")
self.assertEqual(csv_row[0], str(order.id))
self.assertEqual(csv_row[1], format_date(order.created_on))
self.assertEqual(csv_row[2], order.owner.username)
self.assertEqual(csv_row[3], str(order.total))
37 changes: 37 additions & 0 deletions src/backend/joanie/tests/swagger/admin-swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3192,6 +3192,43 @@
}
}
},
"/api/v1.0/admin/orders/export/": {
"get": {
"operationId": "orders_export_retrieve",
"description": "Export orders to a CSV file.",
"tags": [
"orders"
],
"security": [
{
"cookieAuth": []
}
],
"responses": {
"200": {
"content": {
"text/csv": {
"schema": {
"type": "object",
"additionalProperties": {}
}
}
},
"description": ""
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
},
"description": ""
}
}
}
},
"/api/v1.0/admin/organizations/": {
"get": {
"operationId": "organizations_list",
Expand Down

0 comments on commit d08d562

Please sign in to comment.