alwrity chatbot assistant, content scheduler, and content repurposing

This commit is contained in:
ajaysi
2025-06-02 00:00:18 +05:30
parent 889021c078
commit 5ca2fd5977
69 changed files with 13952 additions and 3279 deletions

View File

@@ -0,0 +1,403 @@
"""
Conflict resolution system for content scheduling.
"""
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional, Tuple
from dataclasses import dataclass
# Use unified database models
from lib.database.models import ContentItem, Schedule, ScheduleStatus
logger = logging.getLogger(__name__)
@dataclass
class ConflictInfo:
"""Information about a scheduling conflict."""
schedule_1: Schedule
schedule_2: Schedule
conflict_type: str
severity: str
description: str
suggested_resolution: str
class ConflictResolver:
"""Resolve scheduling conflicts automatically."""
def __init__(self):
"""Initialize the conflict resolver."""
self.logger = logger
self.resolution_strategies = {
'time_overlap': self._resolve_time_overlap,
'platform_conflict': self._resolve_platform_conflict,
'resource_conflict': self._resolve_resource_conflict,
'priority_conflict': self._resolve_priority_conflict
}
def detect_conflicts(self, schedules: List[Schedule]) -> List[ConflictInfo]:
"""Detect conflicts between schedules.
Args:
schedules: List of Schedule objects to check
Returns:
List of detected conflicts
"""
try:
conflicts = []
# Sort schedules by time
sorted_schedules = sorted(schedules, key=lambda x: x.scheduled_time)
for i in range(len(sorted_schedules)):
for j in range(i + 1, len(sorted_schedules)):
schedule_1 = sorted_schedules[i]
schedule_2 = sorted_schedules[j]
# Check for time overlap conflicts
time_conflicts = self._check_time_overlap(schedule_1, schedule_2)
conflicts.extend(time_conflicts)
# Check for platform conflicts
platform_conflicts = self._check_platform_conflict(schedule_1, schedule_2)
conflicts.extend(platform_conflicts)
# Check for priority conflicts
priority_conflicts = self._check_priority_conflict(schedule_1, schedule_2)
conflicts.extend(priority_conflicts)
return conflicts
except Exception as e:
self.logger.error(f"Error detecting conflicts: {str(e)}")
return []
def _check_time_overlap(self, schedule_1: Schedule, schedule_2: Schedule) -> List[ConflictInfo]:
"""Check for time overlap conflicts."""
conflicts = []
try:
# Assume each schedule takes 1 hour (can be made configurable)
duration = timedelta(hours=1)
end_1 = schedule_1.scheduled_time + duration
end_2 = schedule_2.scheduled_time + duration
# Check for overlap
if (schedule_1.scheduled_time < end_2 and end_1 > schedule_2.scheduled_time):
time_diff = abs((schedule_2.scheduled_time - schedule_1.scheduled_time).total_seconds() / 60)
severity = 'high' if time_diff < 30 else 'medium'
conflicts.append(ConflictInfo(
schedule_1=schedule_1,
schedule_2=schedule_2,
conflict_type='time_overlap',
severity=severity,
description=f"Schedules overlap by {60 - time_diff:.0f} minutes",
suggested_resolution=f"Move one schedule by at least {60 - time_diff + 15:.0f} minutes"
))
except Exception as e:
self.logger.error(f"Error checking time overlap: {str(e)}")
return conflicts
def _check_platform_conflict(self, schedule_1: Schedule, schedule_2: Schedule) -> List[ConflictInfo]:
"""Check for platform conflicts."""
conflicts = []
try:
# This is a placeholder - platform conflicts would depend on specific platform limitations
# For now, we'll check if schedules are too close on the same platform
time_diff = abs((schedule_2.scheduled_time - schedule_1.scheduled_time).total_seconds() / 60)
# If schedules are within 15 minutes, it might be a platform conflict
if time_diff < 15:
conflicts.append(ConflictInfo(
schedule_1=schedule_1,
schedule_2=schedule_2,
conflict_type='platform_conflict',
severity='medium',
description=f"Schedules too close for optimal platform performance",
suggested_resolution="Space schedules at least 15 minutes apart"
))
except Exception as e:
self.logger.error(f"Error checking platform conflict: {str(e)}")
return conflicts
def _check_priority_conflict(self, schedule_1: Schedule, schedule_2: Schedule) -> List[ConflictInfo]:
"""Check for priority conflicts."""
conflicts = []
try:
# Check if high priority items are scheduled too close to low priority items
if schedule_1.priority > 7 and schedule_2.priority < 4:
time_diff = abs((schedule_2.scheduled_time - schedule_1.scheduled_time).total_seconds() / 60)
if time_diff < 60: # Within 1 hour
conflicts.append(ConflictInfo(
schedule_1=schedule_1,
schedule_2=schedule_2,
conflict_type='priority_conflict',
severity='low',
description="High priority content scheduled close to low priority content",
suggested_resolution="Consider spacing high and low priority content further apart"
))
except Exception as e:
self.logger.error(f"Error checking priority conflict: {str(e)}")
return conflicts
def resolve_conflicts(self, conflicts: List[ConflictInfo]) -> Dict[str, Any]:
"""Resolve detected conflicts automatically.
Args:
conflicts: List of conflicts to resolve
Returns:
Dictionary containing resolution results
"""
try:
resolved_conflicts = []
unresolved_conflicts = []
schedule_adjustments = {}
for conflict in conflicts:
try:
# Get resolution strategy
strategy = self.resolution_strategies.get(conflict.conflict_type)
if strategy:
resolution = strategy(conflict)
if resolution['success']:
resolved_conflicts.append({
'conflict': conflict,
'resolution': resolution
})
# Track schedule adjustments
for schedule_id, adjustments in resolution.get('adjustments', {}).items():
if schedule_id not in schedule_adjustments:
schedule_adjustments[schedule_id] = {}
schedule_adjustments[schedule_id].update(adjustments)
else:
unresolved_conflicts.append(conflict)
else:
unresolved_conflicts.append(conflict)
except Exception as e:
self.logger.error(f"Error resolving conflict: {str(e)}")
unresolved_conflicts.append(conflict)
return {
'resolved_conflicts': resolved_conflicts,
'unresolved_conflicts': unresolved_conflicts,
'schedule_adjustments': schedule_adjustments,
'success_rate': len(resolved_conflicts) / len(conflicts) if conflicts else 1.0
}
except Exception as e:
self.logger.error(f"Error resolving conflicts: {str(e)}")
return {
'resolved_conflicts': [],
'unresolved_conflicts': conflicts,
'schedule_adjustments': {},
'success_rate': 0.0
}
def _resolve_time_overlap(self, conflict: ConflictInfo) -> Dict[str, Any]:
"""Resolve time overlap conflicts."""
try:
# Strategy: Move the lower priority schedule
schedule_1 = conflict.schedule_1
schedule_2 = conflict.schedule_2
# Determine which schedule to move
if schedule_1.priority >= schedule_2.priority:
schedule_to_move = schedule_2
anchor_schedule = schedule_1
else:
schedule_to_move = schedule_1
anchor_schedule = schedule_2
# Calculate new time (move 1.5 hours after anchor)
new_time = anchor_schedule.scheduled_time + timedelta(hours=1.5)
return {
'success': True,
'strategy': 'move_lower_priority',
'adjustments': {
str(schedule_to_move.id): {
'new_scheduled_time': new_time,
'reason': 'Resolved time overlap conflict'
}
},
'description': f"Moved schedule {schedule_to_move.id} to {new_time}"
}
except Exception as e:
self.logger.error(f"Error resolving time overlap: {str(e)}")
return {'success': False, 'error': str(e)}
def _resolve_platform_conflict(self, conflict: ConflictInfo) -> Dict[str, Any]:
"""Resolve platform conflicts."""
try:
# Strategy: Space schedules 20 minutes apart
schedule_1 = conflict.schedule_1
schedule_2 = conflict.schedule_2
# Move the later schedule
if schedule_1.scheduled_time < schedule_2.scheduled_time:
schedule_to_move = schedule_2
anchor_time = schedule_1.scheduled_time
else:
schedule_to_move = schedule_1
anchor_time = schedule_2.scheduled_time
new_time = anchor_time + timedelta(minutes=20)
return {
'success': True,
'strategy': 'space_schedules',
'adjustments': {
str(schedule_to_move.id): {
'new_scheduled_time': new_time,
'reason': 'Resolved platform conflict'
}
},
'description': f"Spaced schedule {schedule_to_move.id} to {new_time}"
}
except Exception as e:
self.logger.error(f"Error resolving platform conflict: {str(e)}")
return {'success': False, 'error': str(e)}
def _resolve_resource_conflict(self, conflict: ConflictInfo) -> Dict[str, Any]:
"""Resolve resource conflicts."""
try:
# This is a placeholder for resource conflict resolution
return {
'success': False,
'reason': 'Resource conflict resolution not implemented'
}
except Exception as e:
self.logger.error(f"Error resolving resource conflict: {str(e)}")
return {'success': False, 'error': str(e)}
def _resolve_priority_conflict(self, conflict: ConflictInfo) -> Dict[str, Any]:
"""Resolve priority conflicts."""
try:
# Strategy: Move low priority content away from high priority content
schedule_1 = conflict.schedule_1
schedule_2 = conflict.schedule_2
# Identify high and low priority schedules
if schedule_1.priority > schedule_2.priority:
high_priority = schedule_1
low_priority = schedule_2
else:
high_priority = schedule_2
low_priority = schedule_1
# Move low priority content 2 hours away
new_time = high_priority.scheduled_time + timedelta(hours=2)
return {
'success': True,
'strategy': 'separate_priorities',
'adjustments': {
str(low_priority.id): {
'new_scheduled_time': new_time,
'reason': 'Resolved priority conflict'
}
},
'description': f"Moved low priority schedule {low_priority.id} to {new_time}"
}
except Exception as e:
self.logger.error(f"Error resolving priority conflict: {str(e)}")
return {'success': False, 'error': str(e)}
def suggest_optimal_schedule(
self,
new_schedule: Schedule,
existing_schedules: List[Schedule]
) -> Dict[str, Any]:
"""Suggest optimal scheduling for new content.
Args:
new_schedule: New schedule to optimize
existing_schedules: List of existing schedules
Returns:
Dictionary containing optimization suggestions
"""
try:
suggestions = []
# Check for conflicts with proposed time
all_schedules = existing_schedules + [new_schedule]
conflicts = self.detect_conflicts(all_schedules)
if not conflicts:
return {
'optimal_time': new_schedule.scheduled_time,
'conflicts': [],
'suggestions': ['Current time is optimal']
}
# Generate alternative times
base_time = new_schedule.scheduled_time
alternative_times = []
# Try different time slots
for hours_offset in [1, 2, 3, -1, -2, -3]:
alt_time = base_time + timedelta(hours=hours_offset)
alt_schedule = Schedule(
content_item_id=new_schedule.content_item_id,
scheduled_time=alt_time,
status=new_schedule.status,
recurrence=new_schedule.recurrence,
priority=new_schedule.priority
)
# Check conflicts for this alternative
alt_conflicts = self.detect_conflicts(existing_schedules + [alt_schedule])
alternative_times.append({
'time': alt_time,
'conflicts': len(alt_conflicts),
'severity': max([c.severity for c in alt_conflicts], default='none')
})
# Sort by number of conflicts and severity
alternative_times.sort(key=lambda x: (x['conflicts'], x['severity']))
optimal_time = alternative_times[0]['time'] if alternative_times else new_schedule.scheduled_time
return {
'optimal_time': optimal_time,
'conflicts': conflicts,
'alternatives': alternative_times[:3], # Top 3 alternatives
'suggestions': [
f"Consider scheduling at {optimal_time}",
f"Current time has {len(conflicts)} conflicts",
"Review alternative times for better optimization"
]
}
except Exception as e:
self.logger.error(f"Error suggesting optimal schedule: {str(e)}")
return {
'optimal_time': new_schedule.scheduled_time,
'conflicts': [],
'suggestions': ['Error occurred during optimization']
}