diff --git a/api/custom_pagination.py b/api/custom_pagination.py new file mode 100644 index 0000000..6d37de8 --- /dev/null +++ b/api/custom_pagination.py @@ -0,0 +1,17 @@ +from rest_framework.pagination import LimitOffsetPagination +from rest_framework.response import Response + +class CustomLimitOffsetPagination(LimitOffsetPagination): + limit_query_param = 'limit' + offset_query_param = 'skip' + max_limit = 100 + default_limit = 10 + + + def get_paginated_response(self, data): + return Response({ + 'total': self.count, + 'limit': self.limit, + 'skip': self.offset, + 'predictions': data + }) \ No newline at end of file diff --git a/api/services/__pycache__/tawhiri.cpython-313.pyc b/api/services/__pycache__/tawhiri.cpython-313.pyc index 4e13d6e..18b9ffc 100644 Binary files a/api/services/__pycache__/tawhiri.cpython-313.pyc and b/api/services/__pycache__/tawhiri.cpython-313.pyc differ diff --git a/api/views.py b/api/views.py index 26ce38e..f0cb04a 100644 --- a/api/views.py +++ b/api/views.py @@ -4,7 +4,8 @@ import json from rest_framework import status, generics, permissions from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework.viewsets import ModelViewSet, ViewSet +from rest_framework.viewsets import ModelViewSet, ViewSet, GenericViewSet +from rest_framework.exceptions import APIException from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication from rest_framework.decorators import api_view, permission_classes, authentication_classes, action @@ -14,14 +15,15 @@ from django.utils.decorators import method_decorator from django.http import JsonResponse from django.contrib.auth import authenticate, login, logout, get_user_model from django.middleware.csrf import get_token +from django.core.exceptions import ValidationError from django.utils.dateparse import parse_datetime from .models import Prediction, User, Satellite, SavedPoint, SavedRateProfile, PreditctionTemplate, TelemetryPacket from .serializers import PredictionSerializer, TelemetryPacketSerializer, PredictionRequestSerializer, PredictionListSerializer, PredictionDetailSerializer, SavedPointSerializer, SavedRateProfileSerializer, PreditctionTemplateSerializer from .services.tawhiri import TawhiriClient from drf_spectacular.utils import extend_schema from .permissions import ReadOnlyOrAuthenticated, IsOwner - - +from .custom_pagination import CustomLimitOffsetPagination +from datetime import datetime User = get_user_model() @@ -37,9 +39,14 @@ def get_prediction_from_tawhiri(params): f"Tawhiri error: {response.status_code} {response.text}") -class PredictionViewSet(ViewSet): +class PredictionViewSet(GenericViewSet): permission_classes = [IsAuthenticated] + pagination_class = CustomLimitOffsetPagination + def list(self, request): + queryset = Prediction.objects.filter(user=request.user) + return Response(PredictionSerializer(queryset, many=True).data) + def create(self, request): serializer = PredictionRequestSerializer(data=request.data) @@ -92,7 +99,16 @@ class PredictionViewSet(ViewSet): filters['satellite_id'] = satellite_id queryset = Prediction.objects.filter(**filters) - return Response(PredictionSerializer(queryset, many=True).data) + queryset = self.filter_queryset(queryset) + + page = self.paginate_queryset(queryset) + + if page is not None: + serializer = PredictionSerializer(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = PredictionSerializer(queryset, many=True) + return Response(serializer.data) @action(detail=False, methods=["get"]) def history(self, request): @@ -168,6 +184,7 @@ class WhoAmIView(APIView): @extend_schema(methods=["GET"], description="Get CSRF token") +@csrf_exempt @api_view(["GET"]) @permission_classes([AllowAny]) def get_csrf(request): @@ -219,6 +236,7 @@ class SavedPointViewset(ModelViewSet): def perform_create(self, serializer): serializer.save(user=self.request.user) + class PreditctionTemplateViewset(ModelViewSet): permission_classes = [IsOwner] serializer_class = PreditctionTemplateSerializer diff --git a/testapi/settings.py b/testapi/settings.py index a69ade9..614122e 100644 --- a/testapi/settings.py +++ b/testapi/settings.py @@ -105,6 +105,13 @@ WSGI_APPLICATION = 'testapi.wsgi.application' # https://docs.djangoproject.com/en/5.1/ref/settings/#databases if PRODUCTION: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } + } +else: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', @@ -115,13 +122,7 @@ if PRODUCTION: 'PORT': os.getenv('DB_PORT'), } } -else: - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', - } - } + # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators @@ -187,10 +188,10 @@ REST_FRAMEWORK = { } -CSRF_COOKIE_SAMESITE = 'None' # temp +CSRF_COOKIE_SAMESITE = 'Lax' # temp CSRF_COOKIE_SECURE = False -SESSION_COOKIE_SAMESITE = 'None' # temp +SESSION_COOKIE_SAMESITE = 'Lax' # temp SESSION_COOKIE_SECURE = False -CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost:5173').split(',') +CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost:5173, http://localhost:8000').split(',')