Content Calendar, Content Gap Analysis, and Content Optimization

This commit is contained in:
ajaysi
2025-05-27 09:15:08 +05:30
parent 4049d19787
commit 889021c078
100 changed files with 18504 additions and 1251 deletions

View File

@@ -0,0 +1,21 @@
import streamlit as st
def render_add_content_modal(selected_date, on_add_content, on_generate_with_ai):
if st.button("+ Add Content", key="open_add_content_dialog_bottom"):
st.session_state['show_add_content_dialog'] = True
if st.session_state.get('show_add_content_dialog', False):
st.markdown("### Add Content")
with st.form("quick_add_form_dialog_bottom"):
title = st.text_input("Title")
platform = st.selectbox("Platform", ["Blog", "Instagram", "Twitter", "LinkedIn", "Facebook"])
content_type = st.selectbox("Content Type", ["Article", "Social Post", "Video", "Newsletter"])
publish_date = st.date_input("Publish Date", selected_date)
col_add, col_ai = st.columns([0.6, 0.4])
with col_add:
if st.form_submit_button("Add Content"):
on_add_content(title, platform, content_type, publish_date)
with col_ai:
if st.form_submit_button("Generate with AI"):
on_generate_with_ai(title, platform, content_type)
if st.button("Close", key="close_add_content_dialog_bottom"):
st.session_state['show_add_content_dialog'] = False

View File

