| from flask import current_app |
| from datetime import datetime, timedelta |
| from typing import List, Dict |
| import pandas as pd |
| from backend.models.schedule import Schedule |
| from backend.utils.timezone_utils import ( |
| validate_timezone, |
| format_timezone_schedule, |
| calculate_adjusted_time_with_timezone, |
| get_server_timezone, |
| parse_timezone_schedule |
| ) |
|
|
| class ScheduleService: |
| """Service for managing post scheduling.""" |
| |
| def __init__(self): |
| self.supabase = current_app.supabase |
| |
| def get_user_schedules(self, user_id: str) -> List[Dict]: |
| """ |
| Get all schedules for a user. |
| |
| Args: |
| user_id (str): User ID |
| |
| Returns: |
| List[Dict]: List of schedules |
| """ |
| try: |
| response = ( |
| self.supabase |
| .table("Scheduling") |
| .select("*, Social_network(id_utilisateur, social_network, account_name)") |
| .execute() |
| ) |
| |
| |
| schedules = [] |
| for item in response.data: |
| if item.get('Social_network', {}).get('id_utilisateur') == user_id: |
| schedule = Schedule.from_dict({ |
| 'id': item['id'], |
| 'social_account_id': item['id_social'], |
| 'schedule_time': item['schedule_time'], |
| 'adjusted_time': item['adjusted_time'], |
| 'created_at': item['created_at'] |
| }) |
| schedules.append(schedule.to_dict()) |
| |
| return schedules |
| |
| except Exception as e: |
| |
| print(f"[ERROR] Failed to fetch schedules: {str(e)}") |
| |
| return [] |
| |
| def create_schedule(self, user_id: str, social_network: str, schedule_time: str, days: List[str], timezone: str = None) -> Dict: |
| """ |
| Create a new schedule. |
| |
| Args: |
| user_id (str): User ID |
| social_network (str): Social account ID (as string) |
| schedule_time (str): Schedule time in format "HH:MM" |
| days (List[str]): List of days to schedule |
| timezone (str): User's timezone (optional) |
| |
| Returns: |
| Dict: Created schedule |
| """ |
| try: |
| |
| if not social_network or not isinstance(social_network, str): |
| raise Exception("Invalid social account ID provided") |
| |
| |
| try: |
| social_account_id = int(social_network) |
| except ValueError: |
| raise Exception(f"Invalid social account ID format: {social_network}. Expected a numeric ID.") |
| |
| |
| account_response = ( |
| self.supabase |
| .table("Social_network") |
| .select("id, id_utilisateur") |
| .eq("id", social_account_id) |
| .execute() |
| ) |
| |
| if not account_response.data: |
| raise Exception("Social account not found") |
| |
| |
| account = account_response.data[0] |
| if str(account.get('id_utilisateur')) != str(user_id): |
| raise Exception("Social account not found or unauthorized") |
| |
| social_account_id = account['id'] |
| |
| |
| created_schedules = [] |
| |
| for day in days: |
| |
| formatted_schedule = f"{day} {schedule_time}" |
| |
| |
| if timezone: |
| formatted_schedule = format_timezone_schedule(formatted_schedule, timezone) |
| print(f"[DEBUG] Formatted schedule with timezone: {formatted_schedule}") |
| else: |
| print(f"[DEBUG] No timezone provided for schedule: {formatted_schedule}") |
| |
| |
| adjusted_time = calculate_adjusted_time_with_timezone(formatted_schedule, timezone) |
| print(f"[DEBUG] Adjusted time: {adjusted_time}") |
| |
| |
| response = ( |
| self.supabase |
| .table("Scheduling") |
| .insert({ |
| "id_social": social_account_id, |
| "schedule_time": formatted_schedule, |
| "adjusted_time": adjusted_time |
| }) |
| .execute() |
| ) |
| |
| print(f"[DEBUG] Database response: {response.data}") |
| |
| if response.data: |
| schedule = Schedule.from_dict({ |
| 'id': response.data[0]['id'], |
| 'social_account_id': response.data[0]['id_social'], |
| 'schedule_time': response.data[0]['schedule_time'], |
| 'adjusted_time': response.data[0]['adjusted_time'], |
| 'created_at': response.data[0]['created_at'] |
| }) |
| created_schedules.append(schedule.to_dict()) |
| |
| return { |
| 'success': True, |
| 'schedules': created_schedules, |
| 'message': f'Schedule created successfully for {len(created_schedules)} day(s)' |
| } |
| |
| except Exception as e: |
| raise Exception(f"Failed to create schedule: {str(e)}") |
| |
| def delete_schedule(self, schedule_id: str) -> Dict: |
| """ |
| Delete a schedule. |
| |
| Args: |
| schedule_id (str): Schedule ID |
| |
| Returns: |
| Dict: Deletion result |
| """ |
| try: |
| response = ( |
| self.supabase |
| .table("Scheduling") |
| .delete() |
| .eq("id", schedule_id) |
| .execute() |
| ) |
| |
| if response.data: |
| return { |
| 'success': True, |
| 'message': 'Schedule deleted successfully' |
| } |
| else: |
| return { |
| 'success': False, |
| 'message': 'Schedule not found' |
| } |
| |
| except Exception as e: |
| raise Exception(f"Failed to delete schedule: {str(e)}") |
| |
| def _calculate_adjusted_time(self, schedule_time: str) -> str: |
| """ |
| Calculate adjusted time for content generation (5 minutes before schedule). |
| Legacy method - kept for backward compatibility. |
| |
| Args: |
| schedule_time (str): Original schedule time |
| |
| Returns: |
| str: Adjusted time |
| """ |
| |
| parts = schedule_time.strip().split() |
| if len(parts) != 2 or ':' not in parts[1]: |
| return schedule_time |
| |
| day, time_part = parts |
| try: |
| hour, minute = map(int, time_part.split(':')) |
| |
| adjusted_minute = minute - 5 |
| adjusted_hour = hour |
| |
| if adjusted_minute < 0: |
| adjusted_minute += 60 |
| adjusted_hour -= 1 |
| if adjusted_hour < 0: |
| adjusted_hour += 24 |
| |
| return f"{day} {adjusted_hour:02d}:{adjusted_minute:02d}" |
| except ValueError: |
| return schedule_time |
| |
| def get_all_schedules(self) -> pd.DataFrame: |
| """ |
| Get all schedules for the scheduler. |
| |
| Returns: |
| pd.DataFrame: DataFrame with all schedules |
| """ |
| try: |
| response = ( |
| self.supabase |
| .table("Scheduling") |
| .select("*, Social_network(id_utilisateur, account_name)") |
| .execute() |
| ) |
| |
| |
| data = response.data |
| df = pd.json_normalize(data) |
| |
| if not df.empty: |
| df = df.rename(columns={ |
| "Social_network.id_utilisateur": "user_id", |
| "Social_network.account_name": "social_network" |
| }) |
| |
| |
| cols = ["id", "id_social", "user_id", "schedule_time", "social_network", "adjusted_time", "created_at"] |
| df = df[[c for c in cols if c in df.columns]] |
| |
| return df |
| |
| except Exception as e: |
| raise Exception(f"Failed to fetch all schedules: {str(e)}") |