from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView from app.expenses.models import Category, Subcategory from .authentication import HasValidToken, TokenEnvAuthentication from .serializers import ( CategorySerializer, ExpenseReadSerializer, ExpenseWriteSerializer, SubcategorySerializer, ) class RootView(APIView): authentication_classes = [TokenEnvAuthentication] permission_classes = [HasValidToken] def get(self, request): return Response( { "type": "Success", "_links": { "categories": {"href": "/api/categories/", "method": "GET"}, "expenses": {"href": "/api/expenses/", "method": "POST"}, }, } ) class BaseAPIView(APIView): authentication_classes = [TokenEnvAuthentication] permission_classes = [HasValidToken] def _success(self, data, meta=None, links=None, status_code=status.HTTP_200_OK): response = {"type": "Success", "data": data} if meta: response["meta"] = meta if links: response["_links"] = links return Response(response, status=status_code) def _error( self, errors, error_type="ParametersError", status_code=status.HTTP_400_BAD_REQUEST, ): formatted = [] if isinstance(errors, dict): for field, messages in errors.items(): for msg in messages if isinstance(messages, list) else [messages]: formatted.append({"field": field, "message": str(msg)}) else: formatted.append({"field": None, "message": str(errors)}) return Response( {"type": error_type, "errors": formatted}, status=status_code, ) class CategoriesView(BaseAPIView): def get(self, request): categories = Category.objects.prefetch_related("subcategories").all() serializer = CategorySerializer(categories, many=True) return self._success( serializer.data, links={ "self": {"href": "/api/categories/", "method": "GET"}, }, ) class SubcategoriesView(BaseAPIView): def get(self, request, category_id): try: category = Category.objects.get(id=category_id) except Category.DoesNotExist: return self._error( "Category not found.", error_type="ResourceError", status_code=status.HTTP_404_NOT_FOUND, ) subcategories = Subcategory.objects.filter(category=category) serializer = SubcategorySerializer(subcategories, many=True) return self._success( serializer.data, links={ "self": { "href": f"/api/categories/{category_id}/subcategories/", "method": "GET", }, "category": { "href": "/api/categories/", "method": "GET", }, }, ) class ExpensesView(BaseAPIView): def post(self, request): serializer = ExpenseWriteSerializer(data=request.data) if not serializer.is_valid(): return self._error(serializer.errors) expense = serializer.save() read_serializer = ExpenseReadSerializer(expense) return self._success( read_serializer.data, links={ "self": {"href": "/api/expenses/", "method": "POST"}, "categories": {"href": "/api/categories/", "method": "GET"}, }, status_code=status.HTTP_201_CREATED, )