from rest_framework import generics, status from rest_framework.decorators import api_view, permission_classes from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated, AllowAny,IsAdminUser from django.utils import timezone from datetime import datetime, timedelta from .models import AdminWeeklyAvailability, AppointmentRequest from .serializers import ( AdminWeeklyAvailabilitySerializer, AppointmentRequestSerializer, AppointmentRequestCreateSerializer, AppointmentScheduleSerializer, AppointmentRejectSerializer ) from .email_service import EmailService from users.models import CustomUser from django.db.models import Count, Q class AdminAvailabilityView(generics.RetrieveUpdateAPIView): permission_classes = [IsAuthenticated, IsAdminUser] serializer_class = AdminWeeklyAvailabilitySerializer def get_object(self): obj, created = AdminWeeklyAvailability.objects.get_or_create( defaults={'available_days': []} ) return obj class AppointmentRequestListView(generics.ListAPIView): serializer_class = AppointmentRequestSerializer permission_classes = [IsAuthenticated] def get_queryset(self): queryset = AppointmentRequest.objects.all() if self.request.user.is_staff: return queryset return queryset.filter(email=self.request.user.email) class AppointmentRequestCreateView(generics.CreateAPIView): permission_classes = [IsAuthenticated] queryset = AppointmentRequest.objects.all() serializer_class = AppointmentRequestCreateSerializer def perform_create(self, serializer): availability = AdminWeeklyAvailability.objects.first() if availability: available_days = availability.available_days preferred_dates = serializer.validated_data['preferred_dates'] for date_str in preferred_dates: date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() if date_obj.weekday() not in available_days: from rest_framework.exceptions import ValidationError raise ValidationError(f'Date {date_str} is not available for appointments.') appointment = serializer.save() EmailService.send_admin_notification(appointment) class AppointmentRequestDetailView(generics.RetrieveAPIView): permission_classes = [IsAuthenticated] queryset = AppointmentRequest.objects.all() serializer_class = AppointmentRequestSerializer lookup_field = 'pk' class ScheduleAppointmentView(generics.GenericAPIView): permission_classes = [IsAuthenticated, IsAdminUser] serializer_class = AppointmentScheduleSerializer queryset = AppointmentRequest.objects.all() lookup_field = 'pk' def post(self, request, pk): appointment = self.get_object() if appointment.status != 'pending_review': return Response( {'error': 'Only pending review appointments can be scheduled.'}, status=status.HTTP_400_BAD_REQUEST ) serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) appointment.schedule_appointment( datetime_obj=serializer.validated_data['scheduled_datetime'], duration=serializer.validated_data['scheduled_duration'] ) EmailService.send_appointment_scheduled(appointment) response_serializer = AppointmentRequestSerializer(appointment) return Response({ **response_serializer.data, 'message': 'Appointment scheduled successfully. Jitsi meeting created.', 'jitsi_meeting_created': appointment.has_jitsi_meeting }) class RejectAppointmentView(generics.GenericAPIView): permission_classes = [IsAuthenticated] serializer_class = AppointmentRejectSerializer queryset = AppointmentRequest.objects.all() lookup_field = 'pk' def post(self, request, pk): appointment = self.get_object() if appointment.status != 'pending_review': return Response( {'error': 'Only pending appointments can be rejected'}, status=status.HTTP_400_BAD_REQUEST ) serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) appointment.reject_appointment( serializer.validated_data.get('rejection_reason', '') ) EmailService.send_appointment_rejected(appointment) response_serializer = AppointmentRequestSerializer(appointment) return Response(response_serializer.data) class AvailableDatesView(generics.GenericAPIView): permission_classes = [AllowAny] def get(self, request): availability = AdminWeeklyAvailability.objects.first() if not availability: return Response([]) available_days = availability.available_days today = timezone.now().date() available_dates = [] for i in range(1, 31): date = today + timedelta(days=i) if date.weekday() in available_days: available_dates.append(date.strftime('%Y-%m-%d')) return Response(available_dates) class UserAppointmentsView(generics.ListAPIView): permission_classes = [IsAuthenticated] serializer_class = AppointmentRequestSerializer def get_queryset(self): return AppointmentRequest.objects.filter( email=self.request.user.email ).order_by('-created_at') class AppointmentStatsView(generics.GenericAPIView): permission_classes = [IsAuthenticated, IsAdminUser] def get(self, request): total = AppointmentRequest.objects.count() users = CustomUser.objects.filter(is_staff=False).count() pending = AppointmentRequest.objects.filter(status='pending_review').count() scheduled = AppointmentRequest.objects.filter(status='scheduled').count() rejected = AppointmentRequest.objects.filter(status='rejected').count() return Response({ 'total_requests': total, 'pending_review': pending, 'scheduled': scheduled, 'rejected': rejected, 'users': users, 'completion_rate': round((scheduled / total * 100), 2) if total > 0 else 0 }) class UserAppointmentStatsView(generics.GenericAPIView): permission_classes = [IsAuthenticated] def post(self, request): email = request.data.get('email') stats = AppointmentRequest.objects.filter( email=email ).aggregate( total=Count('id'), pending=Count('id', filter=Q(status='pending_review')), scheduled=Count('id', filter=Q(status='scheduled')), rejected=Count('id', filter=Q(status='rejected')), completed=Count('id', filter=Q(status='completed')) ) total = stats['total'] scheduled = stats['scheduled'] completion_rate = round((scheduled / total * 100), 2) if total > 0 else 0 return Response({ 'total_requests': total, 'pending_review': stats['pending'], 'scheduled': scheduled, 'rejected': stats['rejected'], 'completed': stats['completed'], 'completion_rate': completion_rate })