ALwrity Version 0.5.0 (Fastapi + React )
This commit is contained in:
198
lib/content_calendar/utils/date_utils.py
Normal file
198
lib/content_calendar/utils/date_utils.py
Normal file
@@ -0,0 +1,198 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Any
|
||||
import calendar
|
||||
import random
|
||||
|
||||
def calculate_publish_dates(
|
||||
topics: List[Dict[str, Any]],
|
||||
start_date: datetime,
|
||||
duration: str
|
||||
) -> Dict[str, List[Dict[str, Any]]]:
|
||||
"""
|
||||
Calculate optimal publish dates for content topics.
|
||||
|
||||
Args:
|
||||
topics: List of content topics to schedule
|
||||
start_date: When to start publishing
|
||||
duration: How long the calendar should span ('weekly', 'monthly', 'quarterly')
|
||||
|
||||
Returns:
|
||||
Dictionary mapping dates to scheduled content
|
||||
"""
|
||||
# Calculate end date based on duration
|
||||
end_date = _calculate_end_date(start_date, duration)
|
||||
|
||||
# Get all dates in range
|
||||
dates = _get_dates_in_range(start_date, end_date)
|
||||
|
||||
# Calculate optimal posting frequency
|
||||
frequency = _calculate_posting_frequency(len(topics), len(dates))
|
||||
|
||||
# Schedule content
|
||||
schedule = _schedule_content(topics, dates, frequency)
|
||||
|
||||
return schedule
|
||||
|
||||
def _calculate_end_date(start_date: datetime, duration: str) -> datetime:
|
||||
"""Calculate end date based on duration."""
|
||||
if duration == 'weekly':
|
||||
return start_date + timedelta(days=7)
|
||||
elif duration == 'monthly':
|
||||
# Add one month
|
||||
if start_date.month == 12:
|
||||
return datetime(start_date.year + 1, 1, start_date.day)
|
||||
return datetime(start_date.year, start_date.month + 1, start_date.day)
|
||||
elif duration == 'quarterly':
|
||||
# Add three months
|
||||
new_month = start_date.month + 3
|
||||
new_year = start_date.year
|
||||
if new_month > 12:
|
||||
new_month -= 12
|
||||
new_year += 1
|
||||
return datetime(new_year, new_month, start_date.day)
|
||||
else:
|
||||
raise ValueError(f"Invalid duration: {duration}")
|
||||
|
||||
def _get_dates_in_range(
|
||||
start_date: datetime,
|
||||
end_date: datetime
|
||||
) -> List[datetime]:
|
||||
"""Get all dates in the given range."""
|
||||
dates = []
|
||||
current_date = start_date
|
||||
|
||||
while current_date <= end_date:
|
||||
# Skip weekends
|
||||
if current_date.weekday() < 5: # 0-4 are weekdays
|
||||
dates.append(current_date)
|
||||
current_date += timedelta(days=1)
|
||||
|
||||
return dates
|
||||
|
||||
def _calculate_posting_frequency(
|
||||
num_topics: int,
|
||||
num_dates: int
|
||||
) -> Dict[str, int]:
|
||||
"""
|
||||
Calculate optimal posting frequency based on number of topics and dates.
|
||||
|
||||
Returns:
|
||||
Dictionary with posting frequency for each content type
|
||||
"""
|
||||
# Calculate base frequency
|
||||
base_frequency = num_dates / num_topics
|
||||
|
||||
# Adjust for content types
|
||||
return {
|
||||
'blog_post': max(1, int(base_frequency * 0.4)), # 40% of content
|
||||
'social_media': max(1, int(base_frequency * 0.3)), # 30% of content
|
||||
'video': max(1, int(base_frequency * 0.2)), # 20% of content
|
||||
'newsletter': max(1, int(base_frequency * 0.1)) # 10% of content
|
||||
}
|
||||
|
||||
def _schedule_content(
|
||||
topics: List[Dict[str, Any]],
|
||||
dates: List[datetime],
|
||||
frequency: Dict[str, int]
|
||||
) -> Dict[str, List[Dict[str, Any]]]:
|
||||
"""
|
||||
Schedule content topics across available dates.
|
||||
|
||||
Args:
|
||||
topics: List of content topics to schedule
|
||||
dates: Available dates for scheduling
|
||||
frequency: Posting frequency for each content type
|
||||
|
||||
Returns:
|
||||
Dictionary mapping dates to scheduled content
|
||||
"""
|
||||
schedule = {}
|
||||
current_date_index = 0
|
||||
|
||||
# Group topics by content type
|
||||
topics_by_type = _group_topics_by_type(topics)
|
||||
|
||||
# Schedule each content type
|
||||
for content_type, type_topics in topics_by_type.items():
|
||||
type_frequency = frequency.get(content_type, 1)
|
||||
|
||||
for topic in type_topics:
|
||||
# Find next available date
|
||||
while current_date_index < len(dates):
|
||||
date = dates[current_date_index]
|
||||
date_str = date.strftime('%Y-%m-%d')
|
||||
|
||||
# Check if date is available
|
||||
if date_str not in schedule:
|
||||
schedule[date_str] = []
|
||||
|
||||
# Add topic to schedule
|
||||
schedule[date_str].append(topic)
|
||||
|
||||
# Move to next date based on frequency
|
||||
current_date_index += type_frequency
|
||||
break
|
||||
|
||||
# If we've used all dates, wrap around
|
||||
if current_date_index >= len(dates):
|
||||
current_date_index = 0
|
||||
|
||||
return schedule
|
||||
|
||||
def _group_topics_by_type(
|
||||
topics: List[Dict[str, Any]]
|
||||
) -> Dict[str, List[Dict[str, Any]]]:
|
||||
"""Group topics by their content type."""
|
||||
grouped = {}
|
||||
|
||||
for topic in topics:
|
||||
content_type = topic.get('content_type', 'blog_post')
|
||||
if content_type not in grouped:
|
||||
grouped[content_type] = []
|
||||
grouped[content_type].append(topic)
|
||||
|
||||
return grouped
|
||||
|
||||
def get_optimal_posting_time(
|
||||
content_type: str,
|
||||
platform: str
|
||||
) -> datetime.time:
|
||||
"""
|
||||
Get optimal posting time for content type and platform.
|
||||
|
||||
Args:
|
||||
content_type: Type of content
|
||||
platform: Target platform
|
||||
|
||||
Returns:
|
||||
Optimal time to post
|
||||
"""
|
||||
# Default optimal times (can be customized based on platform analytics)
|
||||
optimal_times = {
|
||||
'blog_post': {
|
||||
'website': datetime.time(9, 0), # 9 AM
|
||||
'medium': datetime.time(10, 0) # 10 AM
|
||||
},
|
||||
'social_media': {
|
||||
'facebook': datetime.time(15, 0), # 3 PM
|
||||
'twitter': datetime.time(12, 0), # 12 PM
|
||||
'linkedin': datetime.time(8, 0), # 8 AM
|
||||
'instagram': datetime.time(19, 0) # 7 PM
|
||||
},
|
||||
'video': {
|
||||
'youtube': datetime.time(14, 0) # 2 PM
|
||||
},
|
||||
'newsletter': {
|
||||
'email': datetime.time(6, 0) # 6 AM
|
||||
}
|
||||
}
|
||||
|
||||
# Get optimal time for content type and platform
|
||||
content_times = optimal_times.get(content_type, {})
|
||||
optimal_time = content_times.get(platform)
|
||||
|
||||
if optimal_time is None:
|
||||
# Default to 9 AM if no specific time is set
|
||||
optimal_time = datetime.time(9, 0)
|
||||
|
||||
return optimal_time
|
||||
Reference in New Issue
Block a user