|
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() |
|
) |
|
|
|
|
|
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]) -> 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: |
|
|
|
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}" |
|
|
|
|
|
adjusted_time = self._calculate_adjusted_time(formatted_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 |
|
""" |
|
|
|
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)}") |