from flask import Blueprint, request, jsonify, current_app from flask_jwt_extended import jwt_required, get_jwt_identity from backend.services.schedule_service import ScheduleService from backend.celery_tasks.schedule_loader import load_schedules_task schedules_bp = Blueprint('schedules', __name__) @schedules_bp.route('/', methods=['OPTIONS']) @schedules_bp.route('', methods=['OPTIONS']) def handle_options(): """Handle OPTIONS requests for preflight CORS checks.""" return '', 200 @schedules_bp.route('/', methods=['GET']) @schedules_bp.route('', methods=['GET']) @jwt_required() def get_schedules(): """ Get all schedules for the current user. Returns: JSON: List of schedules """ try: user_id = get_jwt_identity() print(f"[DEBUG] get_schedules called for user_id: {user_id}") # Check if Supabase client is initialized if not hasattr(current_app, 'supabase') or current_app.supabase is None: print("[ERROR] Supabase client not initialized") # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': 'Database connection not initialized' }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 500 schedule_service = ScheduleService() schedules = schedule_service.get_user_schedules(user_id) print(f"[DEBUG] Found {len(schedules)} schedules for user {user_id}") # Add CORS headers explicitly response_data = jsonify({ 'success': True, 'schedules': schedules }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 200 except Exception as e: print(f"[ERROR] Get schedules error: {str(e)}") import traceback print(f"[ERROR] Full traceback: {traceback.format_exc()}") current_app.logger.error(f"Get schedules error: {str(e)}") # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': f'An error occurred while fetching schedules: {str(e)}', 'schedules': [] # Return empty array on error }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 500 @schedules_bp.route('/', methods=['POST']) @schedules_bp.route('', methods=['POST']) @jwt_required() def create_schedule(): """ Create a new schedule for the current user. Request Body: social_network (str): Social account ID schedule_time (str): Schedule time in format "HH:MM" days (List[str]): List of days to schedule Returns: JSON: Create schedule result """ try: user_id = get_jwt_identity() data = request.get_json() # Validate required fields required_fields = ['social_network', 'schedule_time', 'days'] if not data or not all(k in data for k in required_fields): # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': 'Social network, schedule time, and days are required' }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 400 social_network = data['social_network'] schedule_time = data['schedule_time'] days = data['days'] # Validate days format valid_days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] if not isinstance(days, list) or not all(day in valid_days for day in days): # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': 'Days must be a list of valid day names' }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 400 # Validate time format try: hour, minute = map(int, schedule_time.split(':')) if hour < 0 or hour > 23 or minute < 0 or minute > 59: raise ValueError except ValueError: # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': 'Schedule time must be in format HH:MM (24-hour format)' }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 400 # Create schedule using schedule service schedule_service = ScheduleService() result = schedule_service.create_schedule(user_id, social_network, schedule_time, days) if result['success']: # Trigger immediate Celery Beat schedule update try: print("[INFO] Triggering immediate Celery Beat schedule update...") # Execute the schedule loader task immediately to update Celery Beat celery_result = load_schedules_task.delay() print(f"[INFO] Celery Beat update task queued: {celery_result.id}") # Add the task ID to the response for tracking result['celery_update_task_id'] = celery_result.id result['message'] += ' (Scheduler updated immediately)' except Exception as e: print(f"[WARNING] Failed to trigger immediate Celery update: {str(e)}") # Don't fail the schedule creation if Celery update fails result['message'] += ' (Note: Scheduler update will occur in 5 minutes)' # Add CORS headers to success response response_data = jsonify(result) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 201 else: # Add CORS headers to error response response_data = jsonify(result) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 400 except Exception as e: current_app.logger.error(f"Create schedule error: {str(e)}") # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': f'An error occurred while creating schedule: {str(e)}' }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 500 @schedules_bp.route('/', methods=['OPTIONS']) def handle_schedule_options(schedule_id): """Handle OPTIONS requests for preflight CORS checks for specific schedule.""" return '', 200 @schedules_bp.route('/', methods=['DELETE']) @jwt_required() def delete_schedule(schedule_id): """ Delete a schedule. Path Parameters: schedule_id (str): Schedule ID Returns: JSON: Delete schedule result """ try: user_id = get_jwt_identity() # Verify the schedule belongs to the user response = ( current_app.supabase .table("Scheduling") .select("Social_network(id_utilisateur)") .eq("id", schedule_id) .execute() ) if not response.data: # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': 'Schedule not found' }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 404 schedule = response.data[0] if schedule.get('Social_network', {}).get('id_utilisateur') != user_id: # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': 'Unauthorized to delete this schedule' }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 403 # Delete schedule using schedule service schedule_service = ScheduleService() result = schedule_service.delete_schedule(schedule_id) if result['success']: # Trigger immediate Celery Beat schedule update try: print("[INFO] Triggering immediate Celery Beat schedule update after deletion...") # Execute the schedule loader task immediately to update Celery Beat celery_result = load_schedules_task.delay() print(f"[INFO] Celery Beat update task queued: {celery_result.id}") # Add the task ID to the response for tracking result['celery_update_task_id'] = celery_result.id result['message'] += ' (Scheduler updated immediately)' except Exception as e: print(f"[WARNING] Failed to trigger immediate Celery update: {str(e)}") # Don't fail the schedule deletion if Celery update fails result['message'] += ' (Note: Scheduler update will occur in 5 minutes)' # Add CORS headers to success response response_data = jsonify(result) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 200 else: # Add CORS headers to error response response_data = jsonify(result) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 404 except Exception as e: current_app.logger.error(f"Delete schedule error: {str(e)}") # Add CORS headers to error response response_data = jsonify({ 'success': False, 'message': f'An error occurred while deleting schedule: {str(e)}' }) response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') response_data.headers.add('Access-Control-Allow-Credentials', 'true') return response_data, 500