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 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() ) # Filter schedules for the user 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: # Log the error for debugging print(f"[ERROR] Failed to fetch schedules: {str(e)}") # Return empty list instead of raising exception return [] def create_schedule(self, user_id: str, social_network: str, schedule_time: str, days: List[str]) -> 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 Returns: Dict: Created schedule """ try: # Validate that social_network is a valid ID if not social_network or not isinstance(social_network, str): raise Exception("Invalid social account ID provided") # Try to convert social_network to int for database query try: social_account_id = int(social_network) except ValueError: raise Exception(f"Invalid social account ID format: {social_network}. Expected a numeric ID.") # Get social account - verify it belongs to the current user 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") # Verify the account belongs to the current user 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'] # Create schedules for each day created_schedules = [] for day in days: # Format schedule time formatted_schedule = f"{day} {schedule_time}" # Calculate adjusted time (5 minutes before for content generation) adjusted_time = self._calculate_adjusted_time(formatted_schedule) # Insert schedule response = ( self.supabase .table("Scheduling") .insert({ "id_social": social_account_id, "schedule_time": formatted_schedule, "adjusted_time": adjusted_time }) .execute() ) 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). Args: schedule_time (str): Original schedule time Returns: str: Adjusted time """ # Parse schedule 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(':')) # Subtract 5 minutes for content generation 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() ) # Normalize the data 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" }) # Select relevant columns 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)}")