237 lines
8.1 KiB
Python
237 lines
8.1 KiB
Python
import logging
|
|
import sys
|
|
|
|
logging.basicConfig(
|
|
level=logging.DEBUG,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
handlers=[
|
|
logging.StreamHandler(sys.stdout),
|
|
logging.FileHandler('content_calendar_debug.log', mode='a')
|
|
]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
from datetime import datetime
|
|
from typing import Dict, List, Any, Optional
|
|
from dataclasses import dataclass, field
|
|
from enum import Enum
|
|
import pandas as pd
|
|
|
|
class ContentType(Enum):
|
|
"""Types of content that can be scheduled."""
|
|
BLOG_POST = "blog_post"
|
|
SOCIAL_MEDIA = "social_media"
|
|
VIDEO = "video"
|
|
PODCAST = "podcast"
|
|
NEWSLETTER = "newsletter"
|
|
LANDING_PAGE = "landing_page"
|
|
|
|
class Platform(Enum):
|
|
"""Supported content platforms."""
|
|
WEBSITE = "website"
|
|
FACEBOOK = "facebook"
|
|
TWITTER = "twitter"
|
|
LINKEDIN = "linkedin"
|
|
INSTAGRAM = "instagram"
|
|
YOUTUBE = "youtube"
|
|
MEDIUM = "medium"
|
|
|
|
@dataclass
|
|
class SEOData:
|
|
"""SEO-related data for content."""
|
|
title: str
|
|
meta_description: str
|
|
keywords: List[str]
|
|
structured_data: Dict[str, Any]
|
|
canonical_url: Optional[str] = None
|
|
og_tags: Optional[Dict[str, str]] = None
|
|
twitter_cards: Optional[Dict[str, str]] = None
|
|
|
|
@staticmethod
|
|
def from_dict(data):
|
|
return SEOData(
|
|
title=data.get('title', ''),
|
|
meta_description=data.get('meta_description', ''),
|
|
keywords=data.get('keywords', []),
|
|
structured_data=data.get('structured_data', {}),
|
|
canonical_url=data.get('canonical_url'),
|
|
og_tags=data.get('og_tags'),
|
|
twitter_cards=data.get('twitter_cards')
|
|
)
|
|
|
|
@dataclass
|
|
class ContentItem:
|
|
"""Represents a single content item in the calendar."""
|
|
title: str
|
|
description: str
|
|
content_type: ContentType
|
|
platforms: List[Platform]
|
|
publish_date: datetime
|
|
seo_data: SEOData
|
|
status: str = "draft"
|
|
author: Optional[str] = None
|
|
tags: List[str] = field(default_factory=list)
|
|
notes: Optional[str] = None
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert content item to dictionary."""
|
|
return {
|
|
'title': self.title,
|
|
'description': self.description,
|
|
'content_type': self.content_type.value,
|
|
'platforms': [p.value for p in self.platforms],
|
|
'publish_date': self.publish_date.isoformat(),
|
|
'seo_data': {
|
|
'title': self.seo_data.title,
|
|
'meta_description': self.seo_data.meta_description,
|
|
'keywords': self.seo_data.keywords,
|
|
'structured_data': self.seo_data.structured_data,
|
|
'canonical_url': self.seo_data.canonical_url,
|
|
'og_tags': self.seo_data.og_tags,
|
|
'twitter_cards': self.seo_data.twitter_cards
|
|
},
|
|
'status': self.status,
|
|
'author': self.author,
|
|
'tags': self.tags,
|
|
'notes': self.notes
|
|
}
|
|
|
|
@staticmethod
|
|
def from_dict(data):
|
|
from .calendar import ContentType, Platform, SEOData
|
|
return ContentItem(
|
|
title=data['title'],
|
|
description=data.get('description', ''),
|
|
content_type=ContentType(data['content_type']),
|
|
platforms=[Platform(p) for p in data['platforms']],
|
|
publish_date=pd.to_datetime(data['publish_date']),
|
|
seo_data=SEOData.from_dict(data.get('seo_data', {})),
|
|
status=data.get('status', 'draft'),
|
|
author=data.get('author'),
|
|
tags=data.get('tags', []),
|
|
notes=data.get('notes')
|
|
)
|
|
|
|
@dataclass
|
|
class Calendar:
|
|
"""Represents a content calendar."""
|
|
start_date: datetime
|
|
duration: str # 'weekly', 'monthly', 'quarterly'
|
|
platforms: List[Platform]
|
|
schedule: Dict[str, List[ContentItem]]
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
|
|
def __init__(self, start_date: datetime, duration: str, platforms: List[Platform],
|
|
schedule: Dict[str, List[ContentItem]], name: Optional[str] = None,
|
|
description: Optional[str] = None):
|
|
"""Initialize a new calendar.
|
|
|
|
Args:
|
|
start_date: Start date of the calendar
|
|
duration: Duration of the calendar ('weekly', 'monthly', 'quarterly')
|
|
platforms: List of platforms to schedule content for
|
|
schedule: Dictionary mapping dates to content items
|
|
name: Optional name for the calendar
|
|
description: Optional description of the calendar
|
|
"""
|
|
self.start_date = start_date
|
|
self.duration = duration
|
|
self.platforms = platforms
|
|
self.schedule = schedule
|
|
self.name = name
|
|
self.description = description
|
|
self.content_items: List[ContentItem] = []
|
|
self.logger = logging.getLogger('content_calendar.calendar')
|
|
|
|
# Initialize content_items from schedule
|
|
for items in self.schedule.values():
|
|
self.content_items.extend(items)
|
|
|
|
def get_all_content(self) -> List[ContentItem]:
|
|
"""Get all content items in the calendar.
|
|
|
|
Returns:
|
|
List of all ContentItem objects in the calendar
|
|
"""
|
|
try:
|
|
self.logger.debug(f"Getting all content items. Count: {len(self.content_items)}")
|
|
return self.content_items
|
|
except Exception as e:
|
|
self.logger.error(f"Error getting all content: {str(e)}")
|
|
return []
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert calendar to dictionary."""
|
|
return {
|
|
'name': self.name,
|
|
'description': self.description,
|
|
'start_date': self.start_date.isoformat(),
|
|
'duration': self.duration,
|
|
'platforms': [p.value for p in self.platforms],
|
|
'schedule': {
|
|
date: [item.to_dict() for item in items]
|
|
for date, items in self.schedule.items()
|
|
}
|
|
}
|
|
|
|
def export(self, format: str = 'json') -> Dict[str, Any]:
|
|
"""
|
|
Export calendar in specified format.
|
|
Currently only supports JSON format.
|
|
"""
|
|
if format.lower() != 'json':
|
|
raise ValueError(f"Unsupported export format: {format}")
|
|
|
|
return self.to_dict()
|
|
|
|
def get_content_for_date(self, date: datetime) -> List[ContentItem]:
|
|
"""Get all content items scheduled for a specific date."""
|
|
date_str = date.strftime('%Y-%m-%d')
|
|
return self.schedule.get(date_str, [])
|
|
|
|
def get_content_for_platform(
|
|
self,
|
|
platform: Platform
|
|
) -> List[ContentItem]:
|
|
"""Get all content items for a specific platform."""
|
|
all_content = []
|
|
for items in self.schedule.values():
|
|
platform_content = [
|
|
item for item in items
|
|
if platform in item.platforms
|
|
]
|
|
all_content.extend(platform_content)
|
|
return all_content
|
|
|
|
def add_content(self, content: ContentItem) -> None:
|
|
"""Add a new content item to the calendar."""
|
|
date_str = content.publish_date.strftime('%Y-%m-%d')
|
|
if date_str not in self.schedule:
|
|
self.schedule[date_str] = []
|
|
self.schedule[date_str].append(content)
|
|
|
|
def remove_content(self, content: ContentItem) -> None:
|
|
"""Remove a content item from the calendar."""
|
|
date_str = content.publish_date.strftime('%Y-%m-%d')
|
|
if date_str in self.schedule:
|
|
self.schedule[date_str] = [
|
|
item for item in self.schedule[date_str]
|
|
if item != content
|
|
]
|
|
|
|
@staticmethod
|
|
def from_dict(data):
|
|
from .calendar import ContentItem, Platform
|
|
schedule = {
|
|
date: [ContentItem.from_dict(item) for item in items]
|
|
for date, items in data.get('schedule', {}).items()
|
|
}
|
|
return Calendar(
|
|
start_date=pd.to_datetime(data['start_date']),
|
|
duration=data['duration'],
|
|
platforms=[Platform(p) for p in data['platforms']],
|
|
schedule=schedule,
|
|
name=data.get('name'),
|
|
description=data.get('description')
|
|
) |