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

@@ -9,7 +9,7 @@ parent_dir = str(Path(__file__).parent.parent.parent.parent)
if parent_dir not in sys.path:
sys.path.append(parent_dir)
from lib.ai_seo_tools.content_calendar.models.calendar import ContentType, ContentItem, Platform
from lib.database.models import ContentType, ContentItem, Platform
from lib.ai_seo_tools.content_calendar.utils.error_handling import handle_calendar_error
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
from lib.ai_seo_tools.content_gap_analysis.main import ContentGapAnalysis
@@ -570,22 +570,6 @@ class AIGenerator:
) -> List[Dict[str, Any]]:
"""
Generate AI content suggestions based on input parameters.
Args:
content_type: Type of content to generate
topic: Main topic or subject
audience: Target audience
goals: List of content goals
tone: Desired tone
length: Content length
model_settings: AI model settings
style_preferences: Style preferences
seo_preferences: SEO preferences
platform_settings: Platform-specific settings
platform: Target platform
Returns:
List of generated content suggestions
"""
try:
self.logger.info(f"Generating AI suggestions for topic: {topic}")
@@ -601,14 +585,14 @@ Tone: {tone}
Length: {length}
Style Preferences:
- Creativity Level: {model_settings['Creativity Level']}
- Formality Level: {model_settings['Formality Level']}
- Creativity Level: {model_settings.get('Creativity Level', 'medium')}
- Formality Level: {model_settings.get('Formality Level', 'professional')}
- Style Elements: {', '.join(style_preferences)}
SEO Preferences:
- Keyword Density: {seo_preferences['Keyword Density']}%
- Internal Linking: {'Enabled' if seo_preferences['Internal Linking'] else 'Disabled'}
- External Linking: {'Enabled' if seo_preferences['External Linking'] else 'Disabled'}
- Keyword Density: {seo_preferences.get('Keyword Density', 2)}%
- Internal Linking: {'Enabled' if seo_preferences.get('Internal Linking', True) else 'Disabled'}
- External Linking: {'Enabled' if seo_preferences.get('External Linking', True) else 'Disabled'}
Platform Settings:
- Platform: {platform}
@@ -645,55 +629,20 @@ Please generate 3 different content suggestions. Format your response as a valid
IMPORTANT: Your response must be a valid JSON object. Do not include any text before or after the JSON object."""
# Define JSON structure for validation
json_struct = {
"type": "object",
"properties": {
"suggestions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {"type": "string"},
"introduction": {"type": "string"},
"key_points": {"type": "array", "items": {"type": "string"}},
"main_sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {"type": "string"},
"content": {"type": "string"},
"engagement_elements": {"type": "array", "items": {"type": "string"}},
"seo_elements": {"type": "array", "items": {"type": "string"}}
}
}
},
"conclusion": {"type": "string"},
"seo_elements": {"type": "array", "items": {"type": "string"}},
"platform_optimizations": {"type": "array", "items": {"type": "string"}},
"engagement_strategies": {"type": "array", "items": {"type": "string"}},
"content_metrics": {
"type": "object",
"properties": {
"estimated_read_time": {"type": "string"},
"word_count": {"type": "number"},
"keyword_density": {"type": "number"},
"engagement_score": {"type": "number"}
}
}
}
}
}
}
}
# Generate content using llm_text_gen with JSON structure
generated_content = llm_text_gen(prompt, json_struct=json_struct)
# Generate content using llm_text_gen
generated_content = llm_text_gen(
prompt=prompt,
max_tokens=1000,
temperature=0.7,
top_p=0.9,
frequency_penalty=0.5,
presence_penalty=0.5
)
if not generated_content:
raise ValueError("Failed to generate content suggestions")
self.logger.error("No content generated from AI model")
return []
# Parse the generated content
try:
# If generated_content is already a dict, use it directly
@@ -703,6 +652,10 @@ IMPORTANT: Your response must be a valid JSON object. Do not include any text be
# Try to parse as JSON string
content_data = json.loads(generated_content)
if not content_data or 'suggestions' not in content_data:
self.logger.error("Invalid content structure in AI response")
return []
return self._format_suggestions(
content_data,
content_type,
@@ -725,6 +678,9 @@ IMPORTANT: Your response must be a valid JSON object. Do not include any text be
if start >= 0 and end > start:
json_str = generated_content[start:end]
content_data = json.loads(json_str)
if not content_data or 'suggestions' not in content_data:
self.logger.error("Invalid content structure in extracted JSON")
return []
return self._format_suggestions(
content_data,
content_type,
@@ -738,11 +694,11 @@ IMPORTANT: Your response must be a valid JSON object. Do not include any text be
)
except Exception as e2:
self.logger.error(f"Error extracting JSON from response: {str(e2)}")
raise ValueError("Failed to parse generated content")
return []
except Exception as e:
self.logger.error(f"Error generating AI suggestions: {str(e)}", exc_info=True)
raise
return []
def _format_suggestions(
self,

View File

@@ -4,10 +4,9 @@ import logging
import sys
import json
import os
from lib.database.models import ContentItem, ContentType, Platform, get_engine, get_session, init_db
from ..integrations.seo_tools import SEOToolsIntegration
from ..integrations.gap_analyzer import GapAnalyzerIntegration
from ..models.calendar import Calendar, ContentItem
from ..utils.date_utils import calculate_publish_dates
from ..utils.error_handling import handle_calendar_error
@@ -21,24 +20,22 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
CALENDAR_JSON_PATH = "calendar_data.json"
engine = get_engine()
init_db(engine)
session = get_session(engine)
class CalendarManager:
"""
Main calendar management system that coordinates content planning,
scheduling, and optimization.
"""
def __init__(self):
"""Initialize calendar manager."""
self.logger = logging.getLogger('content_calendar.manager')
self.logger.info("Initializing CalendarManager")
self.seo_tools = SEOToolsIntegration()
self.gap_analyzer = GapAnalyzerIntegration()
self._calendar: Optional[Calendar] = None
self.logger.info("CalendarManager initialized successfully")
@handle_calendar_error
def create_calendar(
self,
@@ -46,136 +43,107 @@ class CalendarManager:
duration: str, # 'weekly', 'monthly', 'quarterly'
platforms: List[str],
website_url: str
) -> Calendar:
"""
Create a new content calendar based on content gap analysis and SEO requirements.
Args:
start_date: When the calendar should begin
duration: How long the calendar should span
platforms: List of platforms to create content for
website_url: URL of the website to analyze
Returns:
Calendar object containing the content schedule
"""
) -> List[ContentItem]:
self.logger.info(f"Creating new calendar for {website_url}")
self.logger.debug(f"Parameters: start_date={start_date}, duration={duration}, platforms={platforms}")
try:
# 1. Analyze content gaps
self.logger.info("Analyzing content gaps")
gap_analysis = self.gap_analyzer.analyze_gaps(website_url)
# 2. Generate topics based on gaps
self.logger.info("Generating topics from gap analysis")
topics = self._generate_topics(gap_analysis, platforms)
# 3. Calculate publish dates
self.logger.info("Calculating publish dates")
schedule = calculate_publish_dates(
topics=topics,
start_date=start_date,
duration=duration
)
# 4. Create calendar
self.logger.info("Creating calendar object")
self._calendar = Calendar(
start_date=start_date,
duration=duration,
platforms=platforms,
schedule=schedule
)
self.logger.info("Calendar created successfully")
return self._calendar
# Add to DB
for topic in schedule:
session.add(topic)
session.commit()
self.logger.info("Calendar created and content scheduled in DB successfully")
return schedule
except Exception as e:
self.logger.error(f"Error creating calendar: {str(e)}", exc_info=True)
raise
def _generate_topics(
self,
gap_analysis: Dict[str, Any],
platforms: List[str]
) -> List[ContentItem]:
"""
Generate content topics based on gap analysis and platform requirements.
"""
topics = []
for gap in gap_analysis['gaps']:
# Generate topic using AI
topic = self._generate_topic_from_gap(gap, platforms)
# Optimize for SEO
optimized_topic = self._optimize_topic(topic)
topics.append(optimized_topic)
return topics
def _generate_topic_from_gap(
self,
gap: Dict[str, Any],
platforms: List[str]
) -> ContentItem:
"""
Generate a specific topic based on a content gap.
"""
# Use existing AI tools to generate topic
topic_data = {
'title': self._generate_title(gap),
'description': self._generate_description(gap),
'keywords': gap.get('keywords', []),
'platforms': platforms,
'content_type': self._determine_content_type(gap, platforms)
'content_type': self._determine_content_type(gap, platforms),
'publish_date': datetime.now(),
'status': 'Draft',
'author': None,
'tags': [],
'notes': None,
'seo_data': {}
}
return ContentItem(**topic_data)
def _optimize_topic(self, topic: ContentItem) -> ContentItem:
"""
Optimize a topic for SEO using existing tools.
"""
# Optimize title
topic.title = self.seo_tools.optimize_title(topic.title)
# Generate meta description
topic.meta_description = self.seo_tools.generate_meta_description(
topic.description
)
# Add structured data
topic.structured_data = self.seo_tools.generate_structured_data(
topic.content_type
)
topic.seo_data['meta_description'] = self.seo_tools.generate_meta_description(topic.description)
topic.seo_data['structured_data'] = self.seo_tools.generate_structured_data(topic.content_type)
return topic
def get_calendar(self) -> Optional[Calendar]:
def get_all_content(self) -> List[ContentItem]:
return session.query(ContentItem).all()
def remove_content(self, content_id):
content = session.query(ContentItem).get(content_id)
if content:
session.delete(content)
session.commit()
def update_content(self, content_id, **kwargs):
content = session.query(ContentItem).get(content_id)
if content:
for key, value in kwargs.items():
setattr(content, key, value)
session.commit()
def get_calendar(self) -> Optional[List[ContentItem]]:
"""
Get the current calendar.
"""
self.logger.debug("Getting current calendar")
return self._calendar
return self.get_all_content()
def update_calendar(self, calendar: Calendar) -> None:
def update_calendar(self, calendar: List[ContentItem]) -> None:
"""
Update the current calendar.
"""
self._calendar = calendar
self.get_all_content()
for content in calendar:
session.add(content)
session.commit()
def export_calendar(self) -> Optional[Dict[str, Any]]:
"""Export the current calendar."""
self.logger.info("Exporting calendar")
if not self._calendar:
calendar = self.get_calendar()
if not calendar:
self.logger.warning("No calendar to export")
return None
try:
calendar_data = self._calendar.export()
calendar_data = [content.to_dict() for content in calendar]
self.logger.info("Calendar exported successfully")
return calendar_data
except Exception as e:
@@ -185,12 +153,11 @@ class CalendarManager:
def save_calendar_to_json(self):
calendar = self.get_calendar()
if calendar:
with open(CALENDAR_JSON_PATH, "w") as f:
json.dump(calendar.to_dict(), f, indent=2, default=str)
with open("calendar_data.json", "w") as f:
json.dump(calendar, f, indent=2, default=str)
def load_calendar_from_json(self):
from lib.ai_seo_tools.content_calendar.models.calendar import Calendar
if os.path.exists(CALENDAR_JSON_PATH):
with open(CALENDAR_JSON_PATH, "r") as f:
if os.path.exists("calendar_data.json"):
with open("calendar_data.json", "r") as f:
data = json.load(f)
self._calendar = Calendar.from_dict(data)
self.update_calendar(data)

View File

@@ -8,7 +8,7 @@ parent_dir = str(Path(__file__).parent.parent.parent.parent)
if parent_dir not in sys.path:
sys.path.append(parent_dir)
from lib.ai_seo_tools.content_calendar.models.calendar import ContentType, ContentItem, Platform
from lib.database.models import ContentType, ContentItem, Platform
from lib.ai_seo_tools.content_calendar.utils.error_handling import handle_calendar_error
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
from lib.ai_seo_tools.content_gap_analysis.main import ContentGapAnalysis

View File

@@ -2,23 +2,25 @@ from typing import Dict, List, Any, Optional
import logging
from pathlib import Path
import sys
from datetime import datetime, timedelta
# Add parent directory to path to import existing tools
parent_dir = str(Path(__file__).parent.parent.parent.parent)
if parent_dir not in sys.path:
sys.path.append(parent_dir)
from ..models.calendar import ContentItem, ContentType
from lib.database.models import ContentItem, ContentType, Platform
from ..utils.error_handling import handle_calendar_error
from lib.ai_seo_tools.content_gap_analysis.main import ContentGapAnalysis
from lib.ai_seo_tools.content_title_generator import ai_title_generator
from lib.ai_seo_tools.meta_desc_generator import metadesc_generator_main
from lib.ai_seo_tools.content_calendar.core.content_repurposer import SmartContentRepurposingEngine
logger = logging.getLogger(__name__)
class ContentGenerator:
"""
AI-powered content generation for content briefs.
Enhanced content generator with smart repurposing capabilities.
"""
def __init__(self):
@@ -26,6 +28,8 @@ class ContentGenerator:
self.logger.info("Initializing ContentGenerator")
self._setup_logging()
self._load_ai_tools()
# Initialize the Smart Content Repurposing Engine
self.repurposing_engine = SmartContentRepurposingEngine()
def _setup_logging(self):
"""Configure logging for content generator."""
@@ -320,4 +324,303 @@ class ContentGenerator:
except Exception as e:
self.logger.error(f"Error generating variation: {str(e)}")
return {}
return {}
@handle_calendar_error
def repurpose_content_for_platforms(
self,
content_item: ContentItem,
target_platforms: List[Platform],
strategy: str = 'adaptive'
) -> List[ContentItem]:
"""
Repurpose existing content for multiple platforms using the Smart Content Repurposing Engine.
Args:
content_item: Original content to repurpose
target_platforms: List of platforms to create content for
strategy: Repurposing strategy ('adaptive', 'atomic', 'series')
Returns:
List of repurposed content items optimized for each platform
"""
try:
self.logger.info(f"Repurposing content '{content_item.title}' for {len(target_platforms)} platforms")
# Use the repurposing engine to create platform-specific content
repurposed_content = self.repurposing_engine.repurpose_single_content(
content=content_item,
target_platforms=target_platforms,
strategy=strategy
)
self.logger.info(f"Successfully created {len(repurposed_content)} repurposed content pieces")
return repurposed_content
except Exception as e:
self.logger.error(f"Error repurposing content: {str(e)}")
return []
@handle_calendar_error
def create_content_series_across_platforms(
self,
source_content: ContentItem,
platforms: List[Platform],
series_type: str = 'progressive_disclosure'
) -> Dict[str, List[ContentItem]]:
"""
Create a cross-platform content series with progressive disclosure strategy.
Args:
source_content: Original comprehensive content
platforms: Target platforms for the series
series_type: Type of series ('progressive_disclosure', 'platform_native')
Returns:
Dictionary mapping platforms to their content pieces
"""
try:
self.logger.info(f"Creating cross-platform series for '{source_content.title}'")
# Use the repurposing engine to create a content series
series_content = self.repurposing_engine.create_content_series(
content=source_content,
platforms=platforms,
series_type=series_type
)
total_pieces = sum(len(pieces) for pieces in series_content.values())
self.logger.info(f"Successfully created series with {total_pieces} pieces across {len(series_content)} platforms")
return series_content
except Exception as e:
self.logger.error(f"Error creating content series: {str(e)}")
return {}
@handle_calendar_error
def analyze_content_for_repurposing(
self,
content_item: ContentItem,
available_platforms: List[Platform]
) -> Dict[str, Any]:
"""
Analyze content and get AI-powered repurposing suggestions.
Args:
content_item: Content to analyze
available_platforms: Available platforms for repurposing
Returns:
Dictionary containing repurposing suggestions and analysis
"""
try:
self.logger.info(f"Analyzing content '{content_item.title}' for repurposing opportunities")
# Get repurposing suggestions from the engine
suggestions = self.repurposing_engine.get_repurposing_suggestions(
content=content_item,
available_platforms=available_platforms
)
# Add content analysis
content_text = content_item.description or content_item.notes or ""
content_atoms = self.repurposing_engine.analyze_content_atoms(
content=content_text,
title=content_item.title
)
analysis = {
'content_analysis': {
'word_count': len(content_text.split()) if content_text else 0,
'content_richness': self._assess_content_richness(content_atoms),
'repurposing_potential': self._assess_repurposing_potential(content_atoms),
'content_atoms': content_atoms
},
'platform_suggestions': suggestions['recommended_platforms'],
'strategy_suggestions': suggestions['repurposing_strategies'],
'estimated_output': {
'total_pieces': suggestions['estimated_pieces'],
'time_savings': f"{suggestions['estimated_pieces'] * 2} hours",
'content_multiplication': f"{suggestions['estimated_pieces']}x"
}
}
return analysis
except Exception as e:
self.logger.error(f"Error analyzing content for repurposing: {str(e)}")
return {}
def _assess_content_richness(self, content_atoms: Dict[str, List[str]]) -> str:
"""Assess the richness of content based on extracted atoms."""
total_atoms = sum(len(atoms) for atoms in content_atoms.values())
if total_atoms >= 15:
return "High"
elif total_atoms >= 8:
return "Medium"
else:
return "Low"
def _assess_repurposing_potential(self, content_atoms: Dict[str, List[str]]) -> str:
"""Assess the repurposing potential based on content atoms."""
# Check for diverse content types
atom_types_with_content = sum(1 for atoms in content_atoms.values() if atoms)
if atom_types_with_content >= 4:
return "Excellent"
elif atom_types_with_content >= 3:
return "Good"
elif atom_types_with_content >= 2:
return "Fair"
else:
return "Limited"
@handle_calendar_error
def generate_content_with_repurposing_plan(
self,
content_item: ContentItem,
context: Dict[str, Any],
target_platforms: List[Platform] = None
) -> Dict[str, Any]:
"""
Generate content along with a comprehensive repurposing plan.
Args:
content_item: Content item to generate
context: Content context from gap analysis
target_platforms: Platforms to include in repurposing plan
Returns:
Dictionary containing generated content and repurposing plan
"""
try:
self.logger.info(f"Generating content with repurposing plan for '{content_item.title}'")
# Generate the main content structure
headings = self.generate_headings(content_item, context)
subheadings = self.generate_subheadings(content_item, headings, context)
key_points = self.generate_key_points(content_item, context)
outline = {
'headings': headings,
'subheadings': subheadings,
'key_points': key_points
}
content_flow = self.generate_content_flow(content_item, outline)
# Create repurposing plan if platforms are specified
repurposing_plan = {}
if target_platforms:
# Analyze repurposing potential
analysis = self.analyze_content_for_repurposing(content_item, target_platforms)
# Generate repurposing suggestions
repurposing_plan = {
'analysis': analysis,
'recommended_strategy': self._recommend_repurposing_strategy(analysis),
'platform_roadmap': self._create_platform_roadmap(content_item, target_platforms),
'content_calendar_integration': self._suggest_calendar_integration(content_item, target_platforms)
}
return {
'content': {
'outline': outline,
'content_flow': content_flow,
'metadata': {
'generated_at': str(datetime.now()),
'content_type': content_item.content_type.name,
'platforms': [p.name for p in content_item.platforms] if content_item.platforms else []
}
},
'repurposing_plan': repurposing_plan
}
except Exception as e:
self.logger.error(f"Error generating content with repurposing plan: {str(e)}")
return {}
def _recommend_repurposing_strategy(self, analysis: Dict[str, Any]) -> str:
"""Recommend the best repurposing strategy based on content analysis."""
content_richness = analysis.get('content_analysis', {}).get('content_richness', 'Low')
repurposing_potential = analysis.get('content_analysis', {}).get('repurposing_potential', 'Limited')
if content_richness == 'High' and repurposing_potential in ['Excellent', 'Good']:
return 'progressive_disclosure'
elif content_richness in ['Medium', 'High']:
return 'adaptive'
else:
return 'atomic'
def _create_platform_roadmap(
self,
content_item: ContentItem,
target_platforms: List[Platform]
) -> Dict[str, Any]:
"""Create a roadmap for content distribution across platforms."""
roadmap = {
'timeline': {},
'platform_sequence': [],
'cross_promotion_opportunities': []
}
# Create a timeline for content release
base_date = content_item.publish_date or datetime.now()
for i, platform in enumerate(target_platforms):
release_date = base_date + timedelta(days=i)
roadmap['timeline'][platform.name] = {
'release_date': release_date.strftime('%Y-%m-%d'),
'content_type': self._get_optimal_content_type_for_platform(platform),
'engagement_strategy': self._get_engagement_strategy_for_platform(platform)
}
roadmap['platform_sequence'].append(platform.name)
return roadmap
def _suggest_calendar_integration(
self,
content_item: ContentItem,
target_platforms: List[Platform]
) -> Dict[str, Any]:
"""Suggest how to integrate repurposed content into the content calendar."""
return {
'scheduling_recommendations': {
'primary_content': 'Schedule as main content piece',
'repurposed_content': 'Schedule 1-2 days after primary content',
'series_content': 'Schedule weekly releases for maximum impact'
},
'calendar_tags': [
'repurposed_content',
f'source_{content_item.id}',
'multi_platform_series'
],
'performance_tracking': {
'metrics_to_track': ['engagement_rate', 'cross_platform_traffic', 'conversion_rate'],
'comparison_baseline': 'Compare against single-platform content performance'
}
}
def _get_optimal_content_type_for_platform(self, platform: Platform) -> str:
"""Get the optimal content type for a specific platform."""
platform_content_types = {
Platform.TWITTER: 'Thread or single tweet',
Platform.LINKEDIN: 'Professional post or article',
Platform.INSTAGRAM: 'Visual post with caption',
Platform.FACEBOOK: 'Engaging post with discussion starter',
Platform.WEBSITE: 'Full blog post or article'
}
return platform_content_types.get(platform, 'Standard post')
def _get_engagement_strategy_for_platform(self, platform: Platform) -> str:
"""Get the engagement strategy for a specific platform."""
engagement_strategies = {
Platform.TWITTER: 'Use hashtags, engage in conversations, create polls',
Platform.LINKEDIN: 'Professional networking, thought leadership, industry discussions',
Platform.INSTAGRAM: 'Visual storytelling, user-generated content, stories',
Platform.FACEBOOK: 'Community building, discussions, live interactions',
Platform.WEBSITE: 'SEO optimization, internal linking, lead magnets'
}
return engagement_strategies.get(platform, 'Standard engagement tactics')

View File

@@ -0,0 +1,599 @@
from typing import Dict, List, Any, Optional, Tuple
import logging
import re
from datetime import datetime, timedelta
from pathlib import Path
import sys
import json
# Add parent directory to path to import existing tools
parent_dir = str(Path(__file__).parent.parent.parent.parent)
if parent_dir not in sys.path:
sys.path.append(parent_dir)
from lib.database.models import ContentItem, ContentType, Platform, SEOData
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
from ..utils.error_handling import handle_calendar_error
logger = logging.getLogger(__name__)
class ContentAtomizer:
"""
Break down content into atomic pieces that can be recombined
for different platforms and purposes.
"""
def __init__(self):
self.logger = logging.getLogger('content_calendar.atomizer')
def atomize_content(self, content: str, title: str = "") -> Dict[str, List[str]]:
"""
Extract key quotes, statistics, tips, and examples from content.
Args:
content: The content text to atomize
title: The content title for context
Returns:
Dictionary containing different types of content atoms
"""
try:
self.logger.info(f"Atomizing content: {title[:50]}...")
# Use AI to extract content atoms
prompt = f"""
Analyze the following content and extract key elements that can be repurposed:
Title: {title}
Content: {content[:3000]}...
Extract and categorize the following elements:
1. Key Statistics (numbers, percentages, data points)
2. Quotable Insights (memorable quotes or key insights)
3. Actionable Tips (practical advice or steps)
4. Examples/Case Studies (real examples or stories)
5. Key Questions (thought-provoking questions)
6. Main Arguments (core points or arguments)
Format your response as JSON:
{{
"statistics": ["stat1", "stat2", ...],
"quotes": ["quote1", "quote2", ...],
"tips": ["tip1", "tip2", ...],
"examples": ["example1", "example2", ...],
"questions": ["question1", "question2", ...],
"arguments": ["argument1", "argument2", ...]
}}
"""
response = llm_text_gen(
prompt=prompt,
system_prompt="You are an expert content analyst. Extract key elements that can be repurposed across different platforms.",
json_struct={
"type": "object",
"properties": {
"statistics": {"type": "array", "items": {"type": "string"}},
"quotes": {"type": "array", "items": {"type": "string"}},
"tips": {"type": "array", "items": {"type": "string"}},
"examples": {"type": "array", "items": {"type": "string"}},
"questions": {"type": "array", "items": {"type": "string"}},
"arguments": {"type": "array", "items": {"type": "string"}}
}
}
)
if response:
return response
else:
# Fallback to basic extraction
return self._basic_content_extraction(content)
except Exception as e:
self.logger.error(f"Error atomizing content: {str(e)}")
return self._basic_content_extraction(content)
def _basic_content_extraction(self, content: str) -> Dict[str, List[str]]:
"""Fallback method for basic content extraction."""
atoms = {
"statistics": [],
"quotes": [],
"tips": [],
"examples": [],
"questions": [],
"arguments": []
}
# Extract statistics (numbers with %)
stats = re.findall(r'\d+%|\d+\.\d+%|\d+,\d+|\d+ percent', content)
atoms["statistics"] = stats[:5] # Limit to 5
# Extract questions
questions = re.findall(r'[A-Z][^.!?]*\?', content)
atoms["questions"] = questions[:3] # Limit to 3
# Extract sentences that might be tips (containing words like "should", "must", "need to")
tip_patterns = r'[^.!?]*(?:should|must|need to|important to|remember to)[^.!?]*[.!?]'
tips = re.findall(tip_patterns, content, re.IGNORECASE)
atoms["tips"] = tips[:5] # Limit to 5
return atoms
class ContentRepurposer:
"""
Main content repurposing engine that transforms content for different platforms.
"""
def __init__(self):
self.logger = logging.getLogger('content_calendar.repurposer')
self.atomizer = ContentAtomizer()
# Platform-specific content specifications
self.platform_specs = {
Platform.TWITTER: {
'max_length': 280,
'optimal_length': 240,
'format': 'concise',
'tone': 'engaging',
'hashtags': True,
'mentions': True
},
Platform.LINKEDIN: {
'max_length': 3000,
'optimal_length': 1500,
'format': 'professional',
'tone': 'authoritative',
'hashtags': True,
'mentions': False
},
Platform.INSTAGRAM: {
'max_length': 2200,
'optimal_length': 1000,
'format': 'visual-focused',
'tone': 'casual',
'hashtags': True,
'mentions': True
},
Platform.FACEBOOK: {
'max_length': 63206,
'optimal_length': 500,
'format': 'engaging',
'tone': 'conversational',
'hashtags': False,
'mentions': True
},
Platform.WEBSITE: {
'max_length': None,
'optimal_length': 2000,
'format': 'comprehensive',
'tone': 'informative',
'hashtags': False,
'mentions': False
}
}
@handle_calendar_error
def repurpose_content(
self,
source_content: ContentItem,
target_platforms: List[Platform],
repurpose_strategy: str = 'adaptive'
) -> List[ContentItem]:
"""
Repurpose content for multiple platforms.
Args:
source_content: Original content to repurpose
target_platforms: List of platforms to create content for
repurpose_strategy: Strategy for repurposing ('adaptive', 'atomic', 'series')
Returns:
List of repurposed content items
"""
try:
self.logger.info(f"Repurposing content '{source_content.title}' for {len(target_platforms)} platforms")
repurposed_content = []
# Get content text (assuming it's in description or notes)
content_text = source_content.description or source_content.notes or ""
if not content_text:
self.logger.warning("No content text found for repurposing")
return []
# Atomize the content
atoms = self.atomizer.atomize_content(content_text, source_content.title)
# Generate repurposed content for each platform
for platform in target_platforms:
if platform == source_content.platforms[0] if source_content.platforms else None:
continue # Skip the original platform
repurposed_item = self._create_platform_specific_content(
source_content=source_content,
target_platform=platform,
atoms=atoms,
strategy=repurpose_strategy
)
if repurposed_item:
repurposed_content.append(repurposed_item)
self.logger.info(f"Successfully repurposed content into {len(repurposed_content)} variations")
return repurposed_content
except Exception as e:
self.logger.error(f"Error repurposing content: {str(e)}")
return []
def _create_platform_specific_content(
self,
source_content: ContentItem,
target_platform: Platform,
atoms: Dict[str, List[str]],
strategy: str
) -> Optional[ContentItem]:
"""Create platform-specific content variation."""
try:
platform_spec = self.platform_specs.get(target_platform, {})
# Generate platform-specific content using AI
repurposed_text = self._generate_platform_content(
source_content=source_content,
target_platform=target_platform,
atoms=atoms,
platform_spec=platform_spec,
strategy=strategy
)
if not repurposed_text:
return None
# Create new content item
repurposed_item = ContentItem(
title=self._adapt_title_for_platform(source_content.title, target_platform),
description=repurposed_text,
content_type=self._determine_content_type_for_platform(target_platform),
platforms=[target_platform],
publish_date=source_content.publish_date + timedelta(days=1), # Schedule for next day
status="draft",
author=source_content.author,
tags=source_content.tags + [f"repurposed_from_{source_content.id}"],
notes=f"Repurposed from: {source_content.title}",
seo_data=self._adapt_seo_data_for_platform(source_content.seo_data, target_platform)
)
return repurposed_item
except Exception as e:
self.logger.error(f"Error creating platform-specific content: {str(e)}")
return None
def _generate_platform_content(
self,
source_content: ContentItem,
target_platform: Platform,
atoms: Dict[str, List[str]],
platform_spec: Dict[str, Any],
strategy: str
) -> str:
"""Generate content optimized for specific platform."""
try:
# Prepare content elements
title = source_content.title
original_content = source_content.description or ""
# Create platform-specific prompt
prompt = self._create_repurposing_prompt(
title=title,
original_content=original_content,
target_platform=target_platform,
atoms=atoms,
platform_spec=platform_spec,
strategy=strategy
)
# Generate content using AI
repurposed_content = llm_text_gen(prompt)
return repurposed_content or ""
except Exception as e:
self.logger.error(f"Error generating platform content: {str(e)}")
return ""
def _create_repurposing_prompt(
self,
title: str,
original_content: str,
target_platform: Platform,
atoms: Dict[str, List[str]],
platform_spec: Dict[str, Any],
strategy: str
) -> str:
"""Create AI prompt for content repurposing."""
platform_guidelines = {
Platform.TWITTER: "Create engaging tweets that drive conversation. Use threads for complex topics. Include relevant hashtags.",
Platform.LINKEDIN: "Write professional content that provides value to business professionals. Focus on insights and actionable advice.",
Platform.INSTAGRAM: "Create visually-oriented content with engaging captions. Use storytelling and include relevant hashtags.",
Platform.FACEBOOK: "Write conversational content that encourages engagement. Ask questions and create community discussion.",
Platform.WEBSITE: "Create comprehensive, SEO-optimized content with clear structure and valuable information."
}
atoms_text = ""
for atom_type, atom_list in atoms.items():
if atom_list:
atoms_text += f"\n{atom_type.title()}: {', '.join(atom_list[:3])}"
prompt = f"""
Repurpose the following content for {target_platform.name}:
Original Title: {title}
Original Content: {original_content[:1500]}...
Key Content Elements:{atoms_text}
Platform Guidelines: {platform_guidelines.get(target_platform, '')}
Platform Specifications:
- Optimal Length: {platform_spec.get('optimal_length', 'flexible')} characters
- Format: {platform_spec.get('format', 'standard')}
- Tone: {platform_spec.get('tone', 'professional')}
- Include Hashtags: {platform_spec.get('hashtags', False)}
Requirements:
1. Adapt the content to fit {target_platform.name}'s format and audience
2. Maintain the core message and value
3. Optimize for {target_platform.name} engagement
4. Include platform-appropriate calls to action
5. Use the extracted content elements effectively
Create compelling, platform-optimized content that will perform well on {target_platform.name}.
"""
return prompt
def _adapt_title_for_platform(self, original_title: str, platform: Platform) -> str:
"""Adapt title for specific platform."""
platform_prefixes = {
Platform.TWITTER: "🧵 ",
Platform.LINKEDIN: "💼 ",
Platform.INSTAGRAM: "📸 ",
Platform.FACEBOOK: "💬 ",
Platform.WEBSITE: ""
}
prefix = platform_prefixes.get(platform, "")
return f"{prefix}{original_title}"
def _determine_content_type_for_platform(self, platform: Platform) -> ContentType:
"""Determine appropriate content type for platform."""
platform_content_types = {
Platform.TWITTER: ContentType.SOCIAL_MEDIA,
Platform.LINKEDIN: ContentType.SOCIAL_MEDIA,
Platform.INSTAGRAM: ContentType.SOCIAL_MEDIA,
Platform.FACEBOOK: ContentType.SOCIAL_MEDIA,
Platform.WEBSITE: ContentType.BLOG_POST
}
return platform_content_types.get(platform, ContentType.SOCIAL_MEDIA)
def _adapt_seo_data_for_platform(self, original_seo: SEOData, platform: Platform) -> SEOData:
"""Adapt SEO data for specific platform."""
if platform == Platform.WEBSITE:
return original_seo
# For social media platforms, create simplified SEO data
return SEOData(
title=original_seo.title,
meta_description=original_seo.meta_description[:160] if original_seo.meta_description else "",
keywords=original_seo.keywords[:5] if original_seo.keywords else [],
structured_data={}
)
class ContentSeriesRepurposer:
"""
Create cross-platform content series with progressive disclosure strategy.
"""
def __init__(self):
self.logger = logging.getLogger('content_calendar.series_repurposer')
self.repurposer = ContentRepurposer()
def create_cross_platform_series(
self,
source_content: ContentItem,
platforms: List[Platform],
series_strategy: str = 'progressive_disclosure'
) -> Dict[str, List[ContentItem]]:
"""
Create a content series that progressively reveals information
across different platforms, driving traffic between them.
Args:
source_content: Original comprehensive content
platforms: Target platforms for the series
series_strategy: Strategy for content distribution
Returns:
Dictionary mapping platforms to their content pieces
"""
try:
self.logger.info(f"Creating cross-platform series for: {source_content.title}")
series_content = {}
if series_strategy == 'progressive_disclosure':
series_content = self._create_progressive_disclosure_series(
source_content, platforms
)
elif series_strategy == 'platform_native':
series_content = self._create_platform_native_series(
source_content, platforms
)
else:
# Default to simple repurposing
repurposed = self.repurposer.repurpose_content(
source_content, platforms
)
for item in repurposed:
platform = item.platforms[0]
if platform not in series_content:
series_content[platform] = []
series_content[platform].append(item)
return series_content
except Exception as e:
self.logger.error(f"Error creating cross-platform series: {str(e)}")
return {}
def _create_progressive_disclosure_series(
self,
source_content: ContentItem,
platforms: List[Platform]
) -> Dict[str, List[ContentItem]]:
"""Create series with progressive information disclosure."""
series_content = {}
# Define disclosure strategy
disclosure_strategy = {
Platform.TWITTER: "teaser", # Hook with key stat/question
Platform.INSTAGRAM: "visual", # Visual summary with key points
Platform.LINKEDIN: "insight", # Professional insight/analysis
Platform.FACEBOOK: "discussion", # Community discussion starter
Platform.WEBSITE: "complete" # Full detailed content
}
for platform in platforms:
strategy = disclosure_strategy.get(platform, "summary")
content_piece = self._create_disclosure_content(
source_content, platform, strategy
)
if content_piece:
series_content[platform] = [content_piece]
return series_content
def _create_disclosure_content(
self,
source_content: ContentItem,
platform: Platform,
disclosure_type: str
) -> Optional[ContentItem]:
"""Create content piece for specific disclosure strategy."""
try:
# This would use the repurposer with specific instructions
# for the disclosure type
repurposed = self.repurposer._create_platform_specific_content(
source_content=source_content,
target_platform=platform,
atoms=self.repurposer.atomizer.atomize_content(
source_content.description or "",
source_content.title
),
strategy=disclosure_type
)
return repurposed
except Exception as e:
self.logger.error(f"Error creating disclosure content: {str(e)}")
return None
def _create_platform_native_series(
self,
source_content: ContentItem,
platforms: List[Platform]
) -> Dict[str, List[ContentItem]]:
"""Create series optimized for each platform's native format."""
# Implementation for platform-native series
# This would create multiple pieces per platform
# optimized for that platform's specific characteristics
return {}
# Main repurposing interface
class SmartContentRepurposingEngine:
"""
Main interface for the Smart Content Repurposing Engine.
"""
def __init__(self):
self.logger = logging.getLogger('content_calendar.repurposing_engine')
self.repurposer = ContentRepurposer()
self.series_repurposer = ContentSeriesRepurposer()
self.atomizer = ContentAtomizer()
def repurpose_single_content(
self,
content: ContentItem,
target_platforms: List[Platform],
strategy: str = 'adaptive'
) -> List[ContentItem]:
"""Repurpose a single piece of content."""
return self.repurposer.repurpose_content(content, target_platforms, strategy)
def create_content_series(
self,
content: ContentItem,
platforms: List[Platform],
series_type: str = 'progressive_disclosure'
) -> Dict[str, List[ContentItem]]:
"""Create a cross-platform content series."""
return self.series_repurposer.create_cross_platform_series(
content, platforms, series_type
)
def analyze_content_atoms(self, content: str, title: str = "") -> Dict[str, List[str]]:
"""Analyze content and extract reusable atoms."""
return self.atomizer.atomize_content(content, title)
def get_repurposing_suggestions(
self,
content: ContentItem,
available_platforms: List[Platform]
) -> Dict[str, Any]:
"""Get AI-powered suggestions for content repurposing."""
try:
# Analyze content to suggest best repurposing strategies
content_text = content.description or content.notes or ""
atoms = self.atomizer.atomize_content(content_text, content.title)
suggestions = {
'recommended_platforms': [],
'repurposing_strategies': [],
'content_atoms': atoms,
'estimated_pieces': 0
}
# Analyze content type and suggest platforms
if content.content_type == ContentType.BLOG_POST:
suggestions['recommended_platforms'] = [
Platform.TWITTER, Platform.LINKEDIN, Platform.INSTAGRAM
]
suggestions['estimated_pieces'] = len(available_platforms) * 2
elif content.content_type == ContentType.VIDEO:
suggestions['recommended_platforms'] = [
Platform.TWITTER, Platform.INSTAGRAM, Platform.FACEBOOK
]
suggestions['estimated_pieces'] = len(available_platforms) * 3
# Suggest strategies based on content richness
if len(atoms.get('statistics', [])) > 3:
suggestions['repurposing_strategies'].append('data_driven')
if len(atoms.get('tips', [])) > 5:
suggestions['repurposing_strategies'].append('tip_series')
if len(atoms.get('examples', [])) > 2:
suggestions['repurposing_strategies'].append('case_study_series')
return suggestions
except Exception as e:
self.logger.error(f"Error getting repurposing suggestions: {str(e)}")
return {
'recommended_platforms': [],
'repurposing_strategies': [],
'content_atoms': {},
'estimated_pieces': 0
}

