| 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.utils.timezone_utils import validate_timezone, get_server_timezone, format_timezone_schedule |
| import logging |
|
|
| schedules_bp = Blueprint('schedules', __name__) |
| logger = logging.getLogger(__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() |
| logger.info(f"π Fetching schedules for user: {user_id}") |
| |
| |
| if not hasattr(current_app, 'supabase') or current_app.supabase is None: |
| logger.error("β Supabase client not initialized") |
| |
| 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) |
| logger.info(f"β
Found {len(schedules)} schedules for user {user_id}") |
| |
| |
| 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: |
| logger.error(f"β Get schedules error: {str(e)}") |
| import traceback |
| logger.error(f"Full traceback: {traceback.format_exc()}") |
| current_app.logger.error(f"Get schedules error: {str(e)}") |
| |
| response_data = jsonify({ |
| 'success': False, |
| 'message': f'An error occurred while fetching schedules: {str(e)}', |
| 'schedules': [] |
| }) |
| 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() |
| |
| |
| required_fields = ['social_network', 'schedule_time', 'days'] |
| if not data or not all(k in data for k in required_fields): |
| |
| 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 |
| |
| |
| user_timezone = data.get('timezone', get_server_timezone()) |
| |
| print(f"[DEBUG] Received timezone: {user_timezone}") |
| print(f"[DEBUG] All request data: {data}") |
| |
| |
| if user_timezone and not validate_timezone(user_timezone): |
| |
| response_data = jsonify({ |
| 'success': False, |
| 'message': f'Invalid timezone: {user_timezone}' |
| }) |
| 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'] |
| |
| |
| 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): |
| |
| 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 |
| |
| |
| try: |
| hour, minute = map(int, schedule_time.split(':')) |
| if hour < 0 or hour > 23 or minute < 0 or minute > 59: |
| raise ValueError |
| except ValueError: |
| |
| 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 |
| |
| |
| schedule_service = ScheduleService() |
| result = schedule_service.create_schedule(user_id, social_network, schedule_time, days, user_timezone) |
| |
| if result['success']: |
| |
| try: |
| logger.info("π Triggering immediate APScheduler update...") |
| if hasattr(current_app, 'scheduler'): |
| scheduler_updated = current_app.scheduler.trigger_immediate_update() |
| if scheduler_updated: |
| result['message'] += ' (Scheduler updated immediately)' |
| else: |
| result['message'] += ' (Note: Scheduler update will occur in 5 minutes)' |
| else: |
| result['message'] += ' (Note: Scheduler update will occur in 5 minutes)' |
| except Exception as e: |
| logger.warning(f"β οΈ Failed to trigger immediate scheduler update: {str(e)}") |
| |
| result['message'] += ' (Note: Scheduler update will occur in 5 minutes)' |
| |
| |
| 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: |
| |
| 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)}") |
| |
| 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('/<schedule_id>', methods=['OPTIONS']) |
| def handle_schedule_options(schedule_id): |
| """Handle OPTIONS requests for preflight CORS checks for specific schedule.""" |
| return '', 200 |
|
|
| @schedules_bp.route('/<schedule_id>', 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() |
| |
| |
| response = ( |
| current_app.supabase |
| .table("Scheduling") |
| .select("Social_network(id_utilisateur)") |
| .eq("id", schedule_id) |
| .execute() |
| ) |
| |
| if not response.data: |
| |
| 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: |
| |
| 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 |
| |
| |
| schedule_service = ScheduleService() |
| result = schedule_service.delete_schedule(schedule_id) |
| |
| if result['success']: |
| |
| try: |
| logger.info("π Triggering immediate APScheduler update after deletion...") |
| if hasattr(current_app, 'scheduler'): |
| scheduler_updated = current_app.scheduler.trigger_immediate_update() |
| if scheduler_updated: |
| result['message'] += ' (Scheduler updated immediately)' |
| else: |
| result['message'] += ' (Note: Scheduler update will occur in 5 minutes)' |
| else: |
| result['message'] += ' (Note: Scheduler update will occur in 5 minutes)' |
| except Exception as e: |
| logger.warning(f"β οΈ Failed to trigger immediate scheduler update: {str(e)}") |
| |
| result['message'] += ' (Note: Scheduler update will occur in 5 minutes)' |
| |
| |
| 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: |
| |
| 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)}") |
| |
| 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 |