from django.db.models import Q, Case, When, Value, IntegerField from rest_framework import status from rest_framework.authtoken.models import Token from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from .models import User, Place, Review from .serializers import ( UserSerializer, PlaceSerializer, PlaceDetailSerializer, AddReviewSerializer ) @api_view(['POST']) @permission_classes([AllowAny]) def register(request): """ Register a new user with name and phone number. Request body: { "name": "John Doe", "phone": "1234567890" } Response: { "user": { "id": 1, "name": "John Doe", "phone": "1234567890" }, "token": "your-auth-token" } """ serializer = UserSerializer(data=request.data) if serializer.is_valid(): user = serializer.save() token, _ = Token.objects.get_or_create(user=user) return Response({ 'user': serializer.data, 'token': token.key }, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['POST']) @permission_classes([AllowAny]) def login(request): """ Login with phone number to get authentication token. Request body: { "phone": "1234567890" } Response: { "user": { "id": 1, "name": "John Doe", "phone": "1234567890" }, "token": "your-auth-token" } """ phone = request.data.get('phone') if not phone: return Response( {'error': 'Phone number is required'}, status=status.HTTP_400_BAD_REQUEST ) try: user = User.objects.get(phone=phone) token, _ = Token.objects.get_or_create(user=user) return Response({ 'user': UserSerializer(user).data, 'token': token.key }) except User.DoesNotExist: return Response( {'error': 'User with this phone number does not exist'}, status=status.HTTP_404_NOT_FOUND ) @api_view(['POST']) @permission_classes([IsAuthenticated]) def add_review(request): """ Add a review for a place. Creates the place if it doesn't exist. Request body: { "place_name": "Joe's Pizza", "place_address": "123 Main St, New York, NY", "rating": 5, "review_text": "Best pizza in town!" } Response: { "message": "Review added successfully", "review": { "id": 1, "user_name": "John Doe", "rating": 5, "text": "Best pizza in town!", "created_at": "2024-01-01T12:00:00Z" } } """ serializer = AddReviewSerializer( data=request.data, context={'request': request} ) if serializer.is_valid(): review = serializer.save() from .serializers import ReviewSerializer return Response({ 'message': 'Review added successfully', 'review': ReviewSerializer(review).data }, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['GET']) @permission_classes([IsAuthenticated]) def search_places(request): """ Search for places by name and/or minimum rating. Query parameters: - name (optional): Search by place name (exact match first, then partial) - min_rating (optional): Filter by minimum average rating Example: /api/places/search/?name=pizza&min_rating=4 Response: [ { "id": 1, "name": "Joe's Pizza", "address": "123 Main St, New York, NY", "average_rating": 4.5 } ] """ name_query = request.query_params.get('name', '').strip() min_rating = request.query_params.get('min_rating', None) # Start with all places queryset = Place.objects.all() # Filter by name if provided if name_query: # Use Q objects to combine exact and partial matches # Annotate with priority: 1 for exact match, 2 for partial match queryset = queryset.annotate( match_priority=Case( When(name__iexact=name_query, then=Value(1)), When(name__icontains=name_query, then=Value(2)), default=Value(3), output_field=IntegerField() ) ).filter( Q(name__iexact=name_query) | Q(name__icontains=name_query) ).order_by('match_priority', 'name') else: queryset = queryset.order_by('name') # Get all places and filter by rating in Python (since average is a property) places = list(queryset) if min_rating: try: min_rating_value = float(min_rating) places = [ place for place in places if place.average_rating is not None and place.average_rating >= min_rating_value ] except ValueError: return Response( {'error': 'Invalid min_rating value'}, status=status.HTTP_400_BAD_REQUEST ) serializer = PlaceSerializer(places, many=True) return Response(serializer.data) @api_view(['GET']) @permission_classes([IsAuthenticated]) def place_detail(request, place_id): """ Get detailed information about a place including all reviews. Current user's review appears first, followed by others sorted by newest. Response: { "id": 1, "name": "Joe's Pizza", "address": "123 Main St, New York, NY", "average_rating": 4.5, "reviews": [ { "id": 1, "user_name": "John Doe", "rating": 5, "text": "Best pizza!", "created_at": "2024-01-01T12:00:00Z" } ] } """ try: place = Place.objects.get(pk=place_id) serializer = PlaceDetailSerializer( place, context={'request': request} ) return Response(serializer.data) except Place.DoesNotExist: return Response( {'error': 'Place not found'}, status=status.HTTP_404_NOT_FOUND )