View File

@@ -1,80 +0,0 @@
from datetime import datetime
from typing import List, Dict, Any
from ..core.calendar_manager import CalendarManager
from ..models.calendar import ContentType, Platform
def create_content_calendar(
website_url: str,
start_date: datetime,
duration: str,
platforms: List[str]
) -> Dict[str, Any]:
"""
Example of creating a content calendar.
Args:
website_url: URL of the website to analyze
start_date: When to start the calendar
duration: How long the calendar should span
platforms: List of platforms to create content for
Returns:
Dictionary containing the calendar data
"""
# Initialize calendar manager
calendar_manager = CalendarManager()
# Create calendar
calendar = calendar_manager.create_calendar(
start_date=start_date,
duration=duration,
platforms=platforms,
website_url=website_url
)
# Export calendar
calendar_data = calendar_manager.export_calendar()
return calendar_data
def main():
"""Example usage of the content calendar system."""
# Example parameters
website_url = "https://example.com"
start_date = datetime.now()
duration = "monthly"
platforms = [
Platform.WEBSITE.value,
Platform.FACEBOOK.value,
Platform.TWITTER.value,
Platform.LINKEDIN.value
]
try:
# Create calendar
calendar_data = create_content_calendar(
website_url=website_url,
start_date=start_date,
duration=duration,
platforms=platforms
)
# Print calendar summary
print("\nContent Calendar Summary:")
print(f"Duration: {calendar_data['duration']}")
print(f"Platforms: {', '.join(calendar_data['platforms'])}")
print("\nScheduled Content:")
for date, items in calendar_data['schedule'].items():
print(f"\n{date}:")
for item in items:
print(f"- {item['title']} ({item['content_type']})")
print(f" Platforms: {', '.join(item['platforms'])}")
print(f" Status: {item['status']}")
except Exception as e:
print(f"Error creating calendar: {str(e)}")
if __name__ == "__main__":
main()

