ALwrity Version 0.5.0 (Fastapi + React )

This commit is contained in:
ajaysi
2025-08-06 12:48:02 +05:30
parent f28a919caa
commit 32f97fa6b3
476 changed files with 115544 additions and 28747 deletions

View File

@@ -0,0 +1,294 @@
import streamlit as st
from typing import Dict, Any, List
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: ContentGenerator, calendar_manager: CalendarManager):
"""Render the A/B testing interface."""
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 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,
content: ContentItem,
num_variants: int
) -> List[Dict[str, Any]]:
"""Generate A/B test variants for content."""
try:
logger.info(f"Generating {num_variants} variants for content: {content.title}")
# Convert content to dictionary format
content_dict = {
'title': content.title,
'content': content.description,
'metadata': {
'platform': content.platforms[0].name if content.platforms else 'Unknown',
'content_type': content.content_type.name
}
}
variants = []
for i in range(num_variants):
# Generate different variations
variant = content_generator.generate_variation(
content=content_dict,
variation_type=f"variant_{i+1}"
)
if variant:
variants.append(variant)
return variants
except Exception as e:
logger.error(f"Error generating variants: {str(e)}")
return []
def _analyze_ab_test_results(content_item: ContentItem) -> Dict[str, Any]:
"""Analyze results of A/B testing for content optimization."""
try:
logger.info(f"Analyzing A/B test results for: {content_item.title}")
if content_item.title not in st.session_state.ab_test_results:
raise ValueError("No A/B test results found for this content")
test_data = st.session_state.ab_test_results[content_item.title]
variants = test_data['variants']
# Calculate performance metrics
results = {
'total_engagement': sum(v['metrics']['engagement_score'] for v in variants),
'total_conversions': sum(v['metrics']['conversion_rate'] for v in variants),
'total_reach': sum(v['metrics']['reach'] for v in variants),
'best_performing_variant': max(variants, key=lambda x: x['metrics']['engagement_score']),
'recommendations': []
}
# Generate recommendations
for variant in variants:
if variant['metrics']['engagement_score'] > 0.7: # High engagement threshold
results['recommendations'].append({
'variant_id': variant['variant_id'],
'reason': 'High engagement score',
'suggested_actions': ['Scale this variant', 'Apply learnings to other content']
})
# Update test status
test_data['status'] = 'completed'
test_data['results'] = results
logger.info("A/B test results analyzed successfully")
return results
except Exception as e:
logger.error(f"Error analyzing A/B test results: {str(e)}", exc_info=True)
st.error(f"Error analyzing A/B test results: {str(e)}")
return {}
def _display_test_results(results: Dict[str, Any]) -> None:
"""Display A/B test results in the UI."""
with st.expander("Overall Performance", expanded=True):
col1, col2, col3 = st.columns(3)
with col1:
st.metric(
"Total Engagement",
f"{results['total_engagement']:.1f}%"
)
with col2:
st.metric(
"Total Conversions",
f"{results['total_conversions']:.1f}%"
)
with col3:
st.metric(
"Total Reach",
f"{results['total_reach']:,}"
)
with st.expander("Best Performing Variant", expanded=True):
best_variant = results['best_performing_variant']
st.markdown(f"### {best_variant['variant_id']}")
st.json(best_variant['content'])
with st.expander("Recommendations", expanded=True):
for rec in results['recommendations']:
st.markdown(f"#### {rec['variant_id']}")
st.write(f"Reason: {rec['reason']}")
st.write("Suggested Actions:")
for action in rec['suggested_actions']:
st.write(f"- {action}")

View File

@@ -0,0 +1,2 @@
def render_badge(platform_disp, platform_icon, type_disp, status_disp):
return f"<span class='badge-content-calendar badge-platform-{platform_disp.lower()}'>{platform_icon} {platform_disp} &nbsp;|&nbsp; {type_disp} &nbsp;|&nbsp; <span class='chip-status chip-status-{status_disp.lower()}'>{status_disp}</span></span>"

View File

@@ -0,0 +1,22 @@
import streamlit as st
def render_content_card(row, is_editing, on_edit, on_delete, on_generate, icon_map, status_color, platform_disp, type_disp, status_disp, platform_icon, type_icon, item_key):
st.markdown(f"<div class='card-content-calendar'>", unsafe_allow_html=True)
st.markdown(f"<div style='display:flex;align-items:center;justify-content:space-between;gap:8px;'>", unsafe_allow_html=True)
st.markdown(f"<div style='display:flex;align-items:center;gap:8px;min-width:0;flex:1;'>"
f"{type_icon}<span class='content-title'>{row['title']}</span></div>", unsafe_allow_html=True)
st.markdown("<div style='display:flex;align-items:center;gap:4px;'>", unsafe_allow_html=True)
col1, col2, col3 = st.columns([1, 1, 1])
with col1:
if st.button("", key=f"generate_{item_key}", help="Generate with AI Blog Writer", use_container_width=True):
on_generate()
with col2:
if st.button("✏️", key=f"edit_{item_key}", help="Edit Content", use_container_width=True):
on_edit()
with col3:
if st.button("🗑️", key=f"delete_{item_key}", help="Delete Content", use_container_width=True):
on_delete()
st.markdown("</div>", unsafe_allow_html=True)
st.markdown("</div>", unsafe_allow_html=True)
st.markdown(f"<div class='content-meta'><span class='badge-content-calendar badge-platform-{platform_disp.lower()}'>{platform_icon} {platform_disp} &nbsp;|&nbsp; {type_disp} &nbsp;|&nbsp; <span class='chip-status chip-status-{status_disp.lower()}'>{status_disp}</span></span></div>", unsafe_allow_html=True)
st.markdown("</div>", unsafe_allow_html=True)

View File

