37794-vm/core/api_views.py
2026-02-01 13:17:06 +00:00

137 lines
5.6 KiB
Python

from rest_framework import generics, permissions, status
from rest_framework.response import Response
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.views import APIView
from django.db.models import Q
from .models import Parcel, Profile
from .serializers import ParcelSerializer, ProfileSerializer, PublicParcelSerializer
from .pricing import calculate_haversine_distance, get_pricing_breakdown
from decimal import Decimal
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
# Ensure profile exists
profile, created = Profile.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email,
'role': profile.role,
'username': user.username
})
class ParcelListCreateView(generics.ListCreateAPIView):
serializer_class = ParcelSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
user = self.request.user
profile = user.profile
if profile.role == 'shipper':
return Parcel.objects.filter(shipper=user).order_by('-created_at')
elif profile.role == 'car_owner':
# Drivers see available parcels (pending) or their own assignments
return Parcel.objects.filter(
Q(status='pending') | Q(carrier=user)
).order_by('-created_at')
else:
return Parcel.objects.none()
def perform_create(self, serializer):
from .models import PlatformProfile
platform_profile = PlatformProfile.objects.first()
if platform_profile and not platform_profile.accepting_shipments:
raise permissions.PermissionDenied(platform_profile.maintenance_message or "The platform is currently not accepting new shipments.")
# Only shippers can create
if self.request.user.profile.role != 'shipper':
raise permissions.PermissionDenied("Only shippers can create parcels.")
serializer.save(shipper=self.request.user)
class ParcelDetailView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = ParcelSerializer
permission_classes = [permissions.IsAuthenticated]
queryset = Parcel.objects.all()
def get_queryset(self):
# Restrict access
user = self.request.user
if user.profile.role == 'shipper':
return Parcel.objects.filter(shipper=user)
elif user.profile.role == 'car_owner':
# Drivers can see parcels they can accept (pending) or are assigned to
return Parcel.objects.filter(
Q(status='pending') | Q(carrier=user)
)
return Parcel.objects.none()
def perform_update(self, serializer):
# Add logic: Drivers can only update status, Shippers can edit details if pending
# For simplicity in this v1, we allow updates but validation should be improved for production
serializer.save()
class UserProfileView(generics.RetrieveUpdateAPIView):
serializer_class = ProfileSerializer
permission_classes = [permissions.IsAuthenticated]
def get_object(self):
return self.request.user.profile
class PublicParcelTrackView(generics.RetrieveAPIView):
"""
Public endpoint to track a parcel by its tracking number.
No authentication required.
"""
serializer_class = PublicParcelSerializer
permission_classes = [permissions.AllowAny]
queryset = Parcel.objects.all()
lookup_field = 'tracking_number'
class PriceCalculatorView(APIView):
permission_classes = [permissions.AllowAny] # Allow frontend to query without strict auth if needed, or IsAuthenticated
def post(self, request):
try:
data = request.data
pickup_lat = data.get('pickup_lat')
pickup_lng = data.get('pickup_lng')
delivery_lat = data.get('delivery_lat')
delivery_lng = data.get('delivery_lng')
weight = data.get('weight')
if not all([pickup_lat, pickup_lng, delivery_lat, delivery_lng, weight]):
return Response({'error': 'Missing location or weight data.'}, status=status.HTTP_400_BAD_REQUEST)
weight = Decimal(str(weight))
# Calculate Distance
distance_km = calculate_haversine_distance(pickup_lat, pickup_lng, delivery_lat, delivery_lng)
# Get Breakdown
breakdown = get_pricing_breakdown(distance_km, weight)
if 'error' in breakdown:
return Response(breakdown, status=status.HTTP_400_BAD_REQUEST)
response_data = {
'distance_km': round(float(distance_km), 2),
'weight_kg': float(weight),
'price': float(breakdown['price']),
'platform_fee': float(breakdown['platform_fee']),
'driver_amount': float(breakdown['driver_amount']),
'platform_fee_percentage': float(breakdown['platform_fee_percentage']),
}
return Response(response_data)
except Exception as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)