View File

@@ -1,138 +0,0 @@
from datetime import datetime
from typing import Dict, Any
from ..models.calendar import ContentItem, ContentType, Platform, SEOData
from ..core.content_brief import ContentBriefGenerator
def create_content_brief(
title: str,
content_type: ContentType,
platforms: list[Platform],
website_url: str,
target_audience: Dict[str, Any]
) -> Dict[str, Any]:
"""
Create a content brief for the given content.
Args:
title: Content title
content_type: Type of content
platforms: List of platforms to publish on
website_url: Website URL for context
target_audience: Target audience information
Returns:
Dictionary containing the content brief
"""
# Create content item
content_item = ContentItem(
id=f"content-{datetime.now().strftime('%Y%m%d%H%M%S')}",
title=title,
description=f"Content brief for {title}",
content_type=content_type,
platforms=platforms,
publish_date=datetime.now(),
seo_data=SEOData(
keywords=[], # Will be generated by SEO tools
meta_description="", # Will be generated by SEO tools
structured_data={}
),
platform_specs={}, # Will be generated based on platforms
context={
"website_url": website_url,
"target_audience": target_audience.get("demographics", {}).get("profession", ""),
"content_goals": ["educate", "generate leads"]
}
)
# Initialize content brief generator
generator = ContentBriefGenerator()
# Generate brief
brief = generator.generate_brief(
content_item=content_item,
target_audience=target_audience
)
return brief
def main():
"""Example usage of content brief generation."""
# Example content details
title = "10 Ways to Improve Your SEO Strategy"
content_type = ContentType.BLOG_POST
platforms = [Platform.WEBSITE, Platform.LINKEDIN]
website_url = "https://example.com"
# Example target audience
target_audience = {
"demographics": {
"age_range": "25-45",
"profession": "digital marketers",
"experience_level": "intermediate"
},
"interests": [
"SEO",
"content marketing",
"digital strategy",
"search engine optimization"
],
"pain_points": [
"low search rankings",
"poor content performance",
"lack of organic traffic",
"difficulty in keyword research"
],
"goals": [
"improve search rankings",
"increase organic traffic",
"generate more leads",
"build brand authority"
]
}
try:
# Generate content brief
brief = create_content_brief(
title=title,
content_type=content_type,
platforms=platforms,
website_url=website_url,
target_audience=target_audience
)
# Print brief summary
print("\nContent Brief Summary:")
print(f"Title: {brief['title']}")
print(f"Content Type: {brief['content_type']}")
print("\nOutline:")
for heading in brief['outline']['main_headings']:
print(f"\n- {heading['title']}")
print(f" Keywords: {', '.join(heading['keywords'])}")
print(f" Summary: {heading['summary']}")
# Print subheadings
subheadings = brief['outline']['subheadings'].get(heading['title'], [])
for subheading in subheadings:
print(f" - {subheading['title']}")
print(f" Keywords: {', '.join(subheading['keywords'])}")
print("\nKey Points:")
for point in brief['key_points']:
print(f"\n- {point['point']}")
print(f" Importance: {point['importance']}")
print(f" Evidence: {', '.join(point['supporting_evidence'])}")
print("\nContent Flow:")
flow = brief['content_flow']
print(f"Introduction: {flow['introduction'].get('summary', '')}")
print(f"Main Sections: {len(flow['main_sections'])} sections")
print(f"Conclusion: {flow['conclusion'].get('summary', '')}")
print(f"Transitions: {len(flow['transitions'])} transition points")
except Exception as e:
print(f"Error generating content brief: {str(e)}")
if __name__ == '__main__':
main()

View File

@@ -1,196 +0,0 @@
import logging
from datetime import datetime, timedelta
from typing import Dict, Any, List
from ..integrations.integration_manager import IntegrationManager
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def create_cross_platform_content(
title: str,
content: str,
platforms: List[str],
content_type: str,
target_audience: Dict[str, Any],
industry: str,
keywords: List[str]
) -> Dict[str, Any]:
"""Create and optimize content for multiple platforms."""
try:
# Initialize integration manager
integration_manager = IntegrationManager()
# Prepare content item
content_item = {
'title': title,
'content': content,
'content_type': content_type,
'keywords': keywords,
'target_audience': target_audience,
'industry': industry
}
# Get platform suggestions
suggestions = integration_manager.get_platform_suggestions(
content=content_item,
platforms=platforms
)
# Validate content for each platform
validation_results = {}
for platform in platforms:
validation = integration_manager.validate_platform_content(
content=content_item,
platform=platform
)
validation_results[platform] = validation
# Optimize content for each platform
optimized_content = integration_manager.optimize_cross_platform_content(
content=content_item,
platforms=platforms
)
return {
'original_content': content_item,
'platform_suggestions': suggestions,
'validation_results': validation_results,
'optimized_content': optimized_content
}
except Exception as e:
logger.error(f"Error creating cross-platform content: {str(e)}")
return {
'error': str(e)
}
def create_content_calendar(
start_date: datetime,
end_date: datetime,
platforms: List[str],
content_types: List[str],
target_audience: Dict[str, Any],
industry: str,
keywords: List[str]
) -> Dict[str, Any]:
"""Create a cross-platform content calendar."""
try:
# Initialize integration manager
integration_manager = IntegrationManager()
# Create calendar
calendar = integration_manager.create_cross_platform_calendar(
start_date=start_date,
end_date=end_date,
platforms=platforms,
content_types=content_types,
target_audience=target_audience,
industry=industry,
keywords=keywords
)
return calendar
except Exception as e:
logger.error(f"Error creating content calendar: {str(e)}")
return {
'error': str(e)
}
def main():
"""Main function to demonstrate integration manager usage."""
# Example content details
title = "The Future of AI in Content Marketing"
content = """
Artificial Intelligence is revolutionizing the way we approach content marketing.
From automated content generation to personalized recommendations, AI tools are
helping marketers create more engaging and effective content strategies.
Key points:
1. AI-powered content generation
2. Personalized content recommendations
3. Automated content optimization
4. Data-driven content strategy
5. Future trends in AI marketing
"""
# Platform and content settings
platforms = ['instagram', 'twitter', 'linkedin', 'blog', 'facebook']
content_type = 'article'
target_audience = {
'age_range': '25-34',
'interests': ['technology', 'marketing', 'AI'],
'location': 'global',
'profession': 'marketing professionals'
}
industry = 'technology'
keywords = ['AI', 'content marketing', 'automation', 'personalization']
# Create cross-platform content
logger.info("Creating cross-platform content...")
content_result = create_cross_platform_content(
title=title,
content=content,
platforms=platforms,
content_type=content_type,
target_audience=target_audience,
industry=industry,
keywords=keywords
)
# Print content results
logger.info("\nCross-Platform Content Results:")
logger.info("===============================")
# Print platform suggestions
logger.info("\nPlatform Suggestions:")
for platform, suggestions in content_result['platform_suggestions'].items():
logger.info(f"\n{platform.upper()}:")
for key, value in suggestions.items():
logger.info(f" {key}: {value}")
# Print validation results
logger.info("\nValidation Results:")
for platform, validation in content_result['validation_results'].items():
logger.info(f"\n{platform.upper()}:")
logger.info(f" Valid: {validation['is_valid']}")
if not validation['is_valid']:
logger.info(f" Error: {validation.get('error', 'N/A')}")
# Print optimized content
logger.info("\nOptimized Content:")
for platform, optimized in content_result['optimized_content'].items():
logger.info(f"\n{platform.upper()}:")
for key, value in optimized.items():
logger.info(f" {key}: {value}")
# Create content calendar
logger.info("\nCreating content calendar...")
start_date = datetime.now()
end_date = start_date + timedelta(days=30)
calendar_result = create_content_calendar(
start_date=start_date,
end_date=end_date,
platforms=platforms,
content_types=[content_type],
target_audience=target_audience,
industry=industry,
keywords=keywords
)
# Print calendar results
logger.info("\nContent Calendar Results:")
logger.info("========================")
# Print platform calendars
logger.info("\nPlatform Calendars:")
for platform, calendar in calendar_result['platform_calendars'].items():
logger.info(f"\n{platform.upper()}:")
logger.info(f" Content Items: {len(calendar['content_items'])}")
for item in calendar['content_items']:
logger.info(f" - {item['original_item']['title']}")
if __name__ == '__main__':
main()

View File

@@ -1,142 +0,0 @@
from typing import Dict, Any
from datetime import datetime
from ..integrations.platform_adapters import UnifiedPlatformAdapter
def create_platform_content(
title: str,
content: str,
platforms: list,
context: Dict[str, Any] = None
) -> Dict[str, Any]:
"""
Create platform-specific content using the UnifiedPlatformAdapter.
Args:
title: The title of the content
content: The main content to be adapted
platforms: List of platforms to adapt content for
context: Additional context for content adaptation
Returns:
Dict containing adapted content for each platform
"""
# Initialize the platform adapter
adapter = UnifiedPlatformAdapter()
# Prepare base content
base_content = {
'title': title,
'content': content,
'keywords': ['content', 'marketing', 'social media'],
'tone': 'professional',
'cta': 'Learn More',
'audience': 'For All',
'language': 'English',
'industry': 'technology',
'word_count': 1000
}
# Adapt content for each platform
adapted_content = {}
for platform in platforms:
try:
platform_content = adapter.adapt_content(
content=base_content,
platform=platform,
context=context
)
adapted_content[platform] = platform_content
except Exception as e:
print(f"Error adapting content for {platform}: {str(e)}")
adapted_content[platform] = {'error': str(e)}
return adapted_content
def main():
"""Example usage of platform content adaptation."""
# Example content
title = "The Future of AI in Content Marketing"
content = """
Artificial Intelligence is revolutionizing content marketing in unprecedented ways.
From automated content generation to personalized user experiences, AI is becoming
an indispensable tool for marketers. This article explores the latest trends and
innovations in AI-powered content marketing.
"""
# Example context
context = {
'target_audience': 'marketing professionals',
'campaign_goals': ['awareness', 'engagement', 'lead generation'],
'brand_voice': 'authoritative yet approachable',
'content_theme': 'technology and innovation'
}
# Platforms to adapt content for
platforms = ['instagram', 'twitter', 'linkedin', 'blog', 'facebook']
# Generate platform-specific content
adapted_content = create_platform_content(
title=title,
content=content,
platforms=platforms,
context=context
)
# Print results
print("\nPlatform-Specific Content Adaptation Results:")
print("=" * 50)
for platform, content in adapted_content.items():
print(f"\n{platform.upper()} Content:")
print("-" * 30)
if 'error' in content:
print(f"Error: {content['error']}")
continue
# Print platform-specific content
if platform == 'instagram':
print("\nCaptions:")
for caption in content['captions']:
print(f"- {caption}")
print("\nHashtags:")
print(content['hashtags'])
elif platform == 'twitter':
print("\nTweets:")
for tweet in content['tweets']:
print(f"- {tweet}")
print("\nThread Structure:")
print(content['thread_structure'])
elif platform == 'linkedin':
print("\nPost:")
print(content['post'])
print("\nEngagement Optimization:")
print(content['engagement_optimization'])
elif platform == 'blog':
print("\nPost:")
print(content['post'])
print("\nSEO Optimization:")
print(content['seo_optimization'])
elif platform == 'facebook':
print("\nPost:")
print(content['post'])
print("\nEngagement Optimization:")
print(content['engagement_optimization'])
# Print media suggestions
print("\nMedia Suggestions:")
for media in content['media_suggestions']:
print(f"- {media['type']}: {media['description']}")
# Print platform-specific recommendations
print("\nPlatform-Specific Recommendations:")
for key, value in content['platform_specific'].items():
print(f"- {key}: {value}")
if __name__ == '__main__':
main()

View File