@@ -0,0 +1,137 @@
import streamlit as st
def render_ai_suggestions_modal(generate_ai_suggestions, on_create_brief, on_schedule, on_refine, on_customize):
st.subheader("AI Content Suggestions")
default_type = st.session_state.get('ai_modal_type', "Blog Post")
default_topic = st.session_state.get('ai_modal_topic', "")
default_platform = st.session_state.get('ai_modal_platform', "Blog")
content_types = {
"Blog Post": "Long-form content for in-depth topics",
"Social Media Post": "Short, engaging content for social platforms",
"Video": "Visual content with script and storyboard",
"Newsletter": "Email content for subscriber engagement"
}
content_type = st.selectbox(
"Content Type",
list(content_types.keys()),
format_func=lambda x: f"{x} - {content_types[x]}",
key="modal_suggestion_type",
index=list(content_types.keys()).index(default_type) if default_type in content_types else 0
)
topic = st.text_input("Enter topic or keyword", value=default_topic, key="modal_suggestion_topic")
with st.expander("Advanced Options"):
audience = st.multiselect(
"Target Audience",
["Professionals", "Students", "Entrepreneurs", "General Public", "Industry Experts"],
default=["Professionals"]
)
goals = st.multiselect(
"Content Goals",
["Increase Engagement", "Generate Leads", "Build Authority", "Drive Traffic", "Educate"],
default=["Increase Engagement"]
)
tone = st.select_slider(
"Content Tone",
options=["Professional", "Casual", "Educational", "Entertaining", "Persuasive"],
value="Professional"
)
length = st.radio(
"Content Length",
["Short", "Medium", "Long"],
horizontal=True
)
st.subheader("AI Model Settings")
model_settings = {
"Creativity Level": st.slider("Creativity Level", 0.0, 1.0, 0.7, 0.1),
"Formality Level": st.slider("Formality Level", 0.0, 1.0, 0.5, 0.1),
"Technical Depth": st.slider("Technical Depth", 0.0, 1.0, 0.5, 0.1)
}
st.subheader("Content Style Preferences")
style_preferences = {
"Use Examples": st.checkbox("Include Real-world Examples", True),
"Use Statistics": st.checkbox("Include Statistics and Data", True),
"Use Quotes": st.checkbox("Include Expert Quotes", False),
"Use Case Studies": st.checkbox("Include Case Studies", False)
}
st.subheader("SEO Preferences")
seo_preferences = {
"Keyword Density": st.slider("Keyword Density (%)", 1, 5, 2),
"Internal Linking": st.checkbox("Suggest Internal Links", True),
"External Linking": st.checkbox("Suggest External Links", True),
"Meta Description": st.checkbox("Generate Meta Description", True)
}
st.subheader("Platform-specific Settings")
platform_settings = {
"Hashtag Usage": st.checkbox("Suggest Hashtags", True),
"Image Suggestions": st.checkbox("Suggest Images", True),
"Video Suggestions": st.checkbox("Suggest Videos", False),
"Interactive Elements": st.checkbox("Suggest Interactive Elements", False)
}
if st.button("Generate Suggestions", type="primary", key="modal_generate_btn"):
with st.spinner("Generating suggestions..."):
suggestions = generate_ai_suggestions(
content_type,
topic,
audience,
goals,
tone,
length,
model_settings,
style_preferences,
seo_preferences,
platform_settings
)
if suggestions:
suggestion_tabs = st.tabs([f"Suggestion {i+1}" for i in range(len(suggestions))])
for i, (tab, suggestion) in enumerate(zip(suggestion_tabs, suggestions)):
with tab:
col1, col2 = st.columns([2, 1])
with col1:
st.subheader(suggestion['title'])
st.write(f"**Type:** {suggestion['type']}")
st.write(f"**Platform:** {suggestion['platform']}")
st.write(f"**Target Audience:** {', '.join(suggestion['audience'])}")
st.write(f"**Estimated Impact:** {suggestion['impact']}")
with st.expander("Content Preview"):
st.write(suggestion.get('preview', 'Preview not available'))
if suggestion.get('style_elements'):
st.write("**Style Elements:**")
for element in suggestion['style_elements']:
st.write(f"- {element}")
if suggestion.get('seo_elements'):
st.write("**SEO Elements:**")
for element in suggestion['seo_elements']:
st.write(f"- {element}")
with col2:
st.subheader("Performance Metrics")
metrics = {
"Engagement Score": suggestion.get('engagement_score', '85%'),
"Reach Potential": suggestion.get('reach', 'High'),
"Conversion Rate": suggestion.get('conversion', '3.5%'),
"SEO Impact": suggestion.get('seo_impact', 'Strong')
}
for metric, value in metrics.items():
st.metric(metric, value)
st.subheader("Actions")
if st.button("Create Brief", key=f"modal_brief_{i}"):
on_create_brief(suggestion)
if st.button("Schedule", key=f"modal_schedule_{i}"):
on_schedule(suggestion)
if st.button("Refine", key=f"modal_refine_{i}"):
on_refine(suggestion)
if st.button("Customize", key=f"modal_customize_{i}"):
on_customize(suggestion)
with st.expander("Additional Options"):
st.write("**Platform Optimizations**")
for platform in suggestion.get('platform_optimizations', []):
st.write(f"- {platform}")
st.write("**Content Variations**")
for variation in suggestion.get('variations', []):
st.write(f"- {variation}")
st.write("**SEO Recommendations**")
for seo in suggestion.get('seo_recommendations', []):
st.write(f"- {seo}")
if suggestion.get('media_suggestions'):
st.write("**Media Suggestions**")
for media in suggestion['media_suggestions']:
st.write(f"- {media}")

View File

@@ -0,0 +1,51 @@
import streamlit as st
from .components.content_card import render_content_card
from .components.badge import render_badge
def render_calendar_view(calendar_data, icon_map, status_color, on_edit, on_delete, on_generate, get_item_key):
if calendar_data is not None and not calendar_data.empty:
st.markdown("### All Scheduled Content")
calendar_data = calendar_data.sort_values(by="date")
grouped = list(calendar_data.groupby(calendar_data['date'].dt.date))
for i, (date, group) in enumerate(grouped):
exp_open = (i == 0)
with st.expander(f"{date.strftime('%B %d, %Y')}", expanded=exp_open):
for idx, row in group.iterrows():
item_key = get_item_key(row)
is_editing = st.session_state.get("editing_item_key") == item_key
platform = str(row['platform'])
if hasattr(platform, 'value'):
platform = platform.value
platform_map = {
'blog': 'Blog',
'website': 'Blog',
'instagram': 'Instagram',
'twitter': 'Twitter',
'linkedin': 'LinkedIn',
'facebook': 'Facebook',
}
platform_disp = platform_map.get(platform.lower(), 'Blog')
type_disp = str(row['type'])
if hasattr(type_disp, 'value'):
type_disp = type_disp.value
type_disp = type_disp.replace('_', ' ').title()
status_disp = row['status'].capitalize()
platform_icon = icon_map.get(platform_disp, '🌐')
type_icon = icon_map.get(type_disp, '📄')
render_content_card(
row=row,
is_editing=is_editing,
on_edit=lambda r=row: on_edit(r),
on_delete=lambda r=row: on_delete(r),
on_generate=lambda r=row: on_generate(r),
icon_map=icon_map,
status_color=status_color,
platform_disp=platform_disp,
type_disp=type_disp,
status_disp=status_disp,
platform_icon=platform_icon,
type_icon=type_icon,
item_key=item_key
)
else:
st.info("No content scheduled yet. Add content to see it here.")

