from rest_framework import generics, status, filters from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework.parsers import MultiPartParser, FormParser, JSONParser from django_filters.rest_framework import DjangoFilterBackend from django.shortcuts import get_object_or_404 from django.db.models import Q, Avg from django.db import models from django.conf import settings from .models import Service, ServiceFeature, ServiceCategory from .serializers import ( ServiceSerializer, ServiceListSerializer, ServiceCreateUpdateSerializer, ServiceFeatureSerializer, ServiceCategorySerializer ) class ServiceListView(generics.ListAPIView): """ List all active services with optional filtering """ queryset = Service.objects.filter(is_active=True) serializer_class = ServiceListSerializer permission_classes = [AllowAny] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['featured', 'display_order'] search_fields = ['title', 'description'] ordering_fields = ['display_order', 'created_at', 'title', 'price'] ordering = ['display_order', 'created_at'] def get_queryset(self): queryset = super().get_queryset() # Filter by category if provided category_slug = self.request.query_params.get('category', None) if category_slug: queryset = queryset.filter(category__slug=category_slug) # Filter by price range if provided min_price = self.request.query_params.get('min_price', None) max_price = self.request.query_params.get('max_price', None) if min_price: queryset = queryset.filter(price__gte=min_price) if max_price: queryset = queryset.filter(price__lte=max_price) return queryset class ServiceDetailView(generics.RetrieveAPIView): """ Retrieve a single service by slug """ queryset = Service.objects.filter(is_active=True) serializer_class = ServiceSerializer permission_classes = [AllowAny] lookup_field = 'slug' class ServiceCreateView(generics.CreateAPIView): """ Create a new service (admin only) """ queryset = Service.objects.all() serializer_class = ServiceCreateUpdateSerializer permission_classes = [IsAuthenticated] parser_classes = [MultiPartParser, FormParser, JSONParser] class ServiceUpdateView(generics.UpdateAPIView): """ Update an existing service (admin only) """ queryset = Service.objects.all() serializer_class = ServiceCreateUpdateSerializer permission_classes = [IsAuthenticated] lookup_field = 'slug' parser_classes = [MultiPartParser, FormParser, JSONParser] class ServiceDeleteView(generics.DestroyAPIView): """ Delete a service (admin only) """ queryset = Service.objects.all() permission_classes = [IsAuthenticated] lookup_field = 'slug' def perform_destroy(self, instance): # Delete the image file if it exists if instance.image: instance.image.delete(save=False) instance.delete() class FeaturedServicesView(generics.ListAPIView): """ List all featured services """ queryset = Service.objects.filter(is_active=True, featured=True) serializer_class = ServiceListSerializer permission_classes = [AllowAny] ordering = ['display_order', 'created_at'] class ServiceCategoryListView(generics.ListAPIView): """ List all active service categories """ queryset = ServiceCategory.objects.filter(is_active=True) serializer_class = ServiceCategorySerializer permission_classes = [AllowAny] ordering = ['display_order', 'name'] class ServiceCategoryDetailView(generics.RetrieveAPIView): """ Retrieve a single service category by slug """ queryset = ServiceCategory.objects.filter(is_active=True) serializer_class = ServiceCategorySerializer permission_classes = [AllowAny] lookup_field = 'slug' @api_view(['GET']) @permission_classes([AllowAny]) def service_search(request): """ Search services by query string """ query = request.GET.get('q', '') if not query: return Response({'error': 'Query parameter "q" is required'}, status=status.HTTP_400_BAD_REQUEST) services = Service.objects.filter( Q(is_active=True) & ( Q(title__icontains=query) | Q(description__icontains=query) | Q(slug__icontains=query) ) ).order_by('display_order', 'created_at') serializer = ServiceListSerializer(services, many=True) return Response({ 'query': query, 'count': services.count(), 'results': serializer.data }) @api_view(['GET']) @permission_classes([AllowAny]) def service_stats(request): """ Get service statistics """ total_services = Service.objects.filter(is_active=True).count() featured_services = Service.objects.filter(is_active=True, featured=True).count() categories = ServiceCategory.objects.filter(is_active=True).count() # Average price services_with_price = Service.objects.filter(is_active=True, price__gt=0) avg_price = services_with_price.aggregate(avg_price=Avg('price'))['avg_price'] or 0 return Response({ 'total_services': total_services, 'featured_services': featured_services, 'categories': categories, 'average_price': float(avg_price) }) @api_view(['POST']) @permission_classes([IsAuthenticated]) def upload_service_image(request, slug): """ Upload an image for a specific service (admin only) """ try: service = get_object_or_404(Service, slug=slug) if 'image' not in request.FILES: return Response( {'error': 'No image file provided'}, status=status.HTTP_400_BAD_REQUEST ) # Delete old image if it exists if service.image: service.image.delete(save=False) # Save new image service.image = request.FILES['image'] service.save() serializer = ServiceSerializer(service) return Response({ 'message': 'Image uploaded successfully', 'service': serializer.data }) except Exception as e: return Response( {'error': f'Failed to upload image: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR )