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

@@ -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__)