View File

@@ -0,0 +1,231 @@
import streamlit as st
from typing import Dict, Any, List
from lib.ai_seo_tools.content_calendar.models.calendar import ContentItem
import logging
logger = logging.getLogger(__name__)
def render_ab_testing(
content_generator,
calendar_manager
) -> None:
"""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)
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,467 @@
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
import logging
logger = logging.getLogger('content_calendar.optimization')
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.header("Content Optimization")
# 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
# 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
)
# 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")
# Advanced Optimization Settings
with st.expander("Advanced Settings", expanded=True):
col1, col2 = st.columns(2)
with col1:
tone = st.select_slider(
"Content Tone",
options=['Professional', 'Casual', 'Friendly', 'Authoritative', 'Conversational'],
value='Professional'
)
length = st.select_slider(
"Content Length",
options=['Short', 'Medium', 'Long', 'Comprehensive'],
value='Medium'
)
with col2:
engagement_goal = st.select_slider(
"Engagement Goal",
options=['Awareness', 'Consideration', 'Conversion', 'Retention'],
value='Consideration'
)
creativity_level = st.slider(
"Creativity Level",
min_value=1,
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', {})
}
)
# 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")
# 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"
)
# 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")
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")
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)
# 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.")

View File

@@ -0,0 +1,392 @@
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
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') -> 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,
'pieces': [],
'performance': {},
'created_at': datetime.now(),
'status': 'draft',
'relationships': {},
'platform_distribution': {p.name: [] for p in platforms}
}
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'])}"
piece['id'] = piece_id
# Track relationships
if series['pieces']:
previous_piece = series['pieces'][-1]
piece['relationships'] = {
'previous': previous_piece['id'],
'next': None
}
previous_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)
series['pieces'].append(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 with enhanced features."""
st.header("Content Series Generator")
# 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')}"
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
)
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
}
)
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'
)
})
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 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']
}))
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.experimental_rerun()

View File

@@ -0,0 +1,81 @@
import streamlit as st
from typing import Dict, Any
from lib.ai_seo_tools.content_calendar.models.calendar 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)}")

View File