@@ -0,0 +1,498 @@
import streamlit as st
from typing import Dict, Any, List
from datetime import datetime
import pandas as pd
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:
st.session_state.optimization_history = {}
if 'optimization_previews' not in st.session_state:
st.session_state.optimization_previews = {}
if 'optimization_metrics' not in st.session_state:
st.session_state.optimization_metrics = {}
def track_optimization(self, content_id: str, optimization_data: Dict[str, Any]) -> bool:
"""Track optimization changes for content with detailed metrics."""
try:
if content_id not in st.session_state.optimization_history:
st.session_state.optimization_history[content_id] = []
optimization_data['timestamp'] = datetime.now()
optimization_data['metrics'] = self._calculate_optimization_metrics(optimization_data)
st.session_state.optimization_history[content_id].append(optimization_data)
# Update metrics
if content_id not in st.session_state.optimization_metrics:
st.session_state.optimization_metrics[content_id] = []
st.session_state.optimization_metrics[content_id].append(optimization_data['metrics'])
return True
except Exception as e:
logger.error(f"Error tracking optimization: {str(e)}")
return False
def _calculate_optimization_metrics(self, optimization_data: Dict[str, Any]) -> Dict[str, Any]:
"""Calculate detailed optimization metrics."""
try:
metrics = {
'readability_score': 0,
'seo_score': 0,
'engagement_potential': 0,
'keyword_density': 0,
'content_quality': 0
}
# Calculate readability score
if 'content' in optimization_data:
content = optimization_data['content']
metrics['readability_score'] = self._calculate_readability(content)
# Calculate SEO score
if 'seo_data' in optimization_data:
seo_data = optimization_data['seo_data']
metrics['seo_score'] = self._calculate_seo_score(seo_data)
metrics['keyword_density'] = self._calculate_keyword_density(seo_data)
# Calculate engagement potential
if 'engagement_metrics' in optimization_data:
engagement = optimization_data['engagement_metrics']
metrics['engagement_potential'] = self._calculate_engagement_potential(engagement)
# Calculate overall content quality
metrics['content_quality'] = (
metrics['readability_score'] * 0.3 +
metrics['seo_score'] * 0.3 +
metrics['engagement_potential'] * 0.4
)
return metrics
except Exception as e:
logger.error(f"Error calculating optimization metrics: {str(e)}")
return {}
def _calculate_readability(self, content: str) -> float:
"""Calculate content readability score."""
try:
# Implement readability calculation logic
# This is a placeholder implementation
return 0.8
except Exception as e:
logger.error(f"Error calculating readability: {str(e)}")
return 0.0
def _calculate_seo_score(self, seo_data: SEOData) -> float:
"""Calculate SEO optimization score."""
try:
# Implement SEO score calculation logic
# This is a placeholder implementation
return 0.85
except Exception as e:
logger.error(f"Error calculating SEO score: {str(e)}")
return 0.0
def _calculate_keyword_density(self, seo_data: SEOData) -> float:
"""Calculate keyword density."""
try:
# Implement keyword density calculation logic
# This is a placeholder implementation
return 2.5
except Exception as e:
logger.error(f"Error calculating keyword density: {str(e)}")
return 0.0
def _calculate_engagement_potential(self, engagement: Dict[str, Any]) -> float:
"""Calculate content engagement potential."""
try:
# Implement engagement potential calculation logic
# This is a placeholder implementation
return 0.75
except Exception as e:
logger.error(f"Error calculating engagement potential: {str(e)}")
return 0.0
def get_optimization_history(self, content_id: str) -> List[Dict[str, Any]]:
"""Get detailed optimization history for content."""
return st.session_state.optimization_history.get(content_id, [])
def get_optimization_metrics(self, content_id: str) -> List[Dict[str, Any]]:
"""Get optimization metrics history."""
return st.session_state.optimization_metrics.get(content_id, [])
def save_preview(self, content_id: str, preview_data: Dict[str, Any]) -> bool:
"""Save optimization preview with versioning."""
try:
if content_id not in st.session_state.optimization_previews:
st.session_state.optimization_previews[content_id] = []
preview_data['version'] = len(st.session_state.optimization_previews[content_id]) + 1
preview_data['timestamp'] = datetime.now()
st.session_state.optimization_previews[content_id].append(preview_data)
return True
except Exception as e:
logger.error(f"Error saving preview: {str(e)}")
return False
def get_preview(self, content_id: str, version: int = None) -> Dict[str, Any]:
"""Get optimization preview with optional versioning."""
try:
previews = st.session_state.optimization_previews.get(content_id, [])
if not previews:
return {}
if version is None:
return previews[-1]
for preview in previews:
if preview['version'] == version:
return preview
return {}
except Exception as e:
logger.error(f"Error getting preview: {str(e)}")
return {}
def render_content_optimization(
content_generator: ContentGenerator,
ai_generator: AIGenerator,
seo_optimizer: SEOOptimizer
):
"""Render the content optimization interface with advanced features."""
st.title("Content Calendar")
# Initialize optimization manager
optimization_manager = OptimizationManager()
# 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
# Create main tabs
main_tabs = st.tabs(["Content Planning", "Content Optimization"])
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"
)
# 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"]
}
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)
# 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", "Educational", "Entertaining", "Persuasive"],
value="Professional"
)
length = st.radio(
"Content Length",
["Short", "Medium", "Long"],
horizontal=True
)
with col2:
engagement_goal = st.selectbox(
"Engagement Goal",
["Awareness", "Consideration", "Conversion", "Retention"]
)
creativity_level = st.slider(
"Creativity Level",
min_value=1,
max_value=10,
value=5
)
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
)
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.")
with opt_tabs[1]:
st.subheader("SEO Optimization")
# SEO optimization content here
with opt_tabs[2]:
st.subheader("Content Preview")
# Content preview here
with opt_tabs[3]:
st.subheader("Optimization History")
# Optimization history here
with opt_tabs[4]:
st.subheader("Performance Analytics")
# Analytics content here
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

