import streamlit as st import pandas as pd from datetime import datetime, timedelta import logging import sys import hashlib from pathlib import Path from typing import Dict, Any from .calendar_view import render_calendar_view from .filters import render_filters from .add_content_modal import render_add_content_modal from .ai_suggestions_modal import render_ai_suggestions_modal from .components.content_optimization import render_content_optimization from .components.ab_testing import render_ab_testing from .components.content_series import render_content_series_generator from .components.performance_insights import render_performance_insights import json from lib.content_scheduler.ui.dashboard import run_dashboard as run_scheduler_dashboard # Add parent directory to path to import existing tools parent_dir = str(Path(__file__).parent.parent.parent.parent) if parent_dir not in sys.path: sys.path.append(parent_dir) from lib.database.models import ContentItem, ContentType, Platform, get_engine, get_session, init_db from ..core.calendar_manager import CalendarManager from ..core.content_generator import ContentGenerator from ..core.ai_generator import AIGenerator from ..core.content_brief import ContentBriefGenerator from ..integrations.seo_optimizer import SEOOptimizer from lib.integrations.platform_adapters import PlatformAdapter, UnifiedPlatformAdapter # Initialize logger logger = logging.getLogger(__name__) # Initialize DB/session (do this once at app startup) engine = get_engine() init_db(engine) session = get_session(engine) # Import content repurposing UI with error handling def render_smart_repurposing_tab(): """Render the Smart Content Repurposing tab with error handling.""" try: from lib.ai_seo_tools.content_calendar.ui.components.content_repurposing_ui import render_content_repurposing_ui render_content_repurposing_ui() except ImportError as e: st.error(f"Smart Content Repurposing feature is not available: {str(e)}") st.info("Please ensure all dependencies are installed correctly.") except Exception as e: st.error(f"Error loading Smart Content Repurposing: {str(e)}") st.info("Please check the logs for more details.") class ContentCalendarDashboard: """Interactive dashboard for content calendar management.""" def __init__(self): self.logger = logging.getLogger('content_calendar.dashboard') self.logger.info("Initializing ContentCalendarDashboard") self.content_brief_generator = ContentBriefGenerator() self.content_generator = ContentGenerator() self.ai_generator = AIGenerator() self.platform_adapter = UnifiedPlatformAdapter() self.seo_optimizer = SEOOptimizer() # Initialize session state variables if 'ab_test_results' not in st.session_state: st.session_state.ab_test_results = {} if 'optimization_history' not in st.session_state: st.session_state.optimization_history = {} 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", "🔄 Smart Repurposing", "A/B Testing", "Content Series", "Analytics", "Content Scheduling" ]) 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): try: st.session_state.editing_content = row st.rerun() except Exception as e: logger.error(f"Error handling edit action: {str(e)}") st.error("An error occurred while editing content. Please try again.") def on_delete(row): try: self._delete_content(row) st.success(f"Successfully deleted content: {row['title']}") st.rerun() except Exception as e: logger.error(f"Error handling delete action: {str(e)}") st.error("An error occurred while deleting content. Please try again.") def on_generate(row): st.session_state['show_ai_modal'] = True st.session_state['ai_modal_topic'] = row['title'] st.session_state['ai_modal_type'] = str(row['type']) st.session_state['ai_modal_platform'] = str(row['platform']) st.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.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_smart_repurposing_tab() with tabs[3]: render_ab_testing(self.content_generator, None) with tabs[4]: render_content_series_generator( self.ai_generator, self.content_generator, self.seo_optimizer ) with tabs[5]: st.header("Analytics") st.markdown("### Performance Insights") all_content = session.query(ContentItem).all() selected_content = st.selectbox( "Select content to analyze", options=[item.title for item in all_content], key="analytics_content_select" ) if selected_content: content_item = next( item for item in all_content if item.title == selected_content ) render_performance_insights(content_item, self.platform_adapter) st.markdown("### Optimization History") if selected_content in st.session_state.optimization_history: st.json(st.session_state.optimization_history[selected_content]) with tabs[6]: run_scheduler_dashboard() self.logger.info("Dashboard render completed successfully (tabbed UI)") except Exception as e: self.logger.error(f"Error rendering dashboard: {str(e)}", exc_info=True) st.error(f"An error occurred: {str(e)}") def _inject_custom_css(self): st.markdown(""" """, unsafe_allow_html=True) def _get_calendar_data(self): self.logger.info("_get_calendar_data called") try: all_content = session.query(ContentItem).all() data = [] for item in all_content: data.append({ 'date': item.publish_date, 'title': item.title, 'platform': item.platforms[0] if item.platforms else 'Unknown', 'type': item.content_type.value if hasattr(item.content_type, 'value') else str(item.content_type), 'status': item.status }) df = pd.DataFrame(data) if data else None return df except Exception as e: 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): 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) new_item = ContentItem( title=content['title'], description="", content_type=content_type_enum, platforms=[platform_enum.value], publish_date=pd.to_datetime(content['publish_date']), status=content.get('status', 'Draft'), author=None, tags=[], notes=None, seo_data={} ) session.add(new_item) session.commit() def _delete_content(self, row): # Find by title and publish_date (could be improved with unique IDs) all_content = session.query(ContentItem).all() for item in all_content: if (item.title == row['title'] and str(item.publish_date.date()) == str(row['date'].date()) and (item.platforms[0] if item.platforms else 'Unknown') == str(row['platform']) and (item.content_type.value if hasattr(item.content_type, 'value') else str(item.content_type)) == str(row['type'])): session.delete(item) session.commit() break def _edit_content(self, row, new_title, new_platform, new_type, new_status): self._delete_content(row) 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()