@@ -0,0 +1,634 @@
import streamlit as st
import pandas as pd
from datetime import datetime
import logging
import sys
import hashlib
from .calendar_view import render_calendar_view
from .filters import render_filters
from .add_content_modal import render_add_content_modal
from .ai_suggestions_modal import render_ai_suggestions_modal
from .components.performance_insights import render_performance_insights
from .components.content_series import render_content_series_generator
from .components.ab_testing import render_ab_testing
from .components.content_optimization import render_content_optimization
from ..core.calendar_manager import CalendarManager
from ..core.content_brief import ContentBriefGenerator
from ..core.content_generator import ContentGenerator
from ..core.ai_generator import AIGenerator
from ..integrations.platform_adapters import UnifiedPlatformAdapter
from ..integrations.seo_optimizer import SEOOptimizer
from lib.ai_seo_tools.content_calendar.models.calendar import ContentItem, Platform, ContentType, SEOData, Calendar
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
from typing import Dict, Any, List, Tuple
import json
class ContentCalendarDashboard:
"""Interactive dashboard for content calendar management."""
def __init__(self):
self.logger = logging.getLogger('content_calendar.dashboard')
self.logger.info("Initializing ContentCalendarDashboard")
# Initialize calendar manager and store in session state
if 'calendar_manager' not in st.session_state:
st.session_state.calendar_manager = CalendarManager()
st.session_state.calendar_manager.load_calendar_from_json()
self.calendar_manager = st.session_state.calendar_manager
self.content_brief_generator = ContentBriefGenerator()
self.content_generator = ContentGenerator()
self.ai_generator = AIGenerator()
self.platform_adapter = UnifiedPlatformAdapter()
self.seo_optimizer = SEOOptimizer()
# Initialize A/B testing state
if 'ab_test_results' not in st.session_state:
st.session_state.ab_test_results = {}
# Initialize content optimization state
if 'optimization_history' not in st.session_state:
st.session_state.optimization_history = {}
# Ensure a calendar exists
if not self.calendar_manager.get_calendar():
self.calendar_manager._calendar = Calendar(
start_date=datetime.now(),
duration='monthly',
platforms=[Platform.WEBSITE, Platform.INSTAGRAM, Platform.TWITTER, Platform.LINKEDIN, Platform.FACEBOOK],
schedule={}
)
# Initialize session state
if 'calendar_data' not in st.session_state:
st.session_state.calendar_data = None
if 'selected_content' not in st.session_state:
st.session_state.selected_content = None
if 'view_mode' not in st.session_state:
st.session_state.view_mode = 'day'
if 'selected_date' not in st.session_state:
st.session_state.selected_date = datetime.now()
self.logger.info("ContentCalendarDashboard initialized successfully")
def render(self):
self.logger.info("Starting dashboard render (tabbed UI)")
try:
self._inject_custom_css()
st.title("AI Content Planning")
st.markdown("""
Plan, schedule, and manage your content strategy with AI-powered insights. Use the calendar to organize your content and leverage AI tools for optimization.
""")
tabs = st.tabs(["Content Planning", "Content Optimization", "A/B Testing", "Content Series", "Analytics"])
with tabs[0]:
icon_map = {
'Blog': '📝', 'Website': '🌐', 'Instagram': '📸', 'Twitter': '🐦', 'LinkedIn': '💼', 'Facebook': '📘',
'Article': '📄', 'Social Post': '💬', 'Video': '🎬', 'Newsletter': '✉️'
}
status_color = {
'Draft': '#bdbdbd', 'Scheduled': '#1976d2', 'Published': '#43a047', 'Archived': '#757575'
}
calendar_data = self._get_calendar_data()
def on_edit(row):
st.session_state["editing_item_key"] = self._get_item_key(row)
st.experimental_rerun()
def on_delete(row):
self._delete_content(row)
st.experimental_rerun()
def on_generate(row):
st.session_state['show_ai_modal'] = True
st.session_state['ai_modal_topic'] = row['title']
st.session_state['ai_modal_type'] = str(row['type'])
st.session_state['ai_modal_platform'] = str(row['platform'])
st.experimental_rerun()
render_calendar_view(
calendar_data=calendar_data,
icon_map=icon_map,
status_color=status_color,
on_edit=on_edit,
on_delete=on_delete,
on_generate=on_generate,
get_item_key=self._get_item_key
)
st.markdown("---")
render_filters()
def handle_add_content(title, platform, content_type, publish_date):
self._add_content({
'title': title,
'platform': platform,
'type': content_type,
'publish_date': publish_date
})
st.session_state['show_add_content_dialog'] = False
st.success("Content added!")
st.experimental_rerun()
def handle_generate_with_ai(title, platform, content_type):
st.session_state['show_add_content_dialog'] = False
st.session_state['show_ai_modal'] = True
st.session_state['ai_modal_topic'] = title
st.session_state['ai_modal_type'] = content_type
st.session_state['ai_modal_platform'] = platform
render_add_content_modal(
selected_date=st.session_state.selected_date,
on_add_content=handle_add_content,
on_generate_with_ai=handle_generate_with_ai
)
if st.session_state.get('show_ai_modal', False):
st.markdown("### AI Content Suggestions")
with st.container():
render_ai_suggestions_modal(
generate_ai_suggestions=self._generate_ai_suggestions,
on_create_brief=self._create_content_brief,
on_schedule=self._schedule_content,
on_refine=self._refine_suggestion,
on_customize=self._customize_suggestion
)
if st.button("Close"):
st.session_state['show_ai_modal'] = False
with tabs[1]:
render_content_optimization(
content_generator=self.content_generator,
ai_generator=self.ai_generator,
seo_optimizer=self.seo_optimizer
)
with tabs[2]:
render_ab_testing(self.content_generator, self.calendar_manager)
with tabs[3]:
render_content_series_generator(
self.ai_generator,
self.content_generator,
self.seo_optimizer
)
with tabs[4]:
st.header("Analytics")
st.markdown("### Performance Insights")
selected_content = st.selectbox(
"Select content to analyze",
options=[item.title for item in self.calendar_manager.get_calendar().get_all_content()],
key="analytics_content_select"
)
if selected_content:
content_item = next(
item for item in self.calendar_manager.get_calendar().get_all_content()
if item.title == selected_content
)
render_performance_insights(content_item, self.platform_adapter)
st.markdown("### Optimization History")
if selected_content in st.session_state.optimization_history:
st.json(st.session_state.optimization_history[selected_content])
self.logger.info("Dashboard render completed successfully (tabbed UI)")
except Exception as e:
self.logger.error(f"Error rendering dashboard: {str(e)}", exc_info=True)
st.error(f"An error occurred: {str(e)}")
def _inject_custom_css(self):
st.markdown("""
<style>
/* Add your custom CSS here if needed */
</style>
""", unsafe_allow_html=True)
def _get_calendar_data(self):
self.logger.info("_get_calendar_data called")
try:
calendar_obj = self.calendar_manager.get_calendar()
if not calendar_obj:
self.logger.info("No calendar found in manager")
return None
data = []
for date_str, items in calendar_obj.schedule.items():
for item in items:
data.append({
'date': pd.to_datetime(date_str),
'title': item.title,
'platform': item.platforms[0] if item.platforms else 'Unknown',
'type': item.content_type,
'status': item.status
})
df = pd.DataFrame(data) if data else None
return df
except Exception as e:
self.logger.error(f"Error loading calendar data: {str(e)}", exc_info=True)
st.error(f"Error loading calendar data: {str(e)}")
return None
def _add_content(self, content):
calendar = self.calendar_manager.get_calendar()
if not calendar:
st.error("No calendar found. Please create a calendar first.")
return
platform_map = {
'Blog': Platform.WEBSITE,
'Instagram': Platform.INSTAGRAM,
'Twitter': Platform.TWITTER,
'LinkedIn': Platform.LINKEDIN,
'Facebook': Platform.FACEBOOK,
}
platform_enum = platform_map.get(content['platform'], Platform.WEBSITE)
content_type_map = {
'Article': ContentType.BLOG_POST,
'Social Post': ContentType.SOCIAL_MEDIA,
'Video': ContentType.VIDEO,
'Newsletter': ContentType.NEWSLETTER,
}
content_type_enum = content_type_map.get(content['type'], ContentType.BLOG_POST)
seo_data = SEOData(
title=content['title'],
meta_description="",
keywords=[],
structured_data={},
)
new_item = ContentItem(
title=content['title'],
description="",
content_type=content_type_enum,
platforms=[platform_enum],
publish_date=pd.to_datetime(content['publish_date']),
seo_data=seo_data,
status=content.get('status', 'Draft')
)
calendar.add_content(new_item)
self.calendar_manager.save_calendar_to_json()
def _delete_content(self, row):
calendar = self.calendar_manager.get_calendar()
if not calendar:
return
for date_str, items in list(calendar.schedule.items()):
calendar.schedule[date_str] = [
item for item in items
if not (
item.title == row['title'] and
str(item.publish_date.date()) == str(row['date'].date()) and
item.platforms[0].name == str(row['platform']) and
item.content_type.name == str(row['type'])
)
]
if not calendar.schedule[date_str]:
del calendar.schedule[date_str]
self.calendar_manager.save_calendar_to_json()
def _edit_content(self, row, new_title, new_platform, new_type, new_status):
self._delete_content(row)
self._add_content({
'title': new_title,
'platform': new_platform,
'type': new_type,
'publish_date': row['date'],
'status': new_status
})
def _get_item_key(self, row):
key_str = f"{row['title']}_{row['date']}_{row['platform']}_{row['type']}"
return hashlib.md5(key_str.encode()).hexdigest()
def _generate_ai_suggestions(self, content_type, topic, audience, goals, tone, length, model_settings, style_preferences, seo_preferences, platform_settings):
"""Generate AI content suggestions based on input parameters."""
try:
self.logger.info(f"Generating AI suggestions for topic: {topic}")
# Map content type string to ContentType enum
content_type_map = {
'Blog Post': ContentType.BLOG_POST,
'Social Media Post': ContentType.SOCIAL_MEDIA,
'Video': ContentType.VIDEO,
'Newsletter': ContentType.NEWSLETTER,
'Article': ContentType.BLOG_POST,
'Social Post': ContentType.SOCIAL_MEDIA
}
content_type_enum = content_type_map.get(content_type, ContentType.BLOG_POST)
# Map platform string to Platform enum
platform_map = {
'Blog': Platform.WEBSITE,
'Instagram': Platform.INSTAGRAM,
'Twitter': Platform.TWITTER,
'LinkedIn': Platform.LINKEDIN,
'Facebook': Platform.FACEBOOK,
'Website': Platform.WEBSITE
}
platform = st.session_state.get('ai_modal_platform', 'Blog')
platform_enum = platform_map.get(platform, Platform.WEBSITE)
# Create a content item for the suggestion
content_item = ContentItem(
title=topic,
description="",
content_type=content_type_enum,
platforms=[platform_enum],
publish_date=datetime.now(),
seo_data=SEOData(
title=topic,
meta_description="",
keywords=[],
structured_data={}
),
status='Draft'
)
# Use AIGenerator to generate suggestions
suggestions = self.ai_generator.generate_ai_suggestions(
content_type=content_type_enum,
topic=topic,
audience=audience,
goals=goals,
tone=tone,
length=length,
model_settings=model_settings,
style_preferences=style_preferences,
seo_preferences=seo_preferences,
platform_settings=platform_settings,
platform=platform_enum
)
if not suggestions:
self.logger.warning("No suggestions generated")
return []
# Format suggestions
formatted_suggestions = []
for suggestion in suggestions:
formatted_suggestion = {
'title': suggestion.get('title', topic),
'type': content_type,
'platform': platform,
'audience': audience,
'impact': f"High impact for {', '.join(goals)}",
'preview': suggestion.get('preview', ''),
'style_elements': [
f"Tone: {tone}",
f"Length: {length}",
f"Creativity: {model_settings.get('Creativity Level', 'balanced')}",
f"Formality: {model_settings.get('Formality Level', 'professional')}"
],
'seo_elements': [
f"Keyword Density: {seo_preferences.get('Keyword Density', '2')}%",
"Internal Linking: Enabled" if seo_preferences.get('Internal Linking', True) else "Internal Linking: Disabled",
"External Linking: Enabled" if seo_preferences.get('External Linking', True) else "External Linking: Disabled"
],
'engagement_score': f"{85 + len(formatted_suggestions)*5}%",
'reach': 'High',
'conversion': f"{3.5 + len(formatted_suggestions)*0.5}%",
'seo_impact': 'Strong',
'platform_optimizations': suggestion.get('platform_optimizations', []),
'variations': suggestion.get('variations', [
"Alternative headline",
"Different content angle",
"Alternative format"
]),
'seo_recommendations': suggestion.get('seo_elements', []),
'media_suggestions': suggestion.get('media_suggestions', [
"Featured image",
"Supporting graphics",
"Social media visuals"
])
}
formatted_suggestions.append(formatted_suggestion)
self.logger.info(f"Generated {len(formatted_suggestions)} suggestions successfully")
return formatted_suggestions
except Exception as e:
self.logger.error(f"Error generating AI suggestions: {str(e)}", exc_info=True)
st.error(f"Error generating suggestions: {str(e)}")
return []
def _create_content_brief(self, content_item: ContentItem) -> Dict[str, Any]:
"""Create a detailed content brief for the given content item."""
try:
self.logger.info(f"Creating content brief for: {content_item.title}")
# Generate content brief using the content brief generator
brief = self.content_brief_generator.generate_brief(
content_item=content_item,
target_audience={
'audience': content_item.description,
'goals': ['engage', 'inform', 'convert']
}
)
# Enhance brief with SEO data
if brief and 'content_flow' in brief:
brief['seo_optimization'] = {
'meta_description': self.seo_optimizer.generate_meta_description(
brief['content_flow'].get('introduction', {}).get('summary', '')
),
'keywords': self.seo_optimizer.extract_keywords(
brief['content_flow'].get('introduction', {}).get('summary', '')
),
'structured_data': self.seo_optimizer.generate_structured_data(
content_item.content_type
)
}
self.logger.info(f"Content brief created successfully for: {content_item.title}")
return brief
except Exception as e:
self.logger.error(f"Error creating content brief: {str(e)}", exc_info=True)
st.error(f"Error creating content brief: {str(e)}")
return {}
def _schedule_content(self, content_item: ContentItem, publish_date: datetime) -> bool:
"""Schedule content for publishing on the specified date."""
try:
self.logger.info(f"Scheduling content: {content_item.title} for {publish_date}")
# Get the calendar
calendar = self.calendar_manager.get_calendar()
if not calendar:
raise ValueError("No calendar found")
# Update the publish date
content_item.publish_date = publish_date
# Add to calendar
calendar.add_content(content_item)
# Save changes
self.calendar_manager.save_calendar_to_json()
self.logger.info(f"Content scheduled successfully: {content_item.title}")
return True
except Exception as e:
self.logger.error(f"Error scheduling content: {str(e)}", exc_info=True)
st.error(f"Error scheduling content: {str(e)}")
return False
def _refine_suggestion(self, suggestion: Dict[str, Any], feedback: Dict[str, Any]) -> Dict[str, Any]:
"""Refine an AI-generated suggestion based on user feedback."""
try:
self.logger.info("Refining AI suggestion based on feedback")
# Update suggestion based on feedback
if 'tone' in feedback:
suggestion['style_elements'] = [
f"Tone: {feedback['tone']}",
*[elem for elem in suggestion['style_elements'] if not elem.startswith('Tone:')]
]
if 'length' in feedback:
suggestion['style_elements'] = [
f"Length: {feedback['length']}",
*[elem for elem in suggestion['style_elements'] if not elem.startswith('Length:')]
]
if 'keywords' in feedback:
suggestion['seo_elements'] = [
f"Keywords: {', '.join(feedback['keywords'])}",
*[elem for elem in suggestion['seo_elements'] if not elem.startswith('Keywords:')]
]
# Regenerate content with refined parameters
refined_content = self.content_brief_generator.generate_brief(
content_item=ContentItem(
title=suggestion['title'],
description="",
content_type=ContentType[suggestion['type'].upper().replace(' ', '_')],
platforms=[Platform[suggestion['platform'].upper()]],
publish_date=datetime.now(),
seo_data=SEOData(
title=suggestion['title'],
meta_description="",
keywords=feedback.get('keywords', []),
structured_data={}
),
status='Draft'
),
target_audience={
'audience': suggestion['audience'],
'goals': feedback.get('goals', ['engage', 'inform']),
'preferences': {
'tone': feedback.get('tone', 'professional'),
'length': feedback.get('length', 'medium')
}
}
)
if refined_content:
suggestion['preview'] = refined_content.get('content_flow', {}).get('introduction', {}).get('summary', '')
self.logger.info("Suggestion refined successfully")
return suggestion
except Exception as e:
self.logger.error(f"Error refining suggestion: {str(e)}", exc_info=True)
st.error(f"Error refining suggestion: {str(e)}")
return suggestion
def _customize_suggestion(self, suggestion: Dict[str, Any], customizations: Dict[str, Any]) -> Dict[str, Any]:
"""Customize an AI-generated suggestion with specific requirements."""
try:
self.logger.info("Customizing AI suggestion")
# Apply customizations
if 'title' in customizations:
suggestion['title'] = customizations['title']
if 'platform' in customizations:
suggestion['platform'] = customizations['platform']
if 'style' in customizations:
suggestion['style_elements'] = [
f"Tone: {customizations['style'].get('tone', 'professional')}",
f"Length: {customizations['style'].get('length', 'medium')}",
f"Creativity: {customizations['style'].get('creativity', 'balanced')}",
f"Formality: {customizations['style'].get('formality', 'professional')}"
]
if 'seo' in customizations:
suggestion['seo_elements'] = [
f"Keyword Density: {customizations['seo'].get('keyword_density', '2')}%",
"Internal Linking: Enabled" if customizations['seo'].get('internal_linking', True) else "Internal Linking: Disabled",
"External Linking: Enabled" if customizations['seo'].get('external_linking', True) else "External Linking: Disabled"
]
# Regenerate content with customizations
customized_content = self.content_brief_generator.generate_brief(
content_item=ContentItem(
title=suggestion['title'],
description="",
content_type=ContentType[suggestion['type'].upper().replace(' ', '_')],
platforms=[Platform[suggestion['platform'].upper()]],
publish_date=datetime.now(),
seo_data=SEOData(
title=suggestion['title'],
meta_description="",
keywords=customizations.get('seo', {}).get('keywords', []),
structured_data={}
),
status='Draft'
),
target_audience={
'audience': suggestion['audience'],
'goals': customizations.get('goals', ['engage', 'inform']),
'preferences': customizations.get('style', {})
}
)
if customized_content:
suggestion['preview'] = customized_content.get('content_flow', {}).get('introduction', {}).get('summary', '')
self.logger.info("Suggestion customized successfully")
return suggestion
except Exception as e:
self.logger.error(f"Error customizing suggestion: {str(e)}", exc_info=True)
st.error(f"Error customizing suggestion: {str(e)}")
return suggestion
def _optimize_content_for_platform(self, content_item: ContentItem, platform: Platform) -> Dict[str, Any]:
"""Optimize content specifically for a target platform."""
try:
self.logger.info(f"Optimizing content for {platform.name}: {content_item.title}")
# Get platform-specific requirements
platform_requirements = self.platform_adapter.get_platform_requirements(platform)
# Generate platform-optimized content
optimized_content = self.content_generator.optimize_for_platform(
content=content_item,
platform=platform,
requirements=platform_requirements
)
if not optimized_content:
raise ValueError(f"Failed to optimize content for {platform.name}")
# Enhance with AI
ai_enhanced = self.ai_generator.enhance_for_platform(
content=optimized_content,
platform=platform,
enhancement_type='platform_specific'
)
if ai_enhanced:
optimized_content.update(ai_enhanced)
# Track optimization history
if content_item.title not in st.session_state.optimization_history:
st.session_state.optimization_history[content_item.title] = []
st.session_state.optimization_history[content_item.title].append({
'platform': platform.name,
'timestamp': datetime.now(),
'changes': optimized_content.get('changes', [])
})
self.logger.info(f"Content optimized successfully for {platform.name}")
return optimized_content
except Exception as e:
self.logger.error(f"Error optimizing content: {str(e)}", exc_info=True)
st.error(f"Error optimizing content: {str(e)}")
return {}
if __name__ == "__main__":
dashboard = ContentCalendarDashboard()
dashboard.render()

View File

@@ -0,0 +1,30 @@
import streamlit as st
from datetime import datetime, timedelta
def render_filters():
with st.expander("Filters", expanded=False):
col1, col2 = st.columns(2)
with col1:
start_date = st.date_input("Start Date", st.session_state.get('filter_start_date', datetime.now()))
end_date = st.date_input("End Date", st.session_state.get('filter_end_date', datetime.now() + timedelta(days=30)))
st.session_state['filter_start_date'] = start_date
st.session_state['filter_end_date'] = end_date
with col2:
platforms = st.multiselect(
"Platforms",
["Blog", "Instagram", "Twitter", "LinkedIn", "Facebook"],
default=st.session_state.get('filter_platforms', ["Blog"])
)
st.session_state['filter_platforms'] = platforms
content_types = st.multiselect(
"Content Types",
["Article", "Social Post", "Video", "Newsletter"],
default=st.session_state.get('filter_content_types', ["Article"])
)
st.session_state['filter_content_types'] = content_types
statuses = st.multiselect(
"Status",
["Draft", "Scheduled", "Published", "Archived"],
default=st.session_state.get('filter_statuses', ["Draft", "Scheduled"])
)
st.session_state['filter_statuses'] = statuses