@@ -1,37 +1,30 @@
"""
Platform adapters for content calendar.
Unified platform adapter for content adaptation across different platforms.
"""
import streamlit as st
from typing import Dict, Any, List, Optional
import logging
from typing import Dict, Any, List, Optional, TypedDict
from datetime import datetime
from loguru import logger
from lib.utils.website_analyzer.analyzer import WebsiteAnalyzer
from lib.ai_seo_tools.content_gap_analysis.main import ContentGapAnalysis
from lib.ai_seo_tools.content_title_generator import ai_title_generator
from lib.ai_seo_tools.meta_desc_generator import metadesc_generator_main
from lib.ai_seo_tools.seo_structured_data import ai_structured_data
import asyncio
import sys
import os
import json
# Configure logger
logger.remove() # Remove default handler
logger.add(
"logs/platform_adapters.log",
rotation="50 MB",
retention="10 days",
level="DEBUG",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
)
logger.add(
sys.stdout,
level="INFO",
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{message}</cyan>"
)
# Ensure logs directory exists
os.makedirs("logs", exist_ok=True)
class ContentItem(TypedDict):
"""Type definition for content items."""
id: str
title: str
content: str
platforms: List[str]
status: str
created_at: datetime
updated_at: datetime
published_at: Optional[datetime]
metadata: Dict[str, Any]
analytics: Optional[Dict[str, Any]]
class UnifiedPlatformAdapter:
"""Unified adapter for different social media platforms."""
@@ -72,14 +65,76 @@ class UnifiedPlatformAdapter:
'content': None
}
def get_content_performance(self, content_item: ContentItem) -> Dict[str, Any]:
"""Get performance metrics for content across platforms."""
try:
logger.info(f"Getting performance metrics for content: {getattr(content_item, 'title', 'Untitled')}")
# Get platform from content item
platforms = getattr(content_item, 'platforms', None)
if platforms and len(platforms) > 0:
platform = platforms[0].name if hasattr(platforms[0], 'name') else str(platforms[0])
else:
platform = 'Unknown'
# Initialize performance metrics
performance = {
'engagement_metrics': {
'likes': 0,
'comments': 0,
'shares': 0,
'reach': 0
},
'seo_metrics': {
'impressions': 0,
'clicks': 0,
'ctr': 0,
'position': 0
},
'conversion_metrics': {
'conversions': 0,
'conversion_rate': 0,
'revenue': 0
},
'platform_specific': {},
'performance_trends': [],
'recommendations': []
}
# Add platform-specific metrics
if platform.upper() == 'WEBSITE':
performance['platform_specific'] = {
'bounce_rate': 0,
'time_on_page': 0,
'page_views': 0
}
return performance
except Exception as e:
error_msg = f"Error getting content performance: {str(e)}"
logger.error(error_msg, exc_info=True)
return {
'error': error_msg,
'metrics': {},
'trends': {},
'recommendations': []
}
def _handle_instagram(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle Instagram content generation."""
try:
# Use content title generator for Instagram captions
caption = ai_title_generator(data)
# Generate Instagram-specific content
caption = metadesc_generator_main(data)
hashtags = self._generate_hashtags(data)
return {
'platform': 'instagram',
'content': caption
'content': {
'caption': caption,
'hashtags': hashtags,
'media_suggestions': self._get_media_suggestions(data)
}
}
except Exception as e:
logger.error(f"Error generating Instagram content: {str(e)}")
@@ -91,11 +146,16 @@ class UnifiedPlatformAdapter:
def _handle_linkedin(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle LinkedIn content generation."""
try:
# Use meta description generator for LinkedIn posts
# Generate LinkedIn-specific content
post = metadesc_generator_main(data)
return {
'platform': 'linkedin',
'content': post
'content': {
'post': post,
'engagement_optimization': self._get_engagement_suggestions(data),
'media_suggestions': self._get_media_suggestions(data)
}
}
except Exception as e:
logger.error(f"Error generating LinkedIn content: {str(e)}")
@@ -107,11 +167,18 @@ class UnifiedPlatformAdapter:
def _handle_twitter(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle Twitter content generation."""
try:
# Use content title generator for tweets
tweet = ai_title_generator(data)
# Generate Twitter-specific content
tweet = metadesc_generator_main(data)
hashtags = self._generate_hashtags(data)
return {
'platform': 'twitter',
'content': tweet
'content': {
'tweet': tweet,
'hashtags': hashtags,
'thread_structure': self._get_thread_structure(data),
'media_suggestions': self._get_media_suggestions(data)
}
}
except Exception as e:
logger.error(f"Error generating Twitter content: {str(e)}")
@@ -123,15 +190,118 @@ class UnifiedPlatformAdapter:
def _handle_facebook(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle Facebook content generation."""
try:
# Use meta description generator for Facebook posts
# Generate Facebook-specific content
post = metadesc_generator_main(data)
return {
'platform': 'facebook',
'content': post
'content': {
'post': post,
'engagement_optimization': self._get_engagement_suggestions(data),
'media_suggestions': self._get_media_suggestions(data)
}
}
except Exception as e:
logger.error(f"Error generating Facebook content: {str(e)}")
return {
'platform': 'facebook',
'error': str(e)
}
}
def _generate_hashtags(self, data: Dict[str, Any]) -> List[str]:
"""Generate relevant hashtags for content."""
try:
# Extract keywords from content
keywords = data.get('keywords', [])
# Add platform-specific hashtags
platform = data.get('platform', '').lower()
platform_hashtags = {
'instagram': ['#instagood', '#photooftheday'],
'twitter': ['#trending', '#followme'],
'linkedin': ['#business', '#professional'],
'facebook': ['#social', '#community']
}.get(platform, [])
return keywords + platform_hashtags
except Exception as e:
logger.error(f"Error generating hashtags: {str(e)}")
return []
def _get_media_suggestions(self, data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Get media suggestions for content."""
try:
# Generate media suggestions based on content type
content_type = data.get('type', 'post')
suggestions = []
if content_type == 'blog':
suggestions.append({
'type': 'featured_image',
'description': 'Main blog post image',
'dimensions': '1200x630'
})
elif content_type == 'social':
suggestions.append({
'type': 'post_image',
'description': 'Social media post image',
'dimensions': '1080x1080'
})
return suggestions
except Exception as e:
logger.error(f"Error getting media suggestions: {str(e)}")
return []
def _get_engagement_suggestions(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Get engagement optimization suggestions."""
try:
return {
'best_posting_times': ['9:00 AM', '5:00 PM'],
'engagement_tips': [
'Ask questions to encourage comments',
'Use relevant hashtags',
'Include a clear call-to-action'
],
'content_length': {
'optimal': '150-200 characters',
'maximum': '300 characters'
}
}
except Exception as e:
logger.error(f"Error getting engagement suggestions: {str(e)}")
return {}
def _get_thread_structure(self, data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Get thread structure for Twitter threads."""
try:
content = data.get('content', '')
sentences = content.split('.')
thread = []
current_tweet = ''
for sentence in sentences:
if len(current_tweet + sentence) <= 280:
current_tweet += sentence + '.'
else:
if current_tweet:
thread.append({
'content': current_tweet.strip(),
'type': 'tweet'
})
current_tweet = sentence + '.'
if current_tweet:
thread.append({
'content': current_tweet.strip(),
'type': 'tweet'
})
return thread
except Exception as e:
logger.error(f"Error generating thread structure: {str(e)}")
return []

View File

@@ -1,237 +0,0 @@
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')
)

View File

@@ -1,185 +0,0 @@
import unittest
from typing import Dict, Any
from ..models.calendar import ContentType
from ..core.ai_generator import AIContentGenerator
class TestAIContentGenerator(unittest.TestCase):
"""Test cases for AIContentGenerator."""
def setUp(self):
"""Set up test cases."""
self.generator = AIContentGenerator()
self.test_title = "10 Ways to Improve Your SEO Strategy"
self.test_content_type = ContentType.BLOG_POST
self.test_context = {
"website_url": "https://example.com",
"target_audience": "digital marketers",
"content_goals": ["educate", "generate leads"]
}
def test_generate_headings(self):
"""Test heading generation."""
headings = self.generator.generate_headings(
title=self.test_title,
content_type=self.test_content_type,
context=self.test_context
)
self.assertIsInstance(headings, list)
for heading in headings:
self.assertIn('title', heading)
self.assertIn('level', heading)
self.assertIn('keywords', heading)
self.assertIn('summary', heading)
# Verify heading level
self.assertEqual(heading['level'], 1)
# Verify heading content
self.assertIsInstance(heading['title'], str)
self.assertIsInstance(heading['keywords'], list)
self.assertIsInstance(heading['summary'], str)
def test_generate_subheadings(self):
"""Test subheading generation."""
main_heading = {
'title': 'Understanding SEO Basics',
'level': 1,
'keywords': ['SEO', 'basics', 'fundamentals'],
'summary': 'Introduction to core SEO concepts'
}
subheadings = self.generator.generate_subheadings(
main_heading=main_heading,
content_type=self.test_content_type,
context=self.test_context
)
self.assertIsInstance(subheadings, list)
for subheading in subheadings:
self.assertIn('title', subheading)
self.assertIn('level', subheading)
self.assertIn('keywords', subheading)
self.assertIn('summary', subheading)
# Verify subheading level
self.assertEqual(subheading['level'], 2)
# Verify subheading content
self.assertIsInstance(subheading['title'], str)
self.assertIsInstance(subheading['keywords'], list)
self.assertIsInstance(subheading['summary'], str)
def test_generate_key_points(self):
"""Test key points generation."""
key_points = self.generator.generate_key_points(
title=self.test_title,
content_type=self.test_content_type,
context=self.test_context
)
self.assertIsInstance(key_points, list)
for point in key_points:
self.assertIn('point', point)
self.assertIn('importance', point)
self.assertIn('supporting_evidence', point)
self.assertIn('related_keywords', point)
# Verify point content
self.assertIsInstance(point['point'], str)
self.assertIn(point['importance'], ['high', 'medium', 'low'])
self.assertIsInstance(point['supporting_evidence'], list)
self.assertIsInstance(point['related_keywords'], list)
def test_generate_content_flow(self):
"""Test content flow generation."""
outline = {
'main_headings': [
{
'title': 'Introduction',
'level': 1,
'keywords': ['SEO', 'introduction'],
'summary': 'Overview of SEO importance'
}
],
'subheadings': {
'Introduction': [
{
'title': 'What is SEO?',
'level': 2,
'keywords': ['definition', 'basics'],
'summary': 'Basic definition of SEO'
}
]
}
}
flow = self.generator.generate_content_flow(
title=self.test_title,
content_type=self.test_content_type,
outline=outline
)
self.assertIsInstance(flow, dict)
self.assertIn('introduction', flow)
self.assertIn('main_sections', flow)
self.assertIn('conclusion', flow)
self.assertIn('transitions', flow)
self.assertIn('content_pacing', flow)
# Verify flow content
self.assertIsInstance(flow['introduction'], dict)
self.assertIsInstance(flow['main_sections'], list)
self.assertIsInstance(flow['conclusion'], dict)
self.assertIsInstance(flow['transitions'], list)
self.assertIsInstance(flow['content_pacing'], dict)
def test_prompt_creation(self):
"""Test prompt creation methods."""
# Test heading prompt
heading_prompt = self.generator._create_heading_prompt(
title=self.test_title,
content_type=self.test_content_type,
gaps={'opportunities': ['keyword research', 'content optimization']}
)
self.assertIsInstance(heading_prompt, str)
self.assertIn(self.test_title, heading_prompt)
self.assertIn(self.test_content_type.value, heading_prompt)
# Test subheading prompt
main_heading = {
'title': 'Understanding SEO Basics',
'level': 1,
'keywords': ['SEO', 'basics'],
'summary': 'Introduction to SEO'
}
subheading_prompt = self.generator._create_subheading_prompt(
main_heading=main_heading,
content_type=self.test_content_type,
context=self.test_context
)
self.assertIsInstance(subheading_prompt, str)
self.assertIn(main_heading['title'], subheading_prompt)
# Test key points prompt
key_points_prompt = self.generator._create_key_points_prompt(
title=self.test_title,
content_type=self.test_content_type,
seo_data={'keywords': ['SEO', 'strategy']},
context=self.test_context
)
self.assertIsInstance(key_points_prompt, str)
self.assertIn(self.test_title, key_points_prompt)
# Test flow prompt
flow_prompt = self.generator._create_flow_prompt(
title=self.test_title,
content_type=self.test_content_type,
outline={'main_headings': []}
)
self.assertIsInstance(flow_prompt, str)
self.assertIn(self.test_title, flow_prompt)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,132 +0,0 @@
import unittest
from datetime import datetime
from typing import Dict, Any
from ..models.calendar import ContentItem, ContentType, Platform, SEOData
from ..core.content_brief import ContentBriefGenerator
class TestContentBriefGenerator(unittest.TestCase):
"""Test cases for ContentBriefGenerator."""
def setUp(self):
"""Set up test cases."""
self.generator = ContentBriefGenerator()
self.test_content_item = self._create_test_content_item()
def _create_test_content_item(self) -> ContentItem:
"""Create a test content item."""
return ContentItem(
id="test-001",
title="10 Ways to Improve Your SEO Strategy",
description="A comprehensive guide to enhancing your website's SEO performance",
content_type=ContentType.BLOG_POST,
platforms=[Platform.WEBSITE, Platform.LINKEDIN],
publish_date=datetime.now(),
seo_data=SEOData(
keywords=["SEO", "search engine optimization", "digital marketing"],
meta_description="Learn effective SEO strategies to boost your website's visibility",
structured_data={}
),
platform_specs={
"website": {
"format": "blog post",
"min_length": 1500
},
"linkedin": {
"format": "article",
"min_length": 800
}
},
context={
"website_url": "https://example.com",
"target_audience": "digital marketers",
"content_goals": ["educate", "generate leads"]
}
)
def test_generate_brief(self):
"""Test content brief generation."""
# Generate brief
brief = self.generator.generate_brief(
content_item=self.test_content_item,
target_audience={
"demographics": {
"age_range": "25-45",
"profession": "digital marketers"
},
"interests": ["SEO", "content marketing", "digital strategy"],
"pain_points": [
"low search rankings",
"poor content performance",
"lack of organic traffic"
]
}
)
# Verify brief structure
self.assertIsInstance(brief, dict)
self.assertIn('title', brief)
self.assertIn('content_type', brief)
self.assertIn('outline', brief)
self.assertIn('key_points', brief)
self.assertIn('content_flow', brief)
self.assertIn('target_audience', brief)
self.assertIn('seo_data', brief)
self.assertIn('platform_specs', brief)
# Verify outline structure
outline = brief['outline']
self.assertIn('main_headings', outline)
self.assertIn('subheadings', outline)
# Verify key points
self.assertIsInstance(brief['key_points'], list)
# Verify content flow
flow = brief['content_flow']
self.assertIn('introduction', flow)
self.assertIn('main_sections', flow)
self.assertIn('conclusion', flow)
self.assertIn('transitions', flow)
self.assertIn('content_pacing', flow)
def test_generate_brief_without_audience(self):
"""Test content brief generation without target audience data."""
brief = self.generator.generate_brief(
content_item=self.test_content_item
)
self.assertIsInstance(brief, dict)
self.assertIn('target_audience', brief)
self.assertEqual(brief['target_audience'], {})
def test_generate_outline(self):
"""Test outline generation."""
outline = self.generator._generate_outline(self.test_content_item)
self.assertIsInstance(outline, dict)
self.assertIn('main_headings', outline)
self.assertIn('subheadings', outline)
# Verify main headings
main_headings = outline['main_headings']
self.assertIsInstance(main_headings, list)
for heading in main_headings:
self.assertIn('title', heading)
self.assertIn('level', heading)
self.assertIn('keywords', heading)
self.assertIn('summary', heading)
# Verify subheadings
subheadings = outline['subheadings']
self.assertIsInstance(subheadings, dict)
for heading_title, heading_subheadings in subheadings.items():
self.assertIsInstance(heading_subheadings, list)
for subheading in heading_subheadings:
self.assertIn('title', subheading)
self.assertIn('level', subheading)
self.assertIn('keywords', subheading)
self.assertIn('summary', subheading)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,171 +0,0 @@
import unittest
from datetime import datetime, timedelta
from typing import Dict, Any
from ..integrations.integration_manager import IntegrationManager
class TestIntegrationManager(unittest.TestCase):
"""Test cases for the IntegrationManager class."""
def setUp(self):
"""Set up test fixtures."""
self.integration_manager = IntegrationManager()
self.start_date = datetime.now()
self.end_date = self.start_date + timedelta(days=30)
self.platforms = ['instagram', 'twitter', 'linkedin', 'blog', 'facebook']
self.content_types = ['article', 'social', 'video']
self.target_audience = {
'age_range': '25-34',
'interests': ['technology', 'marketing'],
'location': 'global'
}
self.industry = 'technology'
self.keywords = ['AI', 'content marketing', 'social media']
# Sample content item
self.sample_content = {
'title': 'The Future of AI in Content Marketing',
'content': 'AI is revolutionizing content marketing...',
'content_type': 'article',
'keywords': ['AI', 'content marketing', 'automation'],
'target_audience': self.target_audience,
'industry': self.industry
}
def test_create_cross_platform_calendar(self):
"""Test creating a cross-platform content calendar."""
calendar = self.integration_manager.create_cross_platform_calendar(
start_date=self.start_date,
end_date=self.end_date,
platforms=self.platforms,
content_types=self.content_types,
target_audience=self.target_audience,
industry=self.industry,
keywords=self.keywords
)
# Check basic structure
self.assertIn('base_calendar', calendar)
self.assertIn('platform_calendars', calendar)
self.assertIn('metadata', calendar)
# Check platform calendars
platform_calendars = calendar['platform_calendars']
self.assertEqual(len(platform_calendars), len(self.platforms))
for platform in self.platforms:
self.assertIn(platform, platform_calendars)
platform_calendar = platform_calendars[platform]
self.assertIn('content_items', platform_calendar)
self.assertIn('metadata', platform_calendar)
def test_adapt_calendar_for_platform(self):
"""Test adapting calendar for a specific platform."""
# Create base calendar
calendar = self.integration_manager.create_cross_platform_calendar(
start_date=self.start_date,
end_date=self.end_date,
platforms=[self.platforms[0]], # Test with just Instagram
content_types=self.content_types,
target_audience=self.target_audience,
industry=self.industry,
keywords=self.keywords
)
# Get platform calendar
platform_calendar = calendar['platform_calendars'][self.platforms[0]]
# Check structure
self.assertIn('content_items', platform_calendar)
self.assertIn('metadata', platform_calendar)
# Check content items
for item in platform_calendar['content_items']:
self.assertIn('original_item', item)
self.assertIn('adapted_content', item)
self.assertIn('platform_specifics', item)
def test_adapt_content_item(self):
"""Test adapting a content item for a platform."""
adapted_item = self.integration_manager._adapt_content_item(
item=self.sample_content,
platform='instagram'
)
# Check structure
self.assertIsNotNone(adapted_item)
self.assertIn('original_item', adapted_item)
self.assertIn('adapted_content', adapted_item)
self.assertIn('platform_specifics', adapted_item)
# Check content adaptation
adapted_content = adapted_item['adapted_content']
self.assertIn('captions', adapted_content)
self.assertIn('hashtags', adapted_content)
self.assertIn('media_suggestions', adapted_content)
def test_get_platform_suggestions(self):
"""Test getting platform-specific suggestions."""
suggestions = self.integration_manager.get_platform_suggestions(
content=self.sample_content,
platforms=self.platforms
)
# Check structure
self.assertEqual(len(suggestions), len(self.platforms))
for platform in self.platforms:
self.assertIn(platform, suggestions)
platform_suggestions = suggestions[platform]
self.assertIsInstance(platform_suggestions, dict)
def test_validate_platform_content(self):
"""Test validating content for a platform."""
validation = self.integration_manager.validate_platform_content(
content=self.sample_content,
platform='instagram'
)
# Check structure
self.assertIn('platform', validation)
self.assertIn('is_valid', validation)
self.assertIn('specifications', validation)
# Check validation result
self.assertIsInstance(validation['is_valid'], bool)
def test_optimize_cross_platform_content(self):
"""Test optimizing content for multiple platforms."""
optimized = self.integration_manager.optimize_cross_platform_content(
content=self.sample_content,
platforms=self.platforms
)
# Check structure
self.assertEqual(len(optimized), len(self.platforms))
for platform in self.platforms:
self.assertIn(platform, optimized)
platform_optimized = optimized[platform]
self.assertIsInstance(platform_optimized, dict)
def test_error_handling(self):
"""Test error handling with invalid inputs."""
# Test with invalid platform
with self.assertRaises(Exception):
self.integration_manager.validate_platform_content(
content=self.sample_content,
platform='invalid_platform'
)
# Test with invalid content
invalid_content = {'title': 'Invalid Content'}
validation = self.integration_manager.validate_platform_content(
content=invalid_content,
platform='instagram'
)
self.assertFalse(validation['is_valid'])
self.assertIn('error', validation)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,186 +0,0 @@
import unittest
from typing import Dict, Any
from datetime import datetime
from ..integrations.platform_adapters import UnifiedPlatformAdapter
class TestUnifiedPlatformAdapter(unittest.TestCase):
"""Test cases for the UnifiedPlatformAdapter."""
def setUp(self):
"""Set up test cases."""
self.adapter = UnifiedPlatformAdapter()
self.test_content = {
'title': 'Test Content',
'content': 'This is a test content for platform adaptation.',
'keywords': ['test', 'content', 'platform'],
'tone': 'professional',
'cta': 'Learn More',
'audience': 'For All',
'language': 'English',
'industry': 'technology',
'word_count': 1000
}
def test_adapt_instagram_content(self):
"""Test Instagram content adaptation."""
adapted_content = self.adapter.adapt_content(
content=self.test_content,
platform='instagram'
)
self.assertIsInstance(adapted_content, dict)
self.assertIn('captions', adapted_content)
self.assertIn('hashtags', adapted_content)
self.assertIn('media_suggestions', adapted_content)
self.assertIn('platform_specific', adapted_content)
def test_adapt_twitter_content(self):
"""Test Twitter content adaptation."""
adapted_content = self.adapter.adapt_content(
content=self.test_content,
platform='twitter'
)
self.assertIsInstance(adapted_content, dict)
self.assertIn('tweets', adapted_content)
self.assertIn('thread_structure', adapted_content)
self.assertIn('media_suggestions', adapted_content)
self.assertIn('platform_specific', adapted_content)
def test_adapt_linkedin_content(self):
"""Test LinkedIn content adaptation."""
adapted_content = self.adapter.adapt_content(
content=self.test_content,
platform='linkedin'
)
self.assertIsInstance(adapted_content, dict)
self.assertIn('post', adapted_content)
self.assertIn('engagement_optimization', adapted_content)
self.assertIn('media_suggestions', adapted_content)
self.assertIn('platform_specific', adapted_content)
def test_adapt_blog_content(self):
"""Test blog content adaptation."""
adapted_content = self.adapter.adapt_content(
content=self.test_content,
platform='blog'
)
self.assertIsInstance(adapted_content, dict)
self.assertIn('post', adapted_content)
self.assertIn('seo_optimization', adapted_content)
self.assertIn('media_suggestions', adapted_content)
self.assertIn('platform_specific', adapted_content)
def test_adapt_facebook_content(self):
"""Test Facebook content adaptation."""
adapted_content = self.adapter.adapt_content(
content=self.test_content,
platform='facebook'
)
self.assertIsInstance(adapted_content, dict)
self.assertIn('post', adapted_content)
self.assertIn('engagement_optimization', adapted_content)
self.assertIn('media_suggestions', adapted_content)
self.assertIn('platform_specific', adapted_content)
def test_validate_content(self):
"""Test content validation."""
# Test valid content
self.assertTrue(
self.adapter.validate_content(
self.test_content,
'instagram'
)
)
# Test invalid content (missing required fields)
invalid_content = {
'title': 'Test Content',
'content': 'This is a test content.'
}
self.assertFalse(
self.adapter.validate_content(
invalid_content,
'instagram'
)
)
def test_unsupported_platform(self):
"""Test handling of unsupported platform."""
with self.assertRaises(ValueError):
self.adapter.adapt_content(
content=self.test_content,
platform='unsupported_platform'
)
def test_content_adaptation_with_context(self):
"""Test content adaptation with additional context."""
context = {
'target_audience': 'professionals',
'campaign_goals': ['awareness', 'engagement'],
'brand_voice': 'authoritative'
}
adapted_content = self.adapter.adapt_content(
content=self.test_content,
platform='linkedin',
context=context
)
self.assertIsInstance(adapted_content, dict)
self.assertIn('post', adapted_content)
self.assertIn('engagement_optimization', adapted_content)
def test_error_handling(self):
"""Test error handling in content adaptation."""
# Test with invalid content structure
invalid_content = {
'title': 123, # Invalid type
'content': None # Missing required field
}
adapted_content = self.adapter.adapt_content(
content=invalid_content,
platform='blog'
)
self.assertIn('error', adapted_content)
def test_platform_specs(self):
"""Test platform specifications."""
specs = self.adapter.platform_specs
# Check Instagram specs
self.assertIn('instagram', specs)
self.assertIn('max_caption_length', specs['instagram'])
self.assertIn('max_hashtags', specs['instagram'])
self.assertIn('required_fields', specs['instagram'])
# Check Twitter specs
self.assertIn('twitter', specs)
self.assertIn('max_tweet_length', specs['twitter'])
self.assertIn('max_thread_length', specs['twitter'])
self.assertIn('required_fields', specs['twitter'])
# Check LinkedIn specs
self.assertIn('linkedin', specs)
self.assertIn('max_post_length', specs['linkedin'])
self.assertIn('required_fields', specs['linkedin'])
# Check blog specs
self.assertIn('blog', specs)
self.assertIn('min_word_count', specs['blog'])
self.assertIn('max_word_count', specs['blog'])
self.assertIn('required_fields', specs['blog'])
# Check Facebook specs
self.assertIn('facebook', specs)
self.assertIn('max_post_length', specs['facebook'])
self.assertIn('required_fields', specs['facebook'])
if __name__ == '__main__':
unittest.main()

View File

@@ -1,132 +0,0 @@
import unittest
from datetime import datetime
from typing import Dict, Any
from ..integrations.seo_optimizer import SEOOptimizer
class TestSEOOptimizer(unittest.TestCase):
"""Test cases for the SEOOptimizer class."""
def setUp(self):
"""Set up test fixtures."""
self.seo_optimizer = SEOOptimizer()
# Sample content for testing
self.sample_content = {
'title': 'The Future of AI in Content Marketing',
'content': 'AI is revolutionizing content marketing...',
'keywords': ['AI', 'content marketing', 'automation'],
'author': 'John Doe',
'publish_date': datetime.now().isoformat(),
'description': 'An in-depth look at AI in content marketing',
'image_url': 'https://example.com/image.jpg',
'url': 'https://example.com/article'
}
# Sample calendar for testing
self.sample_calendar = {
'metadata': {
'start_date': datetime.now().isoformat(),
'end_date': datetime.now().isoformat(),
'platforms': ['blog', 'social'],
'content_types': ['article']
},
'content_items': [self.sample_content]
}
def test_optimize_content(self):
"""Test content optimization."""
optimized = self.seo_optimizer.optimize_content(
content=self.sample_content,
content_type='article',
language='English',
search_intent='Informational Intent'
)
# Check structure
self.assertIn('original_content', optimized)
self.assertIn('seo_optimized', optimized)
# Check SEO elements
seo_elements = optimized['seo_optimized']
self.assertIn('title', seo_elements)
self.assertIn('meta_description', seo_elements)
self.assertIn('structured_data', seo_elements)
self.assertIn('keywords', seo_elements)
def test_optimize_title(self):
"""Test title optimization."""
titles = self.seo_optimizer._optimize_title(
title=self.sample_content['title'],
keywords=self.sample_content['keywords'],
content_type='article',
language='English',
search_intent='Informational Intent'
)
# Check titles
self.assertIsInstance(titles, list)
self.assertTrue(len(titles) > 0)
def test_generate_meta_description(self):
"""Test meta description generation."""
descriptions = self.seo_optimizer._generate_meta_description(
keywords=self.sample_content['keywords'],
content_type='article',
language='English',
search_intent='Informational Intent'
)
# Check descriptions
self.assertIsInstance(descriptions, list)
self.assertTrue(len(descriptions) > 0)
def test_generate_structured_data(self):
"""Test structured data generation."""
structured_data = self.seo_optimizer._generate_structured_data(
content=self.sample_content,
content_type='article'
)
# Check structured data
self.assertIsNotNone(structured_data)
def test_optimize_calendar_content(self):
"""Test calendar content optimization."""
optimized_calendar = self.seo_optimizer.optimize_calendar_content(
calendar=self.sample_calendar,
content_type='article',
language='English',
search_intent='Informational Intent'
)
# Check structure
self.assertIn('metadata', optimized_calendar)
self.assertIn('content_items', optimized_calendar)
# Check content items
self.assertTrue(len(optimized_calendar['content_items']) > 0)
for item in optimized_calendar['content_items']:
self.assertIn('original_content', item)
self.assertIn('seo_optimized', item)
def test_error_handling(self):
"""Test error handling with invalid inputs."""
# Test with invalid content
invalid_content = {'title': 'Invalid Content'}
optimized = self.seo_optimizer.optimize_content(
content=invalid_content,
content_type='article'
)
self.assertIn('error', optimized)
# Test with invalid calendar
invalid_calendar = {'metadata': {}}
optimized_calendar = self.seo_optimizer.optimize_calendar_content(
calendar=invalid_calendar,
content_type='article'
)
self.assertIn('error', optimized_calendar)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,125 +1,188 @@
import streamlit as st
from typing import Dict, Any, List
from lib.ai_seo_tools.content_calendar.models.calendar import ContentItem
from lib.database.models import ContentItem
import logging
from lib.ai_seo_tools.content_calendar.core.content_generator import ContentGenerator
from lib.ai_seo_tools.content_calendar.core.calendar_manager import CalendarManager
logger = logging.getLogger(__name__)
def render_ab_testing(
content_generator,
calendar_manager
) -> None:
def render_ab_testing(content_generator: ContentGenerator, calendar_manager: CalendarManager):
"""Render the A/B testing interface."""
try:
st.header("A/B Testing")
# Test Configuration
st.markdown("### Create A/B Test")
col1, col2 = st.columns([2, 1])
with col1:
test_content = st.selectbox(
"Select content for A/B testing",
options=[item.title for item in calendar_manager.get_calendar().get_all_content()],
key="ab_test_content_select"
)
with col2:
num_variants = st.slider(
"Number of variants",
min_value=2,
max_value=5,
value=2,
help="Number of different versions to test"
)
if test_content:
content_item = next(
item for item in calendar_manager.get_calendar().get_all_content()
if item.title == test_content
)
# Test Settings
with st.expander("Test Settings"):
col1, col2 = st.columns(2)
with col1:
test_duration = st.number_input(
"Test Duration (days)",
min_value=1,
max_value=30,
value=7
)
target_metric = st.selectbox(
"Primary Metric",
options=['Engagement', 'Conversion', 'Reach', 'Click-through'],
value='Engagement'
)
with col2:
audience_size = st.select_slider(
"Audience Size",
options=['Small', 'Medium', 'Large'],
value='Medium'
)
confidence_level = st.slider(
"Confidence Level",
min_value=90,
max_value=99,
value=95,
help="Statistical confidence level for test results"
)
# Generate Variants
if st.button("Generate Variants"):
with st.spinner("Generating variants..."):
variants = _generate_ab_test_variants(content_generator, content_item, num_variants)
if variants:
st.success(f"Generated {len(variants)} variants!")
# Display variants in tabs
variant_tabs = st.tabs([f"Variant {i+1}" for i in range(len(variants))])
for i, tab in enumerate(variant_tabs):
with tab:
st.markdown(f"### Variant {i+1}")
st.json(variants[i]['content'])
# Variant metrics
col1, col2, col3 = st.columns(3)
with col1:
st.metric(
"Engagement Score",
f"{variants[i]['metrics']['engagement_score']:.1f}%"
)
with col2:
st.metric(
"Conversion Rate",
f"{variants[i]['metrics']['conversion_rate']:.1f}%"
)
with col3:
st.metric(
"Reach",
f"{variants[i]['metrics']['reach']:,}"
)
# Results Analysis
st.markdown("### Analyze Results")
if test_content in st.session_state.ab_test_results:
test_data = st.session_state.ab_test_results[test_content]
# Test Status
st.info(f"Test Status: {test_data['status']}")
st.write(f"Started: {test_data['start_time']}")
if test_data['status'] == 'running':
if st.button("End Test and Analyze"):
with st.spinner("Analyzing results..."):
results = _analyze_ab_test_results(content_item)
if results:
st.success("Analysis complete!")
_display_test_results(results)
st.header("A/B Testing")
# Check if calendar manager is available
if 'calendar_manager' not in st.session_state:
st.error("Calendar manager not initialized. Please refresh the page.")
return
# Get available content
try:
available_content = calendar_manager.get_calendar().get_all_content()
content_options = [item.title for item in available_content]
except Exception as e:
logger.error(f"Error in A/B testing interface: {str(e)}", exc_info=True)
st.error(f"Error in A/B testing: {str(e)}")
logger.error(f"Error getting content options: {str(e)}")
st.error("Error loading content. Please try again.")
return
if not content_options:
st.info("""
## Welcome to A/B Testing! 🧪
Test different versions of your content to find what works best. Here's what you can do:
### Features:
- 🔄 **Variant Generation**: Create multiple versions of your content
- 📊 **Performance Tracking**: Compare metrics across variants
- 📈 **Statistical Analysis**: Get data-driven insights
- 🎯 **Winner Selection**: Identify the best performing content
### Getting Started:
1. First, add some content to your calendar
2. Select the content you want to test
3. Generate variants with different parameters
4. Track performance and analyze results
Ready to get started? Add some content to your calendar first!
""")
return
# Content Selection
selected_content = st.selectbox(
"Select content to test",
options=content_options,
key="ab_test_content_select"
)
if selected_content:
try:
content_item = next(
item for item in available_content
if item.title == selected_content
)
# Show onboarding info if no test history
if not st.session_state.get('ab_test_results', {}).get(content_item.title):
st.info("""
### A/B Testing Guide
Create and compare different versions of your content:
- **Headline Variations**: Test different titles and hooks
- **Content Structure**: Try different content flows
- **Call-to-Action**: Test various CTAs
- **Visual Elements**: Compare different media placements
Click 'Generate Test Variants' to get started!
""")
# Test Configuration
st.markdown("### Create A/B Test")
col1, col2 = st.columns([2, 1])
with col1:
test_content = st.selectbox(
"Select content to A/B test",
options=content_options,
key="ab_test_content_select_unique"
)
with col2:
num_variants = st.slider(
"Number of variants",
min_value=2,
max_value=5,
value=2,
help="Number of different versions to test"
)
if test_content:
content_item = next(
item for item in calendar_manager.get_calendar().get_all_content()
if item.title == test_content
)
# Test Settings
with st.expander("Test Settings"):
col1, col2 = st.columns(2)
with col1:
test_duration = st.number_input(
"Test Duration (days)",
min_value=1,
max_value=30,
value=7
)
target_metric = st.selectbox(
"Primary Metric",
options=['Engagement', 'Conversion', 'Reach', 'Click-through'],
index=0
)
with col2:
audience_size = st.select_slider(
"Audience Size",
options=['Small', 'Medium', 'Large'],
value='Medium'
)
confidence_level = st.slider(
"Confidence Level",
min_value=90,
max_value=99,
value=95,
help="Statistical confidence level for test results"
)
# Generate Variants
if st.button("Generate Variants"):
with st.spinner("Generating variants..."):
variants = _generate_ab_test_variants(content_generator, content_item, num_variants)
if variants:
st.success(f"Generated {len(variants)} variants!")
# Display variants in tabs
variant_tabs = st.tabs([f"Variant {i+1}" for i in range(len(variants))])
for i, tab in enumerate(variant_tabs):
with tab:
st.markdown(f"### Variant {i+1}")
st.json(variants[i]['content'])
# Variant metrics
col1, col2, col3 = st.columns(3)
with col1:
st.metric(
"Engagement Score",
f"{variants[i]['metrics']['engagement_score']:.1f}%"
)
with col2:
st.metric(
"Conversion Rate",
f"{variants[i]['metrics']['conversion_rate']:.1f}%"
)
with col3:
st.metric(
"Reach",
f"{variants[i]['metrics']['reach']:,}"
)
# Results Analysis
st.markdown("### Analyze Results")
if test_content in st.session_state.ab_test_results:
test_data = st.session_state.ab_test_results[test_content]
# Test Status
st.info(f"Test Status: {test_data['status']}")
st.write(f"Started: {test_data['start_time']}")
if test_data['status'] == 'running':
if st.button("End Test and Analyze"):
with st.spinner("Analyzing results..."):
results = _analyze_ab_test_results(content_item)
if results:
st.success("Analysis complete!")
_display_test_results(results)
except Exception as e:
logger.error(f"Error in A/B testing interface: {str(e)}", exc_info=True)
st.error(f"Error in A/B testing: {str(e)}")
def _generate_ab_test_variants(
content_generator,

View File

@@ -2,14 +2,19 @@ import streamlit as st
from typing import Dict, Any, List
from datetime import datetime
import pandas as pd
from ...core.content_generator import ContentGenerator
from ...core.ai_generator import AIGenerator
from ...integrations.seo_optimizer import SEOOptimizer
from ...models.calendar import ContentItem, ContentType, Platform, SEOData
from lib.ai_seo_tools.content_calendar.core.content_generator import ContentGenerator
from lib.ai_seo_tools.content_calendar.core.ai_generator import AIGenerator
from lib.ai_seo_tools.content_calendar.integrations.seo_optimizer import SEOOptimizer
from lib.database.models import ContentItem, ContentType, Platform, SEOData
import logging
from lib.database.models import get_engine, get_session, init_db
logger = logging.getLogger('content_calendar.optimization')
engine = get_engine()
init_db(engine)
session = get_session(engine)
class OptimizationManager:
def __init__(self):
if 'optimization_history' not in st.session_state:
@@ -165,7 +170,7 @@ def render_content_optimization(
seo_optimizer: SEOOptimizer
):
"""Render the content optimization interface with advanced features."""
st.header("Content Optimization")
st.title("Content Calendar")
# Initialize optimization manager
optimization_manager = OptimizationManager()
@@ -174,61 +179,257 @@ def render_content_optimization(
if 'calendar_manager' not in st.session_state:
st.error("Calendar manager not initialized. Please refresh the page.")
return
# Create main tabs
main_tabs = st.tabs(["Content Planning", "Content Optimization"])
# Get available content
try:
available_content = st.session_state.calendar_manager.get_calendar().get_all_content()
content_options = [item.title for item in available_content]
except Exception as e:
logger.error(f"Error getting content options: {str(e)}")
st.error("Error loading content. Please try again.")
return
if not content_options:
st.info("No content available for optimization. Please add some content first.")
return
# Content Selection
selected_content = st.selectbox(
"Select content to optimize",
options=content_options,
key="optimize_content_select"
)
if selected_content:
try:
content_item = next(
item for item in available_content
if item.title == selected_content
with main_tabs[0]:
# Create two columns for the layout
col1, col2 = st.columns([1, 1])
with col1:
st.header("Quick Calendar Generation")
st.markdown("""
Generate a content calendar in three simple steps:
1. Enter your keywords
2. Select target platforms
3. Choose time period
""")
# Step 1: Keywords Input
st.subheader("Step 1: Enter Keywords")
keywords = st.text_area(
"Enter keywords or topics (one per line)",
help="Enter the main topics or keywords you want to create content about"
)
# Create tabs for different optimization aspects
opt_tabs = st.tabs(["Content Optimization", "SEO Optimization", "Preview", "History", "Analytics"])
# Step 2: Platform Selection
st.subheader("Step 2: Select Target Platforms")
platform_categories = {
"Website": ["WEBSITE"],
"Social Media": ["INSTAGRAM", "FACEBOOK", "TWITTER", "LINKEDIN"],
"Video": ["YOUTUBE"],
"Newsletter": ["NEWSLETTER"]
}
with opt_tabs[0]:
st.subheader("Content Optimization")
selected_platforms = []
for category, platforms in platform_categories.items():
st.markdown(f"**{category}**")
for platform in platforms:
if st.checkbox(platform.replace("_", " ").title(), key=f"platform_{platform}"):
selected_platforms.append(platform)
# Step 3: Time Period
st.subheader("Step 3: Choose Time Period")
time_period = st.selectbox(
"Select time period",
["1 Week", "2 Weeks", "1 Month", "3 Months", "6 Months"],
help="Choose how far ahead you want to plan your content"
)
# Generate Calendar Button
if st.button("Generate with AI", type="primary"):
if not keywords or not selected_platforms:
st.error("Please enter keywords and select at least one platform.")
else:
with st.spinner("Generating content calendar..."):
try:
# Generate content ideas based on keywords
content_ideas = []
for keyword in keywords.split('\n'):
if keyword.strip():
# Generate content ideas for each platform
for platform in selected_platforms:
try:
# Create a content item for the AI generator
content_item = ContentItem(
title=keyword.strip(),
description=f"Content about {keyword.strip()}",
content_type=ContentType.BLOG_POST if platform == "WEBSITE" else ContentType.SOCIAL_MEDIA,
platforms=[Platform[platform]],
publish_date=datetime.now(),
seo_data=SEOData(
title=keyword.strip(),
meta_description=f"Content about {keyword.strip()}",
keywords=[keyword.strip()],
structured_data={}
)
)
# Generate content using AI generator
content_idea = ai_generator.enhance_content(
content=content_item,
enhancement_type='content_generation',
target_audience={
'content_settings': {
'tone': 'professional',
'length': 'medium',
'engagement_goal': 'awareness',
'creativity_level': 5
}
}
)
if content_idea:
content_ideas.append({
'title': content_idea.get('title', keyword.strip()),
'introduction': content_idea.get('content', f"Content about {keyword.strip()}"),
'platform': platform,
'meta_description': content_idea.get('meta_description', ''),
'keywords': [keyword.strip()]
})
except Exception as e:
logger.error(f"Error generating content for {keyword} on {platform}: {str(e)}")
continue
if content_ideas:
# Create calendar entries
calendar = st.session_state.calendar_manager.get_calendar()
for idea in content_ideas:
try:
# Create content item
content_item = ContentItem(
title=idea['title'],
description=idea['introduction'],
content_type=ContentType.BLOG_POST if idea['platform'] == "WEBSITE" else ContentType.SOCIAL_MEDIA,
platforms=[Platform[idea['platform']]],
publish_date=datetime.now(),
seo_data=SEOData(
title=idea['title'],
meta_description=idea.get('meta_description', ''),
keywords=idea.get('keywords', []),
structured_data={}
)
)
calendar.add_content(content_item)
except Exception as e:
logger.error(f"Error adding content to calendar: {str(e)}")
continue
st.success("Content calendar generated successfully!")
st.rerun() # Refresh to show new content
else:
st.error("Failed to generate any content ideas. Please try different keywords or settings.")
except Exception as e:
logger.error(f"Error generating content calendar: {str(e)}")
st.error("An error occurred while generating the content calendar. Please try again.")
with col2:
st.header("Scheduled Content")
# Get all content from calendar
calendar = st.session_state.calendar_manager.get_calendar()
if not calendar:
st.info("No content scheduled yet. Generate content using the form on the left.")
else:
# Group content by platform
platform_content = {}
for item in calendar.get_all_content():
platform = item.platforms[0].name if item.platforms else "Unknown"
if platform not in platform_content:
platform_content[platform] = []
platform_content[platform].append(item)
# Advanced Optimization Settings
with st.expander("Advanced Settings", expanded=True):
col1, col2 = st.columns(2)
# Create tabs for each platform
platform_tabs = st.tabs(list(platform_content.keys()))
for i, (platform, content) in enumerate(platform_content.items()):
with platform_tabs[i]:
st.write(f"### {platform} Content")
# Convert content to DataFrame for better display
content_data = []
for item in content:
content_data.append({
'Date': item.publish_date.strftime('%Y-%m-%d'),
'Title': item.title,
'Type': item.content_type.name,
'Status': item.status
})
if content_data:
df = pd.DataFrame(content_data)
st.dataframe(df, use_container_width=True)
# Add action buttons for each content item
for item in content:
with st.expander(f"Actions for: {item.title}"):
col1, col2, col3 = st.columns(3)
with col1:
if st.button("Edit", key=f"edit_{item.title}"):
st.session_state.selected_content = item.title
with col2:
if st.button("Optimize", key=f"optimize_{item.title}"):
st.session_state.selected_content = item.title
st.session_state.active_tab = "Content Optimization"
with col3:
if st.button("Delete", key=f"delete_{item.title}"):
calendar.remove_content(item)
st.success(f"Removed {item.title}")
st.rerun()
with main_tabs[1]:
st.header("Content Optimization")
# Get available content
calendar = st.session_state.calendar_manager.get_calendar()
if not calendar:
st.info("No content available for optimization. Use the Content Planning tab to generate content.")
return
available_content = calendar.get_all_content()
content_options = [item.title for item in available_content]
# Content selection
selected_content = st.selectbox(
"Select content to optimize",
options=content_options,
key="optimize_content_select"
)
if selected_content:
try:
content_item = next(
item for item in available_content
if item.title == selected_content
)
# Create tabs for different optimization aspects
opt_tabs = st.tabs(["Content Optimization", "SEO Optimization", "Preview", "History", "Analytics"])
with opt_tabs[0]:
st.subheader("Content Optimization")
# Show onboarding info if no optimization history
if not optimization_manager.get_optimization_history(content_item.title):
st.info("""
### Content Optimization Guide
Use these tools to enhance your content:
- **Content Tone**: Adjust the writing style to match your brand voice
- **Content Length**: Optimize for your target platform's requirements
- **Engagement Goal**: Focus on specific audience actions
- **Creativity Level**: Balance between creative and professional content
Click 'Generate Optimization' to get started!
""")
# Advanced Optimization Settings
col1, col2 = st.columns(2)
with col1:
tone = st.select_slider(
"Content Tone",
options=['Professional', 'Casual', 'Friendly', 'Authoritative', 'Conversational'],
value='Professional'
options=["Professional", "Casual", "Educational", "Entertaining", "Persuasive"],
value="Professional"
)
length = st.select_slider(
length = st.radio(
"Content Length",
options=['Short', 'Medium', 'Long', 'Comprehensive'],
value='Medium'
["Short", "Medium", "Long"],
horizontal=True
)
with col2:
engagement_goal = st.select_slider(
engagement_goal = st.selectbox(
"Engagement Goal",
options=['Awareness', 'Consideration', 'Conversion', 'Retention'],
value='Consideration'
["Awareness", "Consideration", "Conversion", "Retention"]
)
creativity_level = st.slider(
"Creativity Level",
@@ -236,232 +437,62 @@ def render_content_optimization(
max_value=10,
value=5
)
# Platform-Specific Optimization
st.subheader("Platform-Specific Optimization")
platforms = st.multiselect(
"Target Platforms",
options=[p.name for p in content_item.platforms],
default=[p.name for p in content_item.platforms]
)
# Generate Optimization
if st.button("Generate Optimization"):
with st.spinner("Generating optimization..."):
try:
# Generate optimized content
optimized_content = content_generator.optimize_for_platform(
content=content_item,
platform=Platform[platforms[0]] if platforms else content_item.platforms[0],
requirements={
'tone': tone,
'length': length,
'engagement_goal': engagement_goal,
'creativity_level': creativity_level
}
)
if optimized_content:
# Track optimization
optimization_manager.track_optimization(
content_item.title,
{
'type': 'content',
'changes': optimized_content.get('changes', []),
'metrics': optimized_content.get('metrics', {}),
'content': optimized_content.get('content', ''),
'engagement_metrics': optimized_content.get('engagement_metrics', {})
}
if st.button("Generate Optimization", type="primary"):
with st.spinner("Optimizing content..."):
try:
# Generate optimization
optimization = content_generator.optimize_content(
content=content_item,
tone=tone,
length=length,
engagement_goal=engagement_goal,
creativity_level=creativity_level
)
# Save preview
optimization_manager.save_preview(
content_item.title,
{
'original': content_item.description,
'optimized': optimized_content.get('content', ''),
'changes': optimized_content.get('changes', []),
'metrics': optimized_content.get('metrics', {})
}
)
st.success("Content optimized successfully!")
except Exception as e:
logger.error(f"Error optimizing content: {str(e)}")
st.error(f"Error optimizing content: {str(e)}")
with opt_tabs[1]:
st.subheader("SEO Optimization")
if optimization:
st.success("Content optimized successfully!")
# Show optimization results
st.subheader("Optimization Results")
st.write(optimization.get('content', ''))
# Save optimization history
optimization_manager.track_optimization(
content_item.title,
{
'tone': tone,
'length': length,
'engagement_goal': engagement_goal,
'creativity_level': creativity_level,
'content': optimization.get('content', ''),
'timestamp': datetime.now()
}
)
else:
st.error("Failed to optimize content. Please try again.")
except Exception as e:
logger.error(f"Error optimizing content: {str(e)}")
st.error("An error occurred while optimizing content. Please try again.")
# SEO Settings
with st.expander("SEO Settings", expanded=True):
col1, col2 = st.columns(2)
with col1:
keyword_density = st.slider(
"Target Keyword Density",
min_value=1,
max_value=5,
value=2,
help="Target percentage of keywords in content"
)
internal_linking = st.checkbox(
"Enable Internal Linking",
value=True,
help="Automatically add internal links to related content"
)
with col2:
external_linking = st.checkbox(
"Enable External Linking",
value=True,
help="Add relevant external links for credibility"
)
structured_data = st.checkbox(
"Add Structured Data",
value=True,
help="Include schema.org structured data"
)
with opt_tabs[1]:
st.subheader("SEO Optimization")
# SEO optimization content here
# Generate SEO Optimization
if st.button("Generate SEO Optimization"):
with st.spinner("Generating SEO optimization..."):
try:
# Generate SEO-optimized content
seo_optimized = seo_optimizer.optimize_content(
content=content_item,
content_type=content_item.content_type.name,
language='English',
search_intent='Informational Intent',
settings={
'keyword_density': keyword_density,
'internal_linking': internal_linking,
'external_linking': external_linking,
'structured_data': structured_data
}
)
if seo_optimized:
# Track optimization
optimization_manager.track_optimization(
content_item.title,
{
'type': 'seo',
'changes': seo_optimized.get('changes', []),
'metrics': seo_optimized.get('metrics', {}),
'seo_data': seo_optimized
}
)
# Save preview
optimization_manager.save_preview(
content_item.title,
{
'meta_description': seo_optimized.get('meta_description', ''),
'keywords': seo_optimized.get('keywords', []),
'structured_data': seo_optimized.get('structured_data', {}),
'changes': seo_optimized.get('changes', [])
}
)
st.success("SEO optimization completed!")
except Exception as e:
logger.error(f"Error optimizing SEO: {str(e)}")
st.error(f"Error optimizing SEO: {str(e)}")
with opt_tabs[2]:
st.subheader("Optimization Preview")
with opt_tabs[2]:
st.subheader("Content Preview")
# Content preview here
preview_data = optimization_manager.get_preview(content_item.title)
if preview_data:
# Content Preview
if 'original' in preview_data:
st.markdown("### Content Changes")
col1, col2 = st.columns(2)
with col1:
st.markdown("#### Original Content")
st.write(preview_data['original'])
with col2:
st.markdown("#### Optimized Content")
st.write(preview_data['optimized'])
st.markdown("#### Key Changes")
for change in preview_data.get('changes', []):
st.write(f"- {change}")
# SEO Preview
if 'meta_description' in preview_data:
st.markdown("### SEO Changes")
st.markdown("#### Meta Description")
st.write(preview_data['meta_description'])
st.markdown("#### Keywords")
st.write(", ".join(preview_data['keywords']))
st.markdown("#### Structured Data")
st.json(preview_data['structured_data'])
# Metrics Preview
if 'metrics' in preview_data:
st.markdown("### Optimization Metrics")
metrics = preview_data['metrics']
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Readability Score", f"{metrics.get('readability_score', 0):.1%}")
with col2:
st.metric("SEO Score", f"{metrics.get('seo_score', 0):.1%}")
with col3:
st.metric("Engagement Potential", f"{metrics.get('engagement_potential', 0):.1%}")
else:
st.info("No optimization preview available. Generate optimization first.")
with opt_tabs[3]:
st.subheader("Optimization History")
with opt_tabs[3]:
st.subheader("Optimization History")
# Optimization history here
history = optimization_manager.get_optimization_history(content_item.title)
if history:
for entry in history:
with st.expander(f"Optimization at {entry['timestamp']}"):
st.write(f"Type: {entry['type']}")
st.write("Changes:")
for change in entry.get('changes', []):
st.write(f"- {change}")
if 'metrics' in entry:
st.write("Metrics:")
st.json(entry['metrics'])
else:
st.info("No optimization history available.")
with opt_tabs[4]:
st.subheader("Optimization Analytics")
metrics_history = optimization_manager.get_optimization_metrics(content_item.title)
if metrics_history:
# Convert metrics history to DataFrame
df = pd.DataFrame(metrics_history)
with opt_tabs[4]:
st.subheader("Performance Analytics")
# Analytics content here
# Plot metrics over time
st.line_chart(df[['readability_score', 'seo_score', 'engagement_potential', 'content_quality']])
# Display current metrics
current_metrics = metrics_history[-1]
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Readability", f"{current_metrics.get('readability_score', 0):.1%}")
with col2:
st.metric("SEO Score", f"{current_metrics.get('seo_score', 0):.1%}")
with col3:
st.metric("Engagement", f"{current_metrics.get('engagement_potential', 0):.1%}")
with col4:
st.metric("Overall Quality", f"{current_metrics.get('content_quality', 0):.1%}")
# Display keyword density trend
st.subheader("Keyword Density Trend")
st.line_chart(df['keyword_density'])
else:
st.info("No optimization metrics available. Generate optimization first.")
except Exception as e:
logger.error(f"Error processing selected content: {str(e)}")
st.error("Error processing selected content. Please try again.")
# Remove everything after this point

View File

@@ -0,0 +1,517 @@
import streamlit as st
import pandas as pd
from typing import Dict, List, Any, Optional
from datetime import datetime, timedelta
import logging
from pathlib import Path
import sys
# Add parent directory to path to import existing tools
parent_dir = str(Path(__file__).parent.parent.parent.parent.parent)
if parent_dir not in sys.path:
sys.path.append(parent_dir)
from lib.database.models import ContentItem, ContentType, Platform, SEOData
from lib.ai_seo_tools.content_calendar.core.content_repurposer import SmartContentRepurposingEngine
from lib.ai_seo_tools.content_calendar.core.content_generator import ContentGenerator
logger = logging.getLogger(__name__)
class ContentRepurposingUI:
"""
Streamlit UI component for the Smart Content Repurposing Engine.
"""
def __init__(self):
self.repurposing_engine = SmartContentRepurposingEngine()
self.content_generator = ContentGenerator()
self.logger = logging.getLogger('content_calendar.repurposing_ui')
def render_repurposing_interface(self):
"""Render the main repurposing interface."""
st.header("🔄 Smart Content Repurposing Engine")
st.markdown("Transform your content into multiple platform-optimized pieces with AI-powered repurposing.")
# Create tabs for different repurposing functions
tab1, tab2, tab3, tab4 = st.tabs([
"📝 Single Content Repurposing",
"📚 Content Series Creation",
"🔍 Content Analysis",
"📊 Repurposing Dashboard"
])
with tab1:
self._render_single_content_repurposing()
with tab2:
self._render_content_series_creation()
with tab3:
self._render_content_analysis()
with tab4:
self._render_repurposing_dashboard()
def _render_single_content_repurposing(self):
"""Render the single content repurposing interface."""
st.subheader("Repurpose Single Content")
st.markdown("Transform one piece of content into multiple platform-optimized variations.")
# Content input section
col1, col2 = st.columns([2, 1])
with col1:
st.markdown("### 📄 Source Content")
# Content input options
input_method = st.radio(
"How would you like to provide content?",
["Manual Input", "Upload File", "Select from Calendar"],
horizontal=True
)
source_content = None
if input_method == "Manual Input":
source_content = self._render_manual_content_input()
elif input_method == "Upload File":
source_content = self._render_file_upload_input()
else: # Select from Calendar
source_content = self._render_calendar_selection()
with col2:
st.markdown("### 🎯 Target Platforms")
# Platform selection
available_platforms = [
Platform.TWITTER,
Platform.LINKEDIN,
Platform.INSTAGRAM,
Platform.FACEBOOK,
Platform.WEBSITE
]
selected_platforms = st.multiselect(
"Select target platforms:",
options=available_platforms,
default=[Platform.TWITTER, Platform.LINKEDIN],
format_func=lambda x: x.name.title()
)
# Repurposing strategy
strategy = st.selectbox(
"Repurposing Strategy:",
["adaptive", "atomic", "series"],
help="Adaptive: AI chooses best approach, Atomic: Break into small pieces, Series: Create connected content"
)
# Generate repurposed content
if st.button("🚀 Generate Repurposed Content", type="primary"):
if source_content and selected_platforms:
with st.spinner("Repurposing content..."):
try:
repurposed_content = self.content_generator.repurpose_content_for_platforms(
content_item=source_content,
target_platforms=selected_platforms,
strategy=strategy
)
if repurposed_content:
self._display_repurposed_content(repurposed_content)
else:
st.error("Failed to generate repurposed content. Please try again.")
except Exception as e:
st.error(f"Error during repurposing: {str(e)}")
else:
st.warning("Please provide source content and select at least one target platform.")
def _render_content_series_creation(self):
"""Render the content series creation interface."""
st.subheader("Create Cross-Platform Content Series")
st.markdown("Generate a strategic content series that progressively reveals information across platforms.")
# Source content input
source_content = self._render_manual_content_input(key_suffix="_series")
if source_content:
col1, col2 = st.columns(2)
with col1:
st.markdown("### 🌐 Platform Strategy")
# Platform selection with strategy
platforms = st.multiselect(
"Select platforms for series:",
options=[Platform.TWITTER, Platform.LINKEDIN, Platform.INSTAGRAM, Platform.FACEBOOK, Platform.WEBSITE],
default=[Platform.TWITTER, Platform.LINKEDIN, Platform.WEBSITE],
format_func=lambda x: x.name.title(),
key="series_platforms"
)
series_type = st.selectbox(
"Series Strategy:",
["progressive_disclosure", "platform_native"],
help="Progressive: Gradually reveal info across platforms, Native: Optimize for each platform's strengths"
)
with col2:
st.markdown("### 📅 Timeline Preview")
if platforms:
# Show timeline preview
timeline_df = self._create_series_timeline_preview(source_content, platforms)
st.dataframe(timeline_df, use_container_width=True)
# Generate series
if st.button("📚 Create Content Series", type="primary", key="create_series"):
if platforms:
with st.spinner("Creating content series..."):
try:
series_content = self.content_generator.create_content_series_across_platforms(
source_content=source_content,
platforms=platforms,
series_type=series_type
)
if series_content:
self._display_content_series(series_content)
else:
st.error("Failed to create content series. Please try again.")
except Exception as e:
st.error(f"Error creating series: {str(e)}")
else:
st.warning("Please select at least one platform for the series.")
def _render_content_analysis(self):
"""Render the content analysis interface."""
st.subheader("Content Repurposing Analysis")
st.markdown("Analyze your content's repurposing potential and get AI-powered recommendations.")
# Content input
content_to_analyze = self._render_manual_content_input(key_suffix="_analysis")
if content_to_analyze:
col1, col2 = st.columns([1, 1])
with col1:
available_platforms = st.multiselect(
"Available platforms for analysis:",
options=[Platform.TWITTER, Platform.LINKEDIN, Platform.INSTAGRAM, Platform.FACEBOOK, Platform.WEBSITE],
default=[Platform.TWITTER, Platform.LINKEDIN, Platform.INSTAGRAM, Platform.FACEBOOK, Platform.WEBSITE],
format_func=lambda x: x.name.title(),
key="analysis_platforms"
)
with col2:
if st.button("🔍 Analyze Content", type="primary"):
if available_platforms:
with st.spinner("Analyzing content..."):
try:
analysis = self.content_generator.analyze_content_for_repurposing(
content_item=content_to_analyze,
available_platforms=available_platforms
)
if analysis:
self._display_content_analysis(analysis)
else:
st.error("Failed to analyze content. Please try again.")
except Exception as e:
st.error(f"Error during analysis: {str(e)}")
else:
st.warning("Please select at least one platform for analysis.")
def _render_repurposing_dashboard(self):
"""Render the repurposing dashboard with metrics and insights."""
st.subheader("Repurposing Dashboard")
st.markdown("Track your content repurposing performance and insights.")
# Mock data for demonstration
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Content Pieces Created", "156", "+23")
with col2:
st.metric("Time Saved", "312 hours", "+45 hours")
with col3:
st.metric("Platform Coverage", "85%", "+12%")
with col4:
st.metric("Engagement Boost", "34%", "+8%")
# Recent repurposing activity
st.markdown("### 📈 Recent Repurposing Activity")
# Mock data for recent activity
recent_activity = pd.DataFrame({
'Date': ['2024-01-15', '2024-01-14', '2024-01-13', '2024-01-12'],
'Source Content': ['AI Writing Tips', 'SEO Best Practices', 'Content Strategy Guide', 'Social Media Trends'],
'Platforms': ['Twitter, LinkedIn', 'LinkedIn, Instagram', 'All Platforms', 'Twitter, Facebook'],
'Pieces Created': [3, 2, 5, 2],
'Status': ['Published', 'Scheduled', 'Draft', 'Published']
})
st.dataframe(recent_activity, use_container_width=True)
# Performance insights
st.markdown("### 💡 Performance Insights")
insights_col1, insights_col2 = st.columns(2)
with insights_col1:
st.info("🎯 **Best Performing Platform**: LinkedIn posts show 45% higher engagement when repurposed from blog content.")
with insights_col2:
st.success("📊 **Optimization Tip**: Twitter threads perform 60% better when created from long-form content with statistics.")
def _render_manual_content_input(self, key_suffix: str = "") -> Optional[ContentItem]:
"""Render manual content input form."""
with st.form(f"content_input_form{key_suffix}"):
title = st.text_input("Content Title:", key=f"title{key_suffix}")
content_type = st.selectbox(
"Content Type:",
options=[ContentType.BLOG_POST, ContentType.SOCIAL_MEDIA, ContentType.VIDEO, ContentType.NEWSLETTER],
format_func=lambda x: x.name.replace('_', ' ').title(),
key=f"content_type{key_suffix}"
)
description = st.text_area(
"Content Description/Body:",
height=200,
help="Paste your content here. This will be analyzed and repurposed.",
key=f"description{key_suffix}"
)
col1, col2 = st.columns(2)
with col1:
author = st.text_input("Author:", value="Content Creator", key=f"author{key_suffix}")
with col2:
tags = st.text_input("Tags (comma-separated):", key=f"tags{key_suffix}")
submitted = st.form_submit_button("📝 Use This Content")
if submitted and title and description:
# Create ContentItem
content_item = ContentItem(
title=title,
description=description,
content_type=content_type,
platforms=[],
publish_date=datetime.now(),
status="draft",
author=author,
tags=tags.split(',') if tags else [],
notes="",
seo_data=SEOData(title=title, meta_description="", keywords=[], structured_data={})
)
return content_item
return None
def _render_file_upload_input(self) -> Optional[ContentItem]:
"""Render file upload input."""
uploaded_file = st.file_uploader(
"Upload content file:",
type=['txt', 'md', 'docx'],
help="Upload a text file, markdown file, or Word document"
)
if uploaded_file:
try:
# Read file content
if uploaded_file.type == "text/plain":
content = str(uploaded_file.read(), "utf-8")
else:
content = str(uploaded_file.read(), "utf-8") # Simplified for demo
# Extract title from filename
title = uploaded_file.name.split('.')[0].replace('_', ' ').title()
# Create ContentItem
content_item = ContentItem(
title=title,
description=content,
content_type=ContentType.BLOG_POST,
platforms=[],
publish_date=datetime.now(),
status="draft",
author="Uploaded Content",
tags=[],
notes=f"Uploaded from file: {uploaded_file.name}",
seo_data=SEOData(title=title, meta_description="", keywords=[], structured_data={})
)
st.success(f"✅ File uploaded: {uploaded_file.name}")
return content_item
except Exception as e:
st.error(f"Error reading file: {str(e)}")
return None
def _render_calendar_selection(self) -> Optional[ContentItem]:
"""Render calendar content selection."""
st.info("📅 Calendar integration coming soon! For now, please use manual input or file upload.")
return None
def _display_repurposed_content(self, repurposed_content: List[ContentItem]):
"""Display the repurposed content results."""
st.success(f"✅ Successfully created {len(repurposed_content)} repurposed content pieces!")
for i, content in enumerate(repurposed_content):
with st.expander(f"📱 {content.platforms[0].name.title()} - {content.title}"):
st.markdown(f"**Platform:** {content.platforms[0].name.title()}")
st.markdown(f"**Content Type:** {content.content_type.name.replace('_', ' ').title()}")
st.markdown(f"**Scheduled for:** {content.publish_date.strftime('%Y-%m-%d')}")
st.markdown("**Content:**")
st.write(content.description)
if content.tags:
st.markdown(f"**Tags:** {', '.join(content.tags)}")
# Action buttons
col1, col2, col3 = st.columns(3)
with col1:
if st.button(f"📝 Edit", key=f"edit_{i}"):
st.info("Edit functionality coming soon!")
with col2:
if st.button(f"📅 Schedule", key=f"schedule_{i}"):
st.info("Scheduling functionality coming soon!")
with col3:
if st.button(f"📋 Copy", key=f"copy_{i}"):
st.code(content.description)
def _display_content_series(self, series_content: Dict[str, List[ContentItem]]):
"""Display the content series results."""
total_pieces = sum(len(pieces) for pieces in series_content.values())
st.success(f"✅ Successfully created content series with {total_pieces} pieces across {len(series_content)} platforms!")
for platform, content_pieces in series_content.items():
st.markdown(f"### 📱 {platform.title()} Series ({len(content_pieces)} pieces)")
for i, content in enumerate(content_pieces):
with st.expander(f"Part {i+1}: {content.title}"):
st.markdown(f"**Scheduled for:** {content.publish_date.strftime('%Y-%m-%d')}")
st.markdown("**Content:**")
st.write(content.description)
if content.tags:
st.markdown(f"**Tags:** {', '.join(content.tags)}")
def _display_content_analysis(self, analysis: Dict[str, Any]):
"""Display content analysis results."""
st.markdown("### 📊 Content Analysis Results")
# Content metrics
col1, col2, col3 = st.columns(3)
content_analysis = analysis.get('content_analysis', {})
with col1:
st.metric("Word Count", content_analysis.get('word_count', 0))
with col2:
richness = content_analysis.get('content_richness', 'Unknown')
st.metric("Content Richness", richness)
with col3:
potential = content_analysis.get('repurposing_potential', 'Unknown')
st.metric("Repurposing Potential", potential)
# Recommendations
st.markdown("### 💡 Recommendations")
col1, col2 = st.columns(2)
with col1:
st.markdown("**Recommended Platforms:**")
platforms = analysis.get('platform_suggestions', [])
for platform in platforms:
st.write(f"{platform.name.title()}")
with col2:
st.markdown("**Suggested Strategies:**")
strategies = analysis.get('strategy_suggestions', [])
for strategy in strategies:
st.write(f"{strategy.replace('_', ' ').title()}")
# Content atoms
st.markdown("### 🔬 Content Atoms Analysis")
atoms = content_analysis.get('content_atoms', {})
for atom_type, atom_list in atoms.items():
if atom_list:
with st.expander(f"{atom_type.title()} ({len(atom_list)} found)"):
for atom in atom_list:
st.write(f"{atom}")
# Estimated output
estimated = analysis.get('estimated_output', {})
if estimated:
st.markdown("### 📈 Estimated Output")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Total Pieces", estimated.get('total_pieces', 0))
with col2:
st.metric("Time Savings", estimated.get('time_savings', '0 hours'))
with col3:
st.metric("Content Multiplication", estimated.get('content_multiplication', '1x'))
def _create_series_timeline_preview(self, content: ContentItem, platforms: List[Platform]) -> pd.DataFrame:
"""Create a preview timeline for content series."""
timeline_data = []
base_date = datetime.now()
for i, platform in enumerate(platforms):
release_date = base_date + timedelta(days=i)
timeline_data.append({
'Platform': platform.name.title(),
'Release Date': release_date.strftime('%Y-%m-%d'),
'Content Type': self._get_platform_content_type(platform),
'Strategy': self._get_platform_strategy(platform)
})
return pd.DataFrame(timeline_data)
def _get_platform_content_type(self, platform: Platform) -> str:
"""Get content type for platform."""
types = {
Platform.TWITTER: "Thread/Tweet",
Platform.LINKEDIN: "Professional Post",
Platform.INSTAGRAM: "Visual Post",
Platform.FACEBOOK: "Engaging Post",
Platform.WEBSITE: "Blog Article"
}
return types.get(platform, "Standard Post")
def _get_platform_strategy(self, platform: Platform) -> str:
"""Get strategy for platform."""
strategies = {
Platform.TWITTER: "Hook & Engage",
Platform.LINKEDIN: "Authority Building",
Platform.INSTAGRAM: "Visual Storytelling",
Platform.FACEBOOK: "Community Discussion",
Platform.WEBSITE: "Complete Information"
}
return strategies.get(platform, "Standard Approach")
# Main function to render the UI
def render_content_repurposing_ui():
"""Main function to render the content repurposing UI."""
ui = ContentRepurposingUI()
ui.render_repurposing_interface()
# For testing
if __name__ == "__main__":
render_content_repurposing_ui()

View File

@@ -2,10 +2,10 @@ import streamlit as st
from typing import Dict, Any, List
from datetime import datetime, timedelta
import pandas as pd
from ...core.content_generator import ContentGenerator
from ...core.ai_generator import AIGenerator
from ...integrations.seo_optimizer import SEOOptimizer
from ...models.calendar import ContentItem, ContentType, Platform, SEOData
from lib.ai_seo_tools.content_calendar.core.content_generator import ContentGenerator
from lib.ai_seo_tools.content_calendar.core.ai_generator import AIGenerator
from lib.ai_seo_tools.content_calendar.integrations.seo_optimizer import SEOOptimizer
from lib.database.models import ContentItem, ContentType, Platform, SEOData
import logging
logger = logging.getLogger('content_calendar.series')
@@ -21,7 +21,7 @@ class SeriesManager:
st.session_state.series_performance = {}
def create_series(self, series_id: str, topic: str, num_pieces: int, content_type: ContentType,
platforms: List[Platform], schedule_strategy: str = 'linear') -> Dict[str, Any]:
platforms: List[Platform], schedule_strategy: str = 'linear', series_type: str = '', series_flow: str = '', metadata: Dict[str, Any] = {}) -> Dict[str, Any]:
"""Create a new content series with tracking and scheduling."""
try:
series = {
@@ -31,12 +31,15 @@ class SeriesManager:
'content_type': content_type,
'platforms': platforms,
'schedule_strategy': schedule_strategy,
'series_type': series_type,
'series_flow': series_flow,
'pieces': [],
'performance': {},
'created_at': datetime.now(),
'status': 'draft',
'relationships': {},
'platform_distribution': {p.name: [] for p in platforms}
'platform_distribution': {p.name: [] for p in platforms},
'metadata': metadata
}
st.session_state.content_series[series_id] = series
return series
@@ -50,23 +53,38 @@ class SeriesManager:
if series_id in st.session_state.content_series:
series = st.session_state.content_series[series_id]
piece_id = f"piece_{len(series['pieces'])}"
piece['id'] = piece_id
# Create a structured piece object
structured_piece = {
'id': piece_id,
'title': piece.get('title', f"Part {len(series['pieces']) + 1}"),
'content': piece.get('content', ''),
'platform': piece.get('platform', series['platforms'][0]),
'scheduled_date': None,
'status': 'draft',
'relationships': {
'previous': None,
'next': None
},
'performance': {
'engagement': 0,
'reach': 0,
'conversion_rate': 0
}
}
# Track relationships
if series['pieces']:
previous_piece = series['pieces'][-1]
piece['relationships'] = {
'previous': previous_piece['id'],
'next': None
}
previous_piece['relationships']['next'] = piece_id
structured_piece['relationships']['previous'] = previous_piece['id']
structured_piece['relationships']['next'] = piece_id
# Add to platform distribution
for platform in piece.get('platforms', []):
if platform.name in series['platform_distribution']:
series['platform_distribution'][platform.name].append(piece_id)
platform_name = structured_piece['platform'].name
if platform_name in series['platform_distribution']:
series['platform_distribution'][platform_name].append(piece_id)
series['pieces'].append(piece)
series['pieces'].append(structured_piece)
return True
return False
except Exception as e:
@@ -176,11 +194,68 @@ class SeriesManager:
logger.error(f"Error scheduling series: {str(e)}")
return False
def render_content_series_generator(ai_generator: AIGenerator, content_generator: ContentGenerator,
seo_optimizer: SEOOptimizer):
"""Render the content series generator interface with enhanced features."""
def render_content_series_generator(
ai_generator: AIGenerator,
content_generator: ContentGenerator,
seo_optimizer: SEOOptimizer
):
"""Render the content series generator interface."""
st.header("Content Series Generator")
# Check if calendar manager is available
if 'calendar_manager' not in st.session_state:
st.error("Calendar manager not initialized. Please refresh the page.")
return
# Get available content
try:
available_content = st.session_state.calendar_manager.get_calendar().get_all_content()
content_options = [item.title for item in available_content]
except Exception as e:
logger.error(f"Error getting content options: {str(e)}")
st.error("Error loading content. Please try again.")
return
if not content_options:
st.info("""
## Welcome to Content Series Generator! 📚
Create and manage content series across multiple platforms. Here's what you can do:
### Features:
- 📝 **Series Creation**: Generate connected content pieces
- 🔄 **Cross-Platform Distribution**: Optimize for different platforms
- 📊 **Series Analytics**: Track performance across the series
- 📅 **Smart Scheduling**: Plan content distribution
### Getting Started:
1. First, add some content to your calendar
2. Select a topic for your content series
3. Configure series parameters and platforms
4. Generate and schedule your series
Ready to get started? Add some content to your calendar first!
""")
return
# Series Configuration
st.subheader("Create New Content Series")
# Show onboarding info if no series exist
if not st.session_state.get('content_series', {}):
st.info("""
### Content Series Guide
Create engaging content series with these features:
- **Series Planning**: Define your series structure and goals
- **Content Generation**: Create connected content pieces
- **Platform Optimization**: Adapt content for each platform
- **Performance Tracking**: Monitor series success
Fill out the form below to create your first series!
""")
# Initialize series manager
series_manager = SeriesManager()
@@ -231,144 +306,125 @@ def render_content_series_generator(ai_generator: AIGenerator, content_generator
try:
# Create series
series_id = f"series_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
# Prepare metadata with default values
metadata = {
'tone': series_tone,
'length': 'medium', # Default length
'engagement_goal': series_goals[0] if series_goals else 'Awareness',
'creativity_level': 'balanced' # Default creativity level
}
series = series_manager.create_series(
series_id=series_id,
topic=series_topic,
num_pieces=num_pieces,
content_type=ContentType[content_type],
platforms=[Platform[p] for p in platforms],
schedule_strategy=schedule_strategy
schedule_strategy=schedule_strategy,
series_type=series_goals[0] if series_goals else 'Awareness',
series_flow='sequential', # Default flow
metadata=metadata
)
if series:
# Generate series content
for i in range(num_pieces):
content_item = ContentItem(
title=f"{series_topic} - Part {i+1}",
description="",
content_type=ContentType[content_type],
platforms=[Platform[p] for p in platforms],
publish_date=datetime.now() + timedelta(days=i*7),
seo_data=SEOData(
title=f"{series_topic} - Part {i+1}",
meta_description="",
keywords=[],
structured_data={}
),
status='Draft'
)
# Generate content using AI
base_content = ai_generator.generate_series_content(
content_item=content_item,
series_info={
'topic': series_topic,
'part_number': i+1,
'total_parts': num_pieces,
'content_type': content_type,
'platforms': platforms,
'audience': target_audience,
'goals': series_goals,
'tone': series_tone
}
)
if base_content:
# Enhance with Content Generator
enhanced_content = content_generator.enhance_series_content(
content=base_content,
series_info={
'topic': series_topic,
'part_number': i+1,
'total_parts': num_pieces
}
series_content = content_generator.generate_content(
content_type=ContentType[content_type],
topic=series_topic,
platforms=[Platform[p] for p in platforms],
num_pieces=num_pieces,
requirements={
'tone': series_tone,
'length': metadata['length'],
'engagement_goal': metadata['engagement_goal'],
'creativity_level': metadata['creativity_level'],
'series_type': metadata['engagement_goal'],
'series_flow': 'sequential',
'target_audience': target_audience
}
)
if series_content:
# Add content pieces to series
for piece in series_content:
series_manager.add_piece(
series_id=series['id'],
piece=piece
)
if enhanced_content:
base_content.update(enhanced_content)
# Add to series
series_manager.add_piece(series_id, {
'part_number': i+1,
'content': base_content,
'seo_data': seo_optimizer.optimize_content(
content=base_content,
content_type=content_type,
language='English',
search_intent='Informational Intent'
# Schedule series
if schedule_strategy == 'linear':
start_date = st.date_input("Start Date", datetime.now())
interval = st.number_input("Days between pieces", min_value=1, value=7)
series_manager.schedule_series(
series_id=series['id'],
start_date=start_date,
interval_days=interval
)
elif schedule_strategy == 'burst':
start_date = st.date_input("Start Date", datetime.now())
burst_size = st.number_input("Burst Size", min_value=1, value=1)
series_manager.schedule_series(
series_id=series['id'],
start_date=start_date,
interval_days=1,
burst_size=burst_size
)
else: # custom
for i, piece in enumerate(series_manager.series_data[series['id']]['pieces']):
piece['scheduled_date'] = st.date_input(
f"Publish Date for Part {i+1}",
datetime.now() + timedelta(days=i*7)
)
})
st.success(f"Generated {num_pieces} content pieces for series!")
# Display series preview
with st.expander("Series Preview", expanded=True):
for piece in series_manager.series_data[series_id]['pieces']:
st.markdown(f"### Part {piece['part_number']}")
st.json(piece['content'])
# Platform-specific previews
st.markdown("#### Platform Previews")
if st.button("Save Schedule"):
st.success("Series schedule saved!")
st.success(f"Generated {num_pieces} content pieces for series!")
# Display series preview
with st.expander("Series Preview", expanded=True):
for piece in series_manager.series_data[series_id]['pieces']:
st.markdown(f"### Part {piece['part_number']}")
st.json(piece['content'])
# Platform-specific previews
st.markdown("#### Platform Previews")
for platform in platforms:
with st.expander(f"{platform} Preview"):
st.write(piece['content'].get('platform_previews', {}).get(platform, 'No preview available'))
# Series performance tracking
st.subheader("Series Performance")
performance_data = series_manager.get_series_performance(series_id)
if performance_data:
st.write("### Overall Performance")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Total Engagement", f"{performance_data['overall']['total_engagement']:.1f}%")
with col2:
st.metric("Total Reach", f"{performance_data['overall']['total_reach']:,}")
with col3:
st.metric("Conversion Rate", f"{performance_data['overall']['conversion_rate']:.1f}%")
# Platform-specific performance
st.write("### Platform Performance")
for platform in platforms:
with st.expander(f"{platform} Preview"):
st.write(piece['content'].get('platform_previews', {}).get(platform, 'No preview available'))
# Series scheduling
st.subheader("Series Scheduling")
if schedule_strategy == 'linear':
start_date = st.date_input("Start Date", datetime.now())
interval = st.number_input("Days between pieces", min_value=1, value=7)
if st.button("Schedule Series"):
series_manager.schedule_series(series_id, start_date, interval)
st.success("Series scheduled successfully!")
elif schedule_strategy == 'burst':
start_date = st.date_input("Start Date", datetime.now())
if st.button("Schedule Series"):
series_manager.schedule_series(series_id, start_date, interval=1)
st.success("Series scheduled successfully!")
else: # custom
for i, piece in enumerate(series_manager.series_data[series_id]['pieces']):
piece['scheduled_date'] = st.date_input(
f"Publish Date for Part {i+1}",
datetime.now() + timedelta(days=i*7)
)
if st.button("Save Schedule"):
st.success("Series schedule saved!")
# Series performance tracking
st.subheader("Series Performance")
performance_data = series_manager.get_series_performance(series_id)
if performance_data:
st.write("### Overall Performance")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Total Engagement", f"{performance_data['overall']['total_engagement']:.1f}%")
with col2:
st.metric("Total Reach", f"{performance_data['overall']['total_reach']:,}")
with col3:
st.metric("Conversion Rate", f"{performance_data['overall']['conversion_rate']:.1f}%")
# Platform-specific performance
st.write("### Platform Performance")
for platform in platforms:
with st.expander(f"{platform} Performance"):
platform_data = performance_data['platforms'].get(platform, {})
st.write(f"Engagement: {platform_data.get('engagement', 0):.1f}%")
st.write(f"Reach: {platform_data.get('reach', 0):,}")
st.write(f"Conversions: {platform_data.get('conversion_rate', 0):.1f}%")
# Performance trends
st.write("### Performance Trends")
trend_data = performance_data['trends']
st.line_chart(pd.DataFrame({
'Engagement': trend_data['engagement'],
'Reach': trend_data['reach'],
'Conversions': trend_data['conversions']
}))
with st.expander(f"{platform} Performance"):
platform_data = performance_data['platforms'].get(platform, {})
st.write(f"Engagement: {platform_data.get('engagement', 0):.1f}%")
st.write(f"Reach: {platform_data.get('reach', 0):,}")
st.write(f"Conversions: {platform_data.get('conversion_rate', 0):.1f}%")
# Performance trends
st.write("### Performance Trends")
trend_data = performance_data['trends']
st.line_chart(pd.DataFrame({
'Engagement': trend_data['engagement'],
'Reach': trend_data['reach'],
'Conversions': trend_data['conversions']
}))
except Exception as e:
logger.error(f"Error generating series: {str(e)}", exc_info=True)
@@ -389,4 +445,13 @@ def render_content_series_generator(ai_generator: AIGenerator, content_generator
if st.button(f"Delete Series", key=f"delete_{series_id}"):
del st.session_state.content_series[series_id]
st.experimental_rerun()
st.rerun()
def on_series_complete():
"""Handle series completion."""
try:
st.session_state.series_complete = True
st.rerun()
except Exception as e:
logger.error(f"Error handling series completion: {str(e)}")
st.error("An error occurred while completing the series. Please try again.")

View File

@@ -1,6 +1,6 @@
import streamlit as st
from typing import Dict, Any
from lib.ai_seo_tools.content_calendar.models.calendar import ContentItem
from lib.database.models import ContentItem
import logging
logger = logging.getLogger(__name__)

View File

@@ -1,64 +1,71 @@
import streamlit as st
import pandas as pd
from datetime import datetime
from datetime import datetime, timedelta
import logging
import sys
import hashlib
from pathlib import Path
from typing import Dict, Any
from .calendar_view import render_calendar_view
from .filters import render_filters
from .add_content_modal import render_add_content_modal
from .ai_suggestions_modal import render_ai_suggestions_modal
from .components.performance_insights import render_performance_insights
from .components.content_series import render_content_series_generator
from .components.ab_testing import render_ab_testing
from .components.content_optimization import render_content_optimization
from .components.ab_testing import render_ab_testing
from .components.content_series import render_content_series_generator
from .components.performance_insights import render_performance_insights
import json
from lib.content_scheduler.ui.dashboard import run_dashboard as run_scheduler_dashboard
# Add parent directory to path to import existing tools
parent_dir = str(Path(__file__).parent.parent.parent.parent)
if parent_dir not in sys.path:
sys.path.append(parent_dir)
from lib.database.models import ContentItem, ContentType, Platform, get_engine, get_session, init_db
from ..core.calendar_manager import CalendarManager
from ..core.content_brief import ContentBriefGenerator
from ..core.content_generator import ContentGenerator
from ..core.ai_generator import AIGenerator
from ..integrations.platform_adapters import UnifiedPlatformAdapter
from ..core.content_brief import ContentBriefGenerator
from ..integrations.seo_optimizer import SEOOptimizer
from lib.ai_seo_tools.content_calendar.models.calendar import ContentItem, Platform, ContentType, SEOData, Calendar
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
from typing import Dict, Any, List, Tuple
import json
from lib.integrations.platform_adapters import PlatformAdapter, UnifiedPlatformAdapter
# Initialize logger
logger = logging.getLogger(__name__)
# Initialize DB/session (do this once at app startup)
engine = get_engine()
init_db(engine)
session = get_session(engine)
# Import content repurposing UI with error handling
def render_smart_repurposing_tab():
"""Render the Smart Content Repurposing tab with error handling."""
try:
from lib.ai_seo_tools.content_calendar.ui.components.content_repurposing_ui import render_content_repurposing_ui
render_content_repurposing_ui()
except ImportError as e:
st.error(f"Smart Content Repurposing feature is not available: {str(e)}")
st.info("Please ensure all dependencies are installed correctly.")
except Exception as e:
st.error(f"Error loading Smart Content Repurposing: {str(e)}")
st.info("Please check the logs for more details.")
class ContentCalendarDashboard:
"""Interactive dashboard for content calendar management."""
def __init__(self):
self.logger = logging.getLogger('content_calendar.dashboard')
self.logger.info("Initializing ContentCalendarDashboard")
# Initialize calendar manager and store in session state
if 'calendar_manager' not in st.session_state:
st.session_state.calendar_manager = CalendarManager()
st.session_state.calendar_manager.load_calendar_from_json()
self.calendar_manager = st.session_state.calendar_manager
self.content_brief_generator = ContentBriefGenerator()
self.content_generator = ContentGenerator()
self.ai_generator = AIGenerator()
self.platform_adapter = UnifiedPlatformAdapter()
self.seo_optimizer = SEOOptimizer()
# Initialize A/B testing state
# Initialize session state variables
if 'ab_test_results' not in st.session_state:
st.session_state.ab_test_results = {}
# Initialize content optimization state
if 'optimization_history' not in st.session_state:
st.session_state.optimization_history = {}
# Ensure a calendar exists
if not self.calendar_manager.get_calendar():
self.calendar_manager._calendar = Calendar(
start_date=datetime.now(),
duration='monthly',
platforms=[Platform.WEBSITE, Platform.INSTAGRAM, Platform.TWITTER, Platform.LINKEDIN, Platform.FACEBOOK],
schedule={}
)
# Initialize session state
if 'calendar_data' not in st.session_state:
st.session_state.calendar_data = None
if 'selected_content' not in st.session_state:
@@ -67,9 +74,8 @@ class ContentCalendarDashboard:
st.session_state.view_mode = 'day'
if 'selected_date' not in st.session_state:
st.session_state.selected_date = datetime.now()
self.logger.info("ContentCalendarDashboard initialized successfully")
def render(self):
self.logger.info("Starting dashboard render (tabbed UI)")
try:
@@ -78,8 +84,15 @@ class ContentCalendarDashboard:
st.markdown("""
Plan, schedule, and manage your content strategy with AI-powered insights. Use the calendar to organize your content and leverage AI tools for optimization.
""")
tabs = st.tabs(["Content Planning", "Content Optimization", "A/B Testing", "Content Series", "Analytics"])
tabs = st.tabs([
"Content Planning",
"Content Optimization",
"🔄 Smart Repurposing",
"A/B Testing",
"Content Series",
"Analytics",
"Content Scheduling"
])
with tabs[0]:
icon_map = {
'Blog': '📝', 'Website': '🌐', 'Instagram': '📸', 'Twitter': '🐦', 'LinkedIn': '💼', 'Facebook': '📘',
@@ -90,17 +103,26 @@ class ContentCalendarDashboard:
}
calendar_data = self._get_calendar_data()
def on_edit(row):
st.session_state["editing_item_key"] = self._get_item_key(row)
st.experimental_rerun()
try:
st.session_state.editing_content = row
st.rerun()
except Exception as e:
logger.error(f"Error handling edit action: {str(e)}")
st.error("An error occurred while editing content. Please try again.")
def on_delete(row):
self._delete_content(row)
st.experimental_rerun()
try:
self._delete_content(row)
st.success(f"Successfully deleted content: {row['title']}")
st.rerun()
except Exception as e:
logger.error(f"Error handling delete action: {str(e)}")
st.error("An error occurred while deleting content. Please try again.")
def on_generate(row):
st.session_state['show_ai_modal'] = True
st.session_state['ai_modal_topic'] = row['title']
st.session_state['ai_modal_type'] = str(row['type'])
st.session_state['ai_modal_platform'] = str(row['platform'])
st.experimental_rerun()
st.rerun()
render_calendar_view(
calendar_data=calendar_data,
icon_map=icon_map,
@@ -121,7 +143,7 @@ class ContentCalendarDashboard:
})
st.session_state['show_add_content_dialog'] = False
st.success("Content added!")
st.experimental_rerun()
st.rerun()
def handle_generate_with_ai(title, platform, content_type):
st.session_state['show_add_content_dialog'] = False
st.session_state['show_ai_modal'] = True
@@ -145,48 +167,47 @@ class ContentCalendarDashboard:
)
if st.button("Close"):
st.session_state['show_ai_modal'] = False
with tabs[1]:
render_content_optimization(
content_generator=self.content_generator,
ai_generator=self.ai_generator,
seo_optimizer=self.seo_optimizer
)
with tabs[2]:
render_ab_testing(self.content_generator, self.calendar_manager)
render_smart_repurposing_tab()
with tabs[3]:
render_ab_testing(self.content_generator, None)
with tabs[4]:
render_content_series_generator(
self.ai_generator,
self.content_generator,
self.seo_optimizer
)
with tabs[4]:
with tabs[5]:
st.header("Analytics")
st.markdown("### Performance Insights")
all_content = session.query(ContentItem).all()
selected_content = st.selectbox(
"Select content to analyze",
options=[item.title for item in self.calendar_manager.get_calendar().get_all_content()],
options=[item.title for item in all_content],
key="analytics_content_select"
)
if selected_content:
content_item = next(
item for item in self.calendar_manager.get_calendar().get_all_content()
item for item in all_content
if item.title == selected_content
)
render_performance_insights(content_item, self.platform_adapter)
st.markdown("### Optimization History")
if selected_content in st.session_state.optimization_history:
st.json(st.session_state.optimization_history[selected_content])
with tabs[6]:
run_scheduler_dashboard()
self.logger.info("Dashboard render completed successfully (tabbed UI)")
except Exception as e:
self.logger.error(f"Error rendering dashboard: {str(e)}", exc_info=True)
st.error(f"An error occurred: {str(e)}")
def _inject_custom_css(self):
st.markdown("""
<style>
@@ -197,20 +218,16 @@ class ContentCalendarDashboard:
def _get_calendar_data(self):
self.logger.info("_get_calendar_data called")
try:
calendar_obj = self.calendar_manager.get_calendar()
if not calendar_obj:
self.logger.info("No calendar found in manager")
return None
all_content = session.query(ContentItem).all()
data = []
for date_str, items in calendar_obj.schedule.items():
for item in items:
data.append({
'date': pd.to_datetime(date_str),
'title': item.title,
'platform': item.platforms[0] if item.platforms else 'Unknown',
'type': item.content_type,
'status': item.status
})
for item in all_content:
data.append({
'date': item.publish_date,
'title': item.title,
'platform': item.platforms[0] if item.platforms else 'Unknown',
'type': item.content_type.value if hasattr(item.content_type, 'value') else str(item.content_type),
'status': item.status
})
df = pd.DataFrame(data) if data else None
return df
except Exception as e:
@@ -219,10 +236,6 @@ class ContentCalendarDashboard:
return None
def _add_content(self, content):
calendar = self.calendar_manager.get_calendar()
if not calendar:
st.error("No calendar found. Please create a calendar first.")
return
platform_map = {
'Blog': Platform.WEBSITE,
'Instagram': Platform.INSTAGRAM,
@@ -238,41 +251,32 @@ class ContentCalendarDashboard:
'Newsletter': ContentType.NEWSLETTER,
}
content_type_enum = content_type_map.get(content['type'], ContentType.BLOG_POST)
seo_data = SEOData(
title=content['title'],
meta_description="",
keywords=[],
structured_data={},
)
new_item = ContentItem(
title=content['title'],
description="",
content_type=content_type_enum,
platforms=[platform_enum],
platforms=[platform_enum.value],
publish_date=pd.to_datetime(content['publish_date']),
seo_data=seo_data,
status=content.get('status', 'Draft')
status=content.get('status', 'Draft'),
author=None,
tags=[],
notes=None,
seo_data={}
)
calendar.add_content(new_item)
self.calendar_manager.save_calendar_to_json()
session.add(new_item)
session.commit()
def _delete_content(self, row):
calendar = self.calendar_manager.get_calendar()
if not calendar:
return
for date_str, items in list(calendar.schedule.items()):
calendar.schedule[date_str] = [
item for item in items
if not (
item.title == row['title'] and
str(item.publish_date.date()) == str(row['date'].date()) and
item.platforms[0].name == str(row['platform']) and
item.content_type.name == str(row['type'])
)
]
if not calendar.schedule[date_str]:
del calendar.schedule[date_str]
self.calendar_manager.save_calendar_to_json()
# Find by title and publish_date (could be improved with unique IDs)
all_content = session.query(ContentItem).all()
for item in all_content:
if (item.title == row['title'] and
str(item.publish_date.date()) == str(row['date'].date()) and
(item.platforms[0] if item.platforms else 'Unknown') == str(row['platform']) and
(item.content_type.value if hasattr(item.content_type, 'value') else str(item.content_type)) == str(row['type'])):
session.delete(item)
session.commit()
break
def _edit_content(self, row, new_title, new_platform, new_type, new_status):
self._delete_content(row)