diff --git a/api/urls.py b/api/urls.py index 08a6a9b..97a8c9c 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,28 +1,35 @@ from django.urls import path -from .views import (PredictionCreateView, PredictionListView, - PredictionHistoryListView, - PredictionHistoryDetailView, - PredictionHistoryDeleteView, - SessionView, - WhoAmIView, +from rest_framework.routers import DefaultRouter +from rest_framework.authtoken.views import obtain_auth_token +from .views import ( + PredictionViewSet, SavedPointViewset, + TelemetryListCreateView, get_csrf, login_view, - logout_view) -from rest_framework.authtoken.views import obtain_auth_token -from .views import TelemetryListCreateView + logout_view, + SessionView, + WhoAmIView +) + + + +router = DefaultRouter() +router.register(r'predictions', PredictionViewSet, basename='predictions') +router.register(r'saved-points', SavedPointViewset, basename='saved-points') + + + urlpatterns = [ - path('predictions', PredictionCreateView.as_view(), name='create_prediction'), - path('predictions/list/', PredictionListView.as_view(), name='get_predictions'), + path("csrf/", get_csrf, name='api-csrf'), path('token', obtain_auth_token, name = 'get_token'), - path("history", PredictionHistoryListView.as_view(), name='view_history_list'), - path("history//", PredictionHistoryDetailView.as_view(), name='view_history_detail'), - path("history//delete/", PredictionHistoryDeleteView.as_view(), name='delete_history'), + path("login/", login_view, name='api-login'), + path("logout/", logout_view, name='api-logout'), + path("session/", SessionView.as_view(), name='api-session'), + path("whoami/", WhoAmIView.as_view(), name='api-whoami'), path("/telemetry/", TelemetryListCreateView.as_view(), name="create_telemetry"), - path('saved-points/', SavedPointViewset.as_view({'get': 'list', 'post': 'create', 'put': 'update', 'delete': 'destroy'}), name='saved_points'), - path('csrf/', get_csrf, name='api-csrf'), - path('login/', login_view, name='api-login'), - path('logout/', logout_view, name='api-logout'), - path('session/', SessionView.as_view(), name='api-session'), # new - path('whoami/', WhoAmIView.as_view(), name='api-whoami'), # new ] + + + +urlpatterns += router.urls diff --git a/api/views.py b/api/views.py index a30c1bb..7d27c1e 100644 --- a/api/views.py +++ b/api/views.py @@ -1,34 +1,34 @@ +import requests +import time +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 +from rest_framework.viewsets import ModelViewSet, ViewSet +from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework.authentication import SessionAuthentication, BasicAuthentication +from rest_framework.decorators import api_view, permission_classes, authentication_classes, action from django.utils import timezone -from .models import Prediction, User, Satellite, SavedPoint, SavedRateProfile, PreditctionTemplate -from .serializers import PredictionSerializer, PredictionRequestSerializer, PredictionListSerializer, PredictionDetailSerializer, SavedPointSerializer, SavedRateProfileSerializer, PreditctionTemplateSerializer -from rest_framework.permissions import IsAuthenticated -import requests from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator -from rest_framework.permissions import AllowAny -from .services.tawhiri import TawhiriClient -from django.contrib.auth import get_user_model -from .models import Satellite, TelemetryPacket -from .serializers import TelemetryPacketSerializer -from .permissions import ReadOnlyOrAuthenticated, IsOwner -import time from django.http import JsonResponse -from rest_framework.authentication import SessionAuthentication, BasicAuthentication -from django.contrib.auth import authenticate, login, logout -import json +from django.contrib.auth import authenticate, login, logout, get_user_model from django.middleware.csrf import get_token -from rest_framework.decorators import api_view, permission_classes, authentication_classes -from drf_spectacular.utils import extend_schema 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 + + User = get_user_model() + def get_prediction_from_tawhiri(params): + base_url = "https://fly.stratonautica.ru/api/v2" response = requests.get(base_url, params=params) @@ -39,14 +39,14 @@ def get_prediction_from_tawhiri(params): -class PredictionCreateView(APIView): +class PredictionViewSet(ViewSet): permission_classes = [IsAuthenticated] - - def post(self, request): - print("DEBUG: request.user =", request.user) - print("DEBUG: request.user.id =", request.user.id) + + + def create(self, request): serializer = PredictionRequestSerializer(data=request.data) + if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -54,11 +54,12 @@ class PredictionCreateView(APIView): try: prediction_result = TawhiriClient.get_prediction(validated_data) + except requests.RequestException as e: - print("Tawhiri error:", str(e), e.response.text if e.response else "no response") return Response({"error": f"Tawhiri error: {str(e)}"}, status=status.HTTP_502_BAD_GATEWAY) prediction = Prediction.objects.create(result=prediction_result, user=request.user, request=request.data) + return Response({ "id": prediction.id, "created_at": prediction.created_at, @@ -66,66 +67,60 @@ class PredictionCreateView(APIView): }, status=status.HTTP_201_CREATED) -class PredictionListView(APIView): - permission_classes = [IsAuthenticated] - def get(self, request): + @action(detail=False, methods=['get']) + def list_user(self, request): user = request.user satellite_id = request.query_params.get('satellite_id') created_from = request.query_params.get('created_from') created_till = request.query_params.get('created_till') - # Проверка доступа к спутнику - if satellite_id and not user.satellites.filter(id=satellite_id).exists(): - return Response({'detail': 'Access to this satellite is forbidden.'}, status=403) - filters = { - 'id__in': Prediction.objects.filter(user=user).values_list('prediction_id', flat=True), + 'user': user, 'deleted_at__isnull': True } - print(filters) - print(Prediction.objects.filter(**filters)) + if created_from: filters['created_at__gte'] = parse_datetime(created_from) + if created_till: filters['created_at__lte'] = parse_datetime(created_till) + if satellite_id: + if not user.satellites.filter(id=satellite_id).exists(): + return Response({'detail': 'Access denied'}, status=403) + filters['satellite_id'] = satellite_id - predictions = Prediction.objects.filter(**filters) - return Response(PredictionSerializer(predictions, many=True).data) - - - -class PredictionHistoryListView(generics.ListAPIView): - permission_classes = [permissions.IsAuthenticated] - serializer_class = PredictionListSerializer - - def get_queryset(self): - return Prediction.objects.filter( - user=self.request.user, - ) + queryset = Prediction.objects.filter(**filters) + return Response(PredictionSerializer(queryset, many=True).data) -class PredictionHistoryDetailView(generics.RetrieveAPIView): - permission_classes = [permissions.IsAuthenticated] - serializer_class = PredictionDetailSerializer - - def get_queryset(self): - return Prediction.objects.filter( - user=self.request.user, - ) + @action(detail=False, methods=["get"]) + def history(self, request): + queryset = Prediction.objects.filter(user=request.user) + return Response(PredictionListSerializer(queryset, many=True).data) -class PredictionHistoryDeleteView(generics.DestroyAPIView): - permission_classes = [permissions.IsAuthenticated] + @action(detail=True, methods=["get"]) + def detail(self, request, pk=None): + prediction = Prediction.objects.filter(user=request.user, pk=pk).first() + if not prediction: + return Response({'detail': 'Not found'}, status=404) + return Response(PredictionDetailSerializer(prediction).data) + + + + @action(detail=True, methods=["delete"]) + def delete(self, request, pk=None): + prediction = Prediction.objects.filter(user=request.user, pk=pk).first() + if not prediction: + return Response({'detail': 'Not found'}, status=404) + prediction.delete() + return Response(status=204) - def get_queryset(self): - return Prediction.objects.filter( - user=self.request.user, - ) class TelemetryListCreateView(generics.ListCreateAPIView): @@ -164,6 +159,7 @@ class TelemetryListCreateView(generics.ListCreateAPIView): return Response(serializer.errors, status=status.HTTP_201_CREATED) + class SessionView(APIView): authentication_classes = [SessionAuthentication, BasicAuthentication] permission_classes = [IsAuthenticated] @@ -173,6 +169,7 @@ class SessionView(APIView): return JsonResponse({'isAuthenticated': True}) + class WhoAmIView(APIView): authentication_classes = [SessionAuthentication, BasicAuthentication] permission_classes = [IsAuthenticated] @@ -182,6 +179,7 @@ class WhoAmIView(APIView): return JsonResponse({'username': request.user.username}) + @extend_schema(methods=["GET"], description="Get CSRF token") @api_view(["GET"]) @permission_classes([AllowAny]) @@ -191,6 +189,7 @@ def get_csrf(request): return response + @extend_schema(methods=["POST"], description="Login user") @csrf_exempt @api_view(["POST"]) @@ -212,6 +211,7 @@ def login_view(request): return JsonResponse({'detail': 'Successfully logged in.'}) + @extend_schema(methods=["POST"], description="Logout user") @api_view(["POST"]) @permission_classes([AllowAny]) @@ -223,6 +223,7 @@ def logout_view(request): return JsonResponse({'detail': 'Successfully logged out.'}) + class SavedPointViewset(ModelViewSet): authentication_classes = [SessionAuthentication, BasicAuthentication] permission_classes = [IsOwner] diff --git a/docker-compose.yml b/docker-compose.yml index 8763c10..f4e3e07 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3.8' services: db: