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 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 class AdminAvailabilityView(generics.RetrieveUpdateAPIView): permission_classes = [IsAuthenticated] 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' @api_view(['POST']) @permission_classes([IsAuthenticated]) def schedule_appointment(request, pk): try: appointment = AppointmentRequest.objects.get(pk=pk) except AppointmentRequest.DoesNotExist: return Response({'error': 'Appointment not found'}, status=status.HTTP_404_NOT_FOUND) if appointment.status != 'pending_review': return Response( {'error': 'Only pending review appointments can be scheduled.'}, status=status.HTTP_400_BAD_REQUEST ) serializer = AppointmentScheduleSerializer(data=request.data) if serializer.is_valid(): 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 }) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['POST']) @permission_classes([IsAuthenticated]) def reject_appointment(request, pk): try: appointment = AppointmentRequest.objects.get(pk=pk) except AppointmentRequest.DoesNotExist: return Response({'error': 'Appointment not found'}, status=status.HTTP_404_NOT_FOUND) if appointment.status != 'pending_review': return Response( {'error': 'Only pending appointments can be rejected'}, status=status.HTTP_400_BAD_REQUEST ) serializer = AppointmentRejectSerializer(data=request.data) if serializer.is_valid(): appointment.reject_appointment(serializer.validated_data.get('rejection_reason', '')) EmailService.send_appointment_rejected(appointment) return Response(AppointmentRequestSerializer(appointment).data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['GET']) @permission_classes([AllowAny]) def available_dates(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) @api_view(['GET']) @permission_classes([IsAuthenticated]) def user_appointments(request): appointments = AppointmentRequest.objects.filter( email=request.user.email ).order_by('-created_at') serializer = AppointmentRequestSerializer(appointments, many=True) return Response(serializer.data) @api_view(['GET']) @permission_classes([IsAuthenticated]) def appointment_stats(request): if not request.user.is_staff: return Response( {'error': 'Unauthorized'}, status=status.HTTP_403_FORBIDDEN ) 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 }) @api_view(['GET']) @permission_classes([IsAuthenticated]) def user_apointment_stats(request): if not request.user.is_staff: return Response( {'error': 'Unauthorized'}, status=status.HTTP_403_FORBIDDEN ) total = AppointmentRequest.objects.filter(email=request.user.email).count() pending = AppointmentRequest.objects.filter(email=request.user.email, status='pending_review').count() scheduled = AppointmentRequest.objects.filter(email=request.user.email, status='scheduled').count() rejected = AppointmentRequest.objects.filter(email=request.user.email, status='rejected').count() completed = AppointmentRequest.objects.filter(email=request.user.email, status='completed').count() return Response({ 'total_requests': total, 'pending_review': pending, 'scheduled': scheduled, 'rejected': rejected, 'completed': completed, 'completion_rate': round((scheduled / total * 100), 2) if total > 0 else 0 })