from rest_framework import viewsets, status, filters from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.permissions import AllowAny, IsAdminUser from rest_framework.parsers import MultiPartParser, FormParser, JSONParser from django_filters.rest_framework import DjangoFilterBackend from django.shortcuts import get_object_or_404 import logging from .models import JobPosition, JobApplication from .serializers import ( JobPositionListSerializer, JobPositionDetailSerializer, JobApplicationSerializer, JobApplicationListSerializer ) from .email_service import CareerEmailService logger = logging.getLogger(__name__) class JobPositionViewSet(viewsets.ReadOnlyModelViewSet): """ ViewSet for job positions GET /api/career/jobs/ - List all active job positions GET /api/career/jobs/{slug}/ - Get job position by slug """ queryset = JobPosition.objects.filter(status='active') permission_classes = [AllowAny] lookup_field = 'slug' filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['department', 'employment_type', 'location_type', 'featured'] search_fields = ['title', 'department', 'location', 'short_description'] ordering_fields = ['posted_date', 'priority', 'title'] ordering = ['-priority', '-posted_date'] def get_serializer_class(self): if self.action == 'retrieve': return JobPositionDetailSerializer return JobPositionListSerializer @action(detail=False, methods=['get']) def featured(self, request): """Get featured job positions""" featured_jobs = self.queryset.filter(featured=True) serializer = self.get_serializer(featured_jobs, many=True) return Response(serializer.data) @action(detail=True, methods=['get']) def applications_count(self, request, slug=None): """Get number of applications for a job""" job = self.get_object() count = job.applications.count() return Response({'count': count}) class JobApplicationViewSet(viewsets.ModelViewSet): """ ViewSet for job applications POST /api/career/applications/ - Submit a job application GET /api/career/applications/ - List applications (admin only) """ queryset = JobApplication.objects.all() serializer_class = JobApplicationSerializer parser_classes = [MultiPartParser, FormParser, JSONParser] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['job', 'status'] search_fields = ['first_name', 'last_name', 'email', 'job__title'] ordering_fields = ['applied_date', 'status'] ordering = ['-applied_date'] def get_permissions(self): """ Allow anyone to create (submit) applications Only admins can list/view/update/delete applications """ if self.action == 'create': return [AllowAny()] return [IsAdminUser()] def get_serializer_class(self): if self.action == 'list': return JobApplicationListSerializer return JobApplicationSerializer def create(self, request, *args, **kwargs): """Submit a job application""" try: # Build data dict - Django QueryDict returns lists, so we need to get first item data = {} # Get POST data (text fields) for key in request.POST.keys(): value = request.POST.get(key) data[key] = value # Get FILES data for key in request.FILES.keys(): file_obj = request.FILES.get(key) data[key] = file_obj except Exception as e: logger.error(f"Error parsing request: {str(e)}") return Response( {'error': 'Error parsing request data'}, status=status.HTTP_400_BAD_REQUEST ) serializer = self.get_serializer(data=data) if not serializer.is_valid(): logger.error(f"Validation errors: {serializer.errors}") return Response( {'error': 'Validation failed', 'details': serializer.errors}, status=status.HTTP_400_BAD_REQUEST ) try: # Save the application application = serializer.save() logger.info(f"New job application received: {application.full_name} for {application.job.title}") # Try to send email notifications (non-blocking - don't fail if emails fail) try: email_service = CareerEmailService() email_service.send_application_confirmation(application) email_service.send_application_notification_to_admin(application) logger.info(f"Email notifications sent successfully for application {application.id}") except Exception as email_error: # Log email error but don't fail the application submission logger.warning(f"Failed to send email notifications for application {application.id}: {str(email_error)}") return Response( { 'message': 'Application submitted successfully', 'data': serializer.data }, status=status.HTTP_201_CREATED ) except Exception as e: logger.error(f"Error submitting job application: {str(e)}", exc_info=True) return Response( {'error': 'Failed to submit application. Please try again.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) @action(detail=True, methods=['post']) def update_status(self, request, pk=None): """Update application status""" application = self.get_object() new_status = request.data.get('status') if new_status not in dict(JobApplication.STATUS_CHOICES): return Response( {'error': 'Invalid status'}, status=status.HTTP_400_BAD_REQUEST ) application.status = new_status application.save() serializer = self.get_serializer(application) return Response(serializer.data)