@@ -0,0 +1,457 @@
import streamlit as st
from typing import Dict, Any, List
from datetime import datetime, timedelta
import pandas as pd
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')
class SeriesManager:
def __init__(self):
self.series_data = {}
if 'content_series' not in st.session_state:
st.session_state.content_series = {}
if 'series_relationships' not in st.session_state:
st.session_state.series_relationships = {}
if 'series_performance' not in st.session_state:
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', series_type: str = '', series_flow: str = '', metadata: Dict[str, Any] = {}) -> Dict[str, Any]:
"""Create a new content series with tracking and scheduling."""
try:
series = {
'id': series_id,
'topic': topic,
'num_pieces': num_pieces,
'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},
'metadata': metadata
}
st.session_state.content_series[series_id] = series
return series
except Exception as e:
logger.error(f"Error creating series: {str(e)}")
return None
def add_piece(self, series_id: str, piece: Dict[str, Any]) -> bool:
"""Add a content piece to the series with relationship tracking."""
try:
if series_id in st.session_state.content_series:
series = st.session_state.content_series[series_id]
piece_id = f"piece_{len(series['pieces'])}"
# 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]
structured_piece['relationships']['previous'] = previous_piece['id']
structured_piece['relationships']['next'] = piece_id
# Add to platform distribution
platform_name = structured_piece['platform'].name
if platform_name in series['platform_distribution']:
series['platform_distribution'][platform_name].append(piece_id)
series['pieces'].append(structured_piece)
return True
return False
except Exception as e:
logger.error(f"Error adding piece to series: {str(e)}")
return False
def get_series_performance(self, series_id: str) -> Dict[str, Any]:
"""Get comprehensive performance analytics for a series."""
try:
if series_id in st.session_state.content_series:
series = st.session_state.content_series[series_id]
performance = {
'overall': {
'total_engagement': 0,
'total_reach': 0,
'conversion_rate': 0,
'average_engagement': 0
},
'platforms': {},
'pieces': {},
'trends': {
'engagement': [],
'reach': [],
'conversions': []
}
}
# Calculate overall metrics
for piece in series['pieces']:
piece_performance = piece.get('performance', {})
performance['overall']['total_engagement'] += piece_performance.get('engagement', 0)
performance['overall']['total_reach'] += piece_performance.get('reach', 0)
performance['overall']['conversion_rate'] += piece_performance.get('conversion_rate', 0)
# Track piece-specific performance
performance['pieces'][piece['id']] = piece_performance
# Track trends
performance['trends']['engagement'].append(piece_performance.get('engagement', 0))
performance['trends']['reach'].append(piece_performance.get('reach', 0))
performance['trends']['conversions'].append(piece_performance.get('conversion_rate', 0))
# Calculate averages
num_pieces = len(series['pieces'])
if num_pieces > 0:
performance['overall']['average_engagement'] = performance['overall']['total_engagement'] / num_pieces
performance['overall']['conversion_rate'] = performance['overall']['conversion_rate'] / num_pieces
# Calculate platform-specific performance
for platform in series['platforms']:
platform_pieces = series['platform_distribution'].get(platform.name, [])
platform_performance = {
'engagement': 0,
'reach': 0,
'conversion_rate': 0
}
for piece_id in platform_pieces:
piece_performance = performance['pieces'].get(piece_id, {})
platform_performance['engagement'] += piece_performance.get('engagement', 0)
platform_performance['reach'] += piece_performance.get('reach', 0)
platform_performance['conversion_rate'] += piece_performance.get('conversion_rate', 0)
if platform_pieces:
platform_performance['engagement'] /= len(platform_pieces)
platform_performance['conversion_rate'] /= len(platform_pieces)
performance['platforms'][platform.name] = platform_performance
return performance
return {}
except Exception as e:
logger.error(f"Error getting series performance: {str(e)}")
return {}
def update_series_status(self, series_id: str, status: str) -> bool:
"""Update the status of a series."""
try:
if series_id in st.session_state.content_series:
st.session_state.content_series[series_id]['status'] = status
return True
return False
except Exception as e:
logger.error(f"Error updating series status: {str(e)}")
return False
def schedule_series(self, series_id: str, start_date: datetime, interval: int = 7) -> bool:
"""Schedule the series content with flexible scheduling strategies."""
try:
if series_id in st.session_state.content_series:
series = st.session_state.content_series[series_id]
current_date = start_date
for piece in series['pieces']:
piece['scheduled_date'] = current_date
if series['schedule_strategy'] == 'linear':
current_date += timedelta(days=interval)
elif series['schedule_strategy'] == 'burst':
current_date += timedelta(days=1)
elif series['schedule_strategy'] == 'custom':
# Custom scheduling is handled by the UI
pass
return True
return False
except Exception as e:
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."""
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()
# Series Creation Form
with st.form("series_creation_form"):
st.subheader("Create New Series")
series_topic = st.text_input("Series Topic")
num_pieces = st.slider("Number of pieces", 2, 10, 3)
content_type = st.selectbox(
"Content Type",
options=[ct.name for ct in ContentType],
key="series_content_type"
)
# Multi-platform selection
platforms = st.multiselect(
"Target Platforms",
options=[p.name for p in Platform],
default=['WEBSITE'],
key="series_platforms"
)
# Schedule strategy
schedule_strategy = st.selectbox(
"Schedule Strategy",
options=['linear', 'burst', 'custom'],
help="Linear: Evenly spaced, Burst: Grouped together, Custom: Manual scheduling"
)
# Series metadata
with st.expander("Series Metadata"):
target_audience = st.text_area("Target Audience")
series_goals = st.multiselect(
"Series Goals",
options=['Awareness', 'Engagement', 'Conversion', 'Education'],
default=['Awareness']
)
series_tone = st.select_slider(
"Series Tone",
options=['Professional', 'Casual', 'Friendly', 'Authoritative', 'Conversational'],
value='Professional'
)
submitted = st.form_submit_button("Generate Series")
if submitted and series_topic:
with st.spinner("Generating content series..."):
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,
series_type=series_goals[0] if series_goals else 'Awareness',
series_flow='sequential', # Default flow
metadata=metadata
)
if series:
# Generate series content
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
)
# 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)
)
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} 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)
st.error(f"Error generating series: {str(e)}")
# Display existing series
if st.session_state.content_series:
st.subheader("Existing Series")
for series_id, series in st.session_state.content_series.items():
with st.expander(f"Series: {series['topic']}"):
st.write(f"Status: {series['status']}")
st.write(f"Pieces: {len(series['pieces'])}")
st.write(f"Created: {series['created_at']}")
# Series actions
if st.button(f"View Details", key=f"view_{series_id}"):
st.session_state.selected_series = series_id
if st.button(f"Delete Series", key=f"delete_{series_id}"):
del st.session_state.content_series[series_id]
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

@@ -0,0 +1,81 @@
import streamlit as st
from typing import Dict, Any
from lib.database.models import ContentItem
import logging
logger = logging.getLogger(__name__)
def render_performance_insights(content_item: ContentItem, platform_adapter) -> None:
"""Render performance insights for a content item."""
try:
logger.info(f"Rendering performance insights for: {content_item.title}")
# Get performance data from platform adapter
performance_data = platform_adapter.get_content_performance(content_item)
if not performance_data:
st.warning("No performance data available for this content")
return
# Create metrics section
st.subheader("Performance Metrics")
col1, col2, col3 = st.columns(3)
with col1:
st.metric(
"Engagement Rate",
f"{performance_data.get('engagement_rate', 0):.1f}%",
f"{performance_data.get('engagement_rate_change', 0):+.1f}%"
)
with col2:
st.metric(
"Reach",
f"{performance_data.get('reach', 0):,}",
f"{performance_data.get('reach_change', 0):+,}"
)
with col3:
st.metric(
"Conversion Rate",
f"{performance_data.get('conversion_rate', 0):.1f}%",
f"{performance_data.get('conversion_rate_change', 0):+.1f}%"
)
# Create audience insights section
st.subheader("Audience Insights")
audience_data = performance_data.get('audience_insights', {})
if audience_data:
col1, col2 = st.columns(2)
with col1:
st.write("Demographics")
st.write(f"- Age: {audience_data.get('age_range', 'N/A')}")
st.write(f"- Gender: {audience_data.get('gender', 'N/A')}")
st.write(f"- Location: {audience_data.get('location', 'N/A')}")
with col2:
st.write("Behavior")
st.write(f"- Peak Time: {audience_data.get('peak_time', 'N/A')}")
st.write(f"- Device: {audience_data.get('device', 'N/A')}")
st.write(f"- Platform: {audience_data.get('platform', 'N/A')}")
# Create content insights section
st.subheader("Content Insights")
content_insights = performance_data.get('content_insights', {})
if content_insights:
st.write("Top Performing Elements")
for element, score in content_insights.get('top_elements', {}).items():
st.write(f"- {element}: {score}")
st.write("Improvement Suggestions")
for suggestion in content_insights.get('suggestions', []):
st.write(f"- {suggestion}")
logger.info(f"Performance insights rendered successfully for: {content_item.title}")
except Exception as e:
logger.error(f"Error rendering performance insights: {str(e)}", exc_info=True)
st.error(f"Error rendering performance insights: {str(e)}")