From f76381030b49c6d88681e08961e440313a62dfbf Mon Sep 17 00:00:00 2001 From: ajaysi Date: Thu, 28 Aug 2025 11:11:55 +0530 Subject: [PATCH] Alwrity monitoring data service --- backend/api/content_planning/api/router.py | 6 + .../api/content_planning/monitoring_routes.py | 311 ++++++--------- .../services/ai_quality_analysis_service.py | 2 +- backend/services/monitoring_data_service.py | 359 ++++++++++++++++++ backend/start_alwrity_backend.py | 61 ++- fix_imports.py | 48 --- .../components/DataTransparencyPanel.tsx | 82 +++- .../EnhancedPerformanceVisualization.tsx | 53 ++- .../EnhancedStrategyActivationButton.tsx | 88 ++++- .../components/MetricTransparencyCard.tsx | 47 +++ .../components/TrendAnalysis.tsx | 48 ++- .../tabs/AnalyticsTab.tsx | 191 +++++++--- .../src/services/navigationOrchestrator.ts | 18 +- .../src/services/strategyMonitoringApi.ts | 6 +- 14 files changed, 1000 insertions(+), 320 deletions(-) create mode 100644 backend/services/monitoring_data_service.py delete mode 100644 fix_imports.py diff --git a/backend/api/content_planning/api/router.py b/backend/api/content_planning/api/router.py index e444c8ca..31ee3d7c 100644 --- a/backend/api/content_planning/api/router.py +++ b/backend/api/content_planning/api/router.py @@ -20,6 +20,9 @@ from .content_strategy.routes import router as content_strategy_router # Import quality analysis routes from ..quality_analysis_routes import router as quality_analysis_router +# Import monitoring routes +from ..monitoring_routes import router as monitoring_routes_router + # Create main router router = APIRouter(prefix="/api/content-planning", tags=["content-planning"]) @@ -41,6 +44,9 @@ router.include_router(content_strategy_router) # Include quality analysis routes router.include_router(quality_analysis_router) +# Include monitoring routes +router.include_router(monitoring_routes_router) + # Add health check endpoint @router.get("/health") async def content_planning_health_check(): diff --git a/backend/api/content_planning/monitoring_routes.py b/backend/api/content_planning/monitoring_routes.py index 221bbac4..9495f9bd 100644 --- a/backend/api/content_planning/monitoring_routes.py +++ b/backend/api/content_planning/monitoring_routes.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, HTTPException, Depends, Query +from fastapi import APIRouter, HTTPException, Depends, Query, Body from typing import Dict, Any import logging from datetime import datetime, timedelta @@ -8,6 +8,7 @@ import json from services.monitoring_plan_generator import MonitoringPlanGenerator from services.strategy_service import StrategyService +from services.monitoring_data_service import MonitoringDataService from services.database import get_db from models.monitoring_models import ( StrategyMonitoringPlan, MonitoringTask, TaskExecutionLog, @@ -42,11 +43,13 @@ async def generate_monitoring_plan(strategy_id: int): @router.post("/{strategy_id}/activate-with-monitoring") async def activate_strategy_with_monitoring( strategy_id: int, - monitoring_plan: Dict[str, Any] + monitoring_plan: Dict[str, Any] = Body(...), + db: Session = Depends(get_db) ): """Activate strategy with monitoring plan""" try: strategy_service = StrategyService() + monitoring_service = MonitoringDataService(db) # Activate strategy activation_success = await strategy_service.activate_strategy(strategy_id) @@ -56,10 +59,10 @@ async def activate_strategy_with_monitoring( detail=f"Failed to activate strategy {strategy_id}" ) - # Save monitoring plan - plan_success = await strategy_service.save_monitoring_plan(strategy_id, monitoring_plan) - if not plan_success: - logger.warning(f"Failed to save monitoring plan for strategy {strategy_id}") + # Save monitoring data to database + monitoring_success = await monitoring_service.save_monitoring_data(strategy_id, monitoring_plan) + if not monitoring_success: + logger.warning(f"Failed to save monitoring data for strategy {strategy_id}") logger.info(f"Successfully activated strategy {strategy_id} with monitoring") return { @@ -77,16 +80,16 @@ async def activate_strategy_with_monitoring( ) @router.get("/{strategy_id}/monitoring-plan") -async def get_monitoring_plan(strategy_id: int): +async def get_monitoring_plan(strategy_id: int, db: Session = Depends(get_db)): """Get monitoring plan for a strategy""" try: - strategy_service = StrategyService() - monitoring_plan = await strategy_service.get_monitoring_plan(strategy_id) + monitoring_service = MonitoringDataService(db) + monitoring_data = await monitoring_service.get_monitoring_data(strategy_id) - if monitoring_plan: + if monitoring_data: return { "success": True, - "data": monitoring_plan + "data": monitoring_data } else: raise HTTPException( @@ -102,6 +105,25 @@ async def get_monitoring_plan(strategy_id: int): detail=f"Failed to get monitoring plan: {str(e)}" ) +@router.get("/{strategy_id}/analytics-data") +async def get_analytics_data(strategy_id: int, db: Session = Depends(get_db)): + """Get analytics data from monitoring data (no external API calls)""" + try: + monitoring_service = MonitoringDataService(db) + analytics_data = await monitoring_service.get_analytics_data(strategy_id) + + return { + "success": True, + "data": analytics_data, + "message": "Analytics data retrieved from monitoring database" + } + except Exception as e: + logger.error(f"Error getting analytics data for strategy {strategy_id}: {e}") + raise HTTPException( + status_code=500, + detail=f"Failed to get analytics data: {str(e)}" + ) + @router.get("/{strategy_id}/performance-history") async def get_strategy_performance_history(strategy_id: int, days: int = 30): """Get performance history for a strategy""" @@ -496,194 +518,105 @@ async def get_transparency_data( StrategyPerformanceMetrics.strategy_id == strategy_id ).order_by(desc(StrategyPerformanceMetrics.created_at)).first() - # Build transparency data + # Build transparency data from actual monitoring tasks transparency_data = [] - - # Traffic Growth Metric - traffic_growth_data = { - "metricName": "Traffic Growth", - "currentValue": 15.7, # This would come from actual analytics - "unit": "%", - "dataFreshness": { - "lastUpdated": task_logs[0].execution_date.isoformat() if task_logs else datetime.now().isoformat(), - "updateFrequency": "Every 4 hours", - "dataSource": "Google Analytics + AI Analysis", - "confidence": 92 - }, - "measurementMethodology": { - "description": "Organic traffic growth compared to previous period", - "calculationMethod": "Percentage change in organic sessions over 30-day rolling period, weighted by content performance and user engagement", - "dataPoints": ["Organic Sessions", "Page Views", "Bounce Rate", "Time on Site", "Content Performance"], - "validationProcess": "Cross-validated with Google Search Console data and AI-powered content performance analysis" - }, - "monitoringTasks": [], - "strategyMapping": { - "relatedComponents": ["Strategic Insights", "Content Strategy", "Audience Analysis"], - "impactAreas": ["Brand Awareness", "Lead Generation", "Market Reach"], - "dependencies": ["SEO Optimization", "Content Quality", "User Experience"] - }, - "aiInsights": { - "trendAnalysis": "Traffic growth shows strong upward trend with 15.7% increase. Top-performing content categories are educational blog posts and case studies.", - "recommendations": [ - "Increase content production in educational blog category by 25%", - "Optimize case study content for better search visibility", - "Implement A/B testing for content headlines", - "Focus on long-form content (2000+ words) which shows 40% higher engagement" - ], - "riskFactors": ["Seasonal traffic fluctuations", "Competitor content strategy changes", "Algorithm updates"], - "opportunities": ["Video content expansion", "Guest posting opportunities", "Social media amplification"] - } - } - - # Add real monitoring tasks - map based on task content and purpose + + # Group tasks by component for better organization + tasks_by_component = {} for task in monitoring_tasks: - task_title_lower = task.task_title.lower() - task_description_lower = task.task_description.lower() - - # Traffic Growth related tasks - if any(keyword in task_title_lower or keyword in task_description_lower - for keyword in ['traffic', 'organic', 'goal', 'strategic', 'performance', 'prediction']): - task_data = { - "title": task.task_title, - "description": task.task_description, - "assignee": task.assignee, - "frequency": task.frequency, - "metric": task.metric, - "measurementMethod": task.measurement_method, - "successCriteria": task.success_criteria, - "alertThreshold": task.alert_threshold, - "actionableInsights": getattr(task, 'actionable_insights', None), - "status": "active", - "lastExecuted": task_logs[0].execution_date.isoformat() if task_logs else None - } - traffic_growth_data["monitoringTasks"].append(task_data) - - transparency_data.append(traffic_growth_data) - - # Engagement Rate Metric - engagement_data = { - "metricName": "Engagement Rate", - "currentValue": 8.3, - "unit": "%", - "dataFreshness": { - "lastUpdated": task_logs[0].execution_date.isoformat() if task_logs else datetime.now().isoformat(), - "updateFrequency": "Every 2 hours", - "dataSource": "Social Media Analytics + Website Analytics", - "confidence": 88 - }, - "measurementMethodology": { - "description": "Average engagement rate across all content and social media", - "calculationMethod": "Weighted average of likes, shares, comments, and time spent across all platforms", - "dataPoints": ["Social Media Engagement", "Website Comments", "Time on Page", "Social Shares", "Email Engagement"], - "validationProcess": "Cross-platform validation using multiple analytics tools and AI sentiment analysis" - }, - "monitoringTasks": [], - "strategyMapping": { - "relatedComponents": ["Audience Analysis", "Content Strategy", "Social Media Strategy"], - "impactAreas": ["Brand Engagement", "Community Building", "Customer Loyalty"], - "dependencies": ["Content Quality", "Social Media Presence", "Community Management"] - }, - "aiInsights": { - "trendAnalysis": "Engagement rate is stable at 8.3% with peak engagement during lunch hours and early evenings.", - "recommendations": [ - "Increase video content production by 50%", - "Optimize posting times for peak engagement hours", - "Implement interactive content elements", - "Focus on community-building content" + component = task.component_name or 'General' + if component not in tasks_by_component: + tasks_by_component[component] = [] + tasks_by_component[component].append(task) + + # Create transparency data for each component + for component, tasks in tasks_by_component.items(): + component_data = { + "metricName": component, + "currentValue": len(tasks), + "unit": "tasks", + "dataFreshness": { + "lastUpdated": task_logs[0].execution_date.isoformat() if task_logs else datetime.now().isoformat(), + "updateFrequency": "Real-time", + "dataSource": "Monitoring System", + "confidence": 95 + }, + "measurementMethodology": { + "description": f"AI-powered monitoring for {component} with {len(tasks)} active tasks", + "calculationMethod": "Automated monitoring with real-time data collection and analysis", + "dataPoints": [task.metric for task in tasks if task.metric], + "validationProcess": "Cross-validated with multiple data sources and AI analysis" + }, + "monitoringTasks": [ + { + "title": task.task_title, + "description": task.task_description, + "assignee": task.assignee, + "frequency": task.frequency, + "metric": task.metric, + "measurementMethod": task.measurement_method, + "successCriteria": task.success_criteria, + "alertThreshold": task.alert_threshold, + "status": task.status, + "lastExecuted": task.last_executed.isoformat() if task.last_executed else None + } + for task in tasks ], - "riskFactors": ["Platform algorithm changes", "Content fatigue", "Competition for attention"], - "opportunities": ["Live streaming opportunities", "User-generated content campaigns", "Influencer collaborations"] - } - } - - # Add engagement-related tasks - for task in monitoring_tasks: - task_title_lower = task.task_title.lower() - task_description_lower = task.task_description.lower() - - if any(keyword in task_title_lower or keyword in task_description_lower - for keyword in ['engagement', 'social', 'community', 'audience', 'insight', 'competitive']): - task_data = { - "title": task.task_title, - "description": task.task_description, - "assignee": task.assignee, - "frequency": task.frequency, - "metric": task.metric, - "measurementMethod": task.measurement_method, - "successCriteria": task.success_criteria, - "alertThreshold": task.alert_threshold, - "actionableInsights": getattr(task, 'actionable_insights', None), - "status": "active", - "lastExecuted": task_logs[0].execution_date.isoformat() if task_logs else None + "strategyMapping": { + "relatedComponents": [component], + "impactAreas": ["Performance Monitoring", "Strategy Optimization", "Risk Management"], + "dependencies": ["Data Collection", "AI Analysis", "Alert System"] + }, + "aiInsights": { + "trendAnalysis": f"Active monitoring for {component} with {len(tasks)} configured tasks", + "recommendations": [ + "Monitor task execution status regularly", + "Review performance metrics weekly", + "Adjust thresholds based on performance trends" + ], + "riskFactors": ["Task execution failures", "Data collection issues", "System downtime"], + "opportunities": ["Automated optimization", "Predictive analytics", "Enhanced monitoring"] } - engagement_data["monitoringTasks"].append(task_data) - - transparency_data.append(engagement_data) - - # Conversion Rate Metric - conversion_data = { - "metricName": "Conversion Rate", - "currentValue": 2.1, - "unit": "%", - "dataFreshness": { - "lastUpdated": task_logs[0].execution_date.isoformat() if task_logs else datetime.now().isoformat(), - "updateFrequency": "Every 6 hours", - "dataSource": "Google Analytics + CRM Data", - "confidence": 85 - }, - "measurementMethodology": { - "description": "Content-driven conversion rate across all touchpoints", - "calculationMethod": "Conversions divided by total visitors, weighted by content attribution and customer journey analysis", - "dataPoints": ["Website Conversions", "Email Signups", "Lead Form Submissions", "Content Downloads", "Sales Attribution"], - "validationProcess": "CRM integration validation and conversion funnel analysis" - }, - "monitoringTasks": [], - "strategyMapping": { - "relatedComponents": ["Performance Predictions", "Implementation Roadmap", "Risk Assessment"], - "impactAreas": ["Revenue Generation", "Lead Quality", "Customer Acquisition"], - "dependencies": ["Content Quality", "User Experience", "Lead Nurturing"] - }, - "aiInsights": { - "trendAnalysis": "Conversion rate is improving steadily with 2.1% current rate. Top-converting content includes case studies and product demos.", - "recommendations": [ - "Increase case study and demo content production", - "Optimize mobile user experience further", - "Implement personalized content recommendations", - "A/B test call-to-action buttons and forms" - ], - "riskFactors": ["Market competition", "Economic factors", "Technology changes"], - "opportunities": ["Personalization opportunities", "Automation implementation", "Cross-selling strategies"] } - } - - # Add conversion-related tasks - for task in monitoring_tasks: - task_title_lower = task.task_title.lower() - task_description_lower = task.task_description.lower() - - if any(keyword in task_title_lower or keyword in task_description_lower - for keyword in ['conversion', 'funnel', 'implementation', 'resource', 'risk', 'mitigation']): - task_data = { - "title": task.task_title, - "description": task.task_description, - "assignee": task.assignee, - "frequency": task.frequency, - "metric": task.metric, - "measurementMethod": task.measurement_method, - "successCriteria": task.success_criteria, - "alertThreshold": task.alert_threshold, - "actionableInsights": getattr(task, 'actionable_insights', None), - "status": "active", - "lastExecuted": task_logs[0].execution_date.isoformat() if task_logs else None + transparency_data.append(component_data) + + # If no monitoring tasks found, create a default transparency entry + if not transparency_data: + transparency_data = [{ + "metricName": "Strategy Monitoring", + "currentValue": 0, + "unit": "tasks", + "dataFreshness": { + "lastUpdated": datetime.now().isoformat(), + "updateFrequency": "Real-time", + "dataSource": "Monitoring System", + "confidence": 0 + }, + "measurementMethodology": { + "description": "No monitoring tasks configured yet", + "calculationMethod": "Manual setup required", + "dataPoints": [], + "validationProcess": "Not applicable" + }, + "monitoringTasks": [], + "strategyMapping": { + "relatedComponents": ["Strategy"], + "impactAreas": ["Monitoring"], + "dependencies": ["Setup"] + }, + "aiInsights": { + "trendAnalysis": "No monitoring data available", + "recommendations": ["Set up monitoring tasks", "Configure alerts", "Enable data collection"], + "riskFactors": ["No monitoring in place"], + "opportunities": ["Implement comprehensive monitoring"] } - conversion_data["monitoringTasks"].append(task_data) - - transparency_data.append(conversion_data) + }] + # Return the transparency data return { "success": True, "data": transparency_data, - "message": "Transparency data retrieved successfully" + "message": f"Transparency data retrieved successfully for strategy {strategy_id}" } except Exception as e: diff --git a/backend/services/ai_quality_analysis_service.py b/backend/services/ai_quality_analysis_service.py index 4e684d51..23b10265 100644 --- a/backend/services/ai_quality_analysis_service.py +++ b/backend/services/ai_quality_analysis_service.py @@ -177,7 +177,7 @@ class AIQualityAnalysisService: Focus on strategic depth, clarity, and measurability. """ - ai_response = await gemini_structured_json_response( + ai_response = gemini_structured_json_response( prompt=prompt, schema=QUALITY_ANALYSIS_SCHEMA, temperature=0.3, diff --git a/backend/services/monitoring_data_service.py b/backend/services/monitoring_data_service.py new file mode 100644 index 00000000..b783a170 --- /dev/null +++ b/backend/services/monitoring_data_service.py @@ -0,0 +1,359 @@ +""" +Monitoring Data Service +Handles saving and retrieving monitoring data from database and cache. +""" + +import logging +from typing import Dict, Any, List, Optional +from datetime import datetime +from sqlalchemy.orm import Session +from sqlalchemy import and_, desc + +from models.monitoring_models import ( + StrategyMonitoringPlan, MonitoringTask, TaskExecutionLog, + StrategyPerformanceMetrics, StrategyActivationStatus +) +from models.enhanced_strategy_models import EnhancedContentStrategy + +logger = logging.getLogger(__name__) + +class MonitoringDataService: + """Service for managing monitoring data in database and cache.""" + + def __init__(self, db_session: Session): + self.db = db_session + + async def save_monitoring_data(self, strategy_id: int, monitoring_plan: Dict[str, Any]) -> bool: + """Save monitoring plan and tasks to database.""" + try: + logger.info(f"Saving monitoring data for strategy {strategy_id}") + logger.info(f"Monitoring plan received: {monitoring_plan}") + + # Save the complete monitoring plan + monitoring_plan_record = StrategyMonitoringPlan( + strategy_id=strategy_id, + plan_data=monitoring_plan + ) + self.db.add(monitoring_plan_record) + + # Save individual monitoring tasks + monitoring_tasks = monitoring_plan.get('monitoringTasks', []) + logger.info(f"Found {len(monitoring_tasks)} monitoring tasks to save") + + for i, task_data in enumerate(monitoring_tasks): + logger.info(f"Saving task {i+1}: {task_data.get('title', 'Unknown')}") + task = MonitoringTask( + strategy_id=strategy_id, + component_name=task_data.get('component', ''), + task_title=task_data.get('title', ''), + task_description=task_data.get('description', ''), + assignee=task_data.get('assignee', 'ALwrity'), + frequency=task_data.get('frequency', 'Monthly'), + metric=task_data.get('metric', ''), + measurement_method=task_data.get('measurementMethod', ''), + success_criteria=task_data.get('successCriteria', ''), + alert_threshold=task_data.get('alertThreshold', ''), + status='active' + ) + self.db.add(task) + + # Save activation status + activation_status = StrategyActivationStatus( + strategy_id=strategy_id, + user_id=1, # Default user ID + activation_date=datetime.utcnow(), + status='active' + ) + self.db.add(activation_status) + + # Save initial performance metrics + performance_metrics = StrategyPerformanceMetrics( + strategy_id=strategy_id, + user_id=1, # Default user ID + metric_date=datetime.utcnow(), + data_source='monitoring_plan', + confidence_score=85 # High confidence for monitoring plan data + ) + self.db.add(performance_metrics) + + self.db.commit() + logger.info(f"Successfully saved monitoring data for strategy {strategy_id}") + return True + + except Exception as e: + logger.error(f"Error saving monitoring data for strategy {strategy_id}: {e}") + self.db.rollback() + return False + + async def get_monitoring_data(self, strategy_id: int) -> Optional[Dict[str, Any]]: + """Get monitoring data from database.""" + try: + logger.info(f"Retrieving monitoring data for strategy {strategy_id}") + + # Get the monitoring plan + monitoring_plan = self.db.query(StrategyMonitoringPlan).filter( + StrategyMonitoringPlan.strategy_id == strategy_id + ).order_by(desc(StrategyMonitoringPlan.created_at)).first() + + if not monitoring_plan: + logger.warning(f"No monitoring plan found for strategy {strategy_id}") + return None + + # Get monitoring tasks + tasks = self.db.query(MonitoringTask).filter( + MonitoringTask.strategy_id == strategy_id + ).all() + + # Get activation status + activation_status = self.db.query(StrategyActivationStatus).filter( + StrategyActivationStatus.strategy_id == strategy_id + ).first() + + # Get performance metrics + performance_metrics = self.db.query(StrategyPerformanceMetrics).filter( + StrategyPerformanceMetrics.strategy_id == strategy_id + ).order_by(desc(StrategyPerformanceMetrics.metric_date)).first() + + # Build comprehensive monitoring data + monitoring_data = { + 'strategy_id': strategy_id, + 'monitoring_plan': monitoring_plan.plan_data, + 'monitoring_tasks': [ + { + 'id': task.id, + 'component': task.component_name, + 'title': task.task_title, + 'description': task.task_description, + 'assignee': task.assignee, + 'frequency': task.frequency, + 'metric': task.metric, + 'measurementMethod': task.measurement_method, + 'successCriteria': task.success_criteria, + 'alertThreshold': task.alert_threshold, + 'status': task.status, + 'last_executed': task.last_executed.isoformat() if task.last_executed else None, + 'next_execution': task.next_execution.isoformat() if task.next_execution else None + } + for task in tasks + ], + 'activation_status': { + 'activation_date': activation_status.activation_date.isoformat() if activation_status else None, + 'status': activation_status.status if activation_status else 'unknown', + 'performance_score': activation_status.performance_score if activation_status else None + }, + 'performance_metrics': { + 'traffic_growth': performance_metrics.traffic_growth_percentage if performance_metrics else None, + 'engagement_rate': performance_metrics.engagement_rate_percentage if performance_metrics else None, + 'conversion_rate': performance_metrics.conversion_rate_percentage if performance_metrics else None, + 'roi_ratio': performance_metrics.roi_ratio if performance_metrics else None, + 'content_quality_score': performance_metrics.content_quality_score if performance_metrics else None, + 'data_source': performance_metrics.data_source if performance_metrics else None, + 'confidence_score': performance_metrics.confidence_score if performance_metrics else None + }, + 'created_at': monitoring_plan.created_at.isoformat(), + 'updated_at': monitoring_plan.updated_at.isoformat() + } + + logger.info(f"Successfully retrieved monitoring data for strategy {strategy_id}") + return monitoring_data + + except Exception as e: + logger.error(f"Error retrieving monitoring data for strategy {strategy_id}: {e}") + return None + + async def get_analytics_data(self, strategy_id: int) -> Dict[str, Any]: + """Get analytics data from monitoring data (no external API calls).""" + try: + logger.info(f"Generating analytics data for strategy {strategy_id}") + + # Get monitoring data from database + monitoring_data = await self.get_monitoring_data(strategy_id) + + if not monitoring_data: + logger.warning(f"No monitoring data found for strategy {strategy_id}") + return self._get_empty_analytics_data() + + # Extract analytics from monitoring data + monitoring_plan = monitoring_data['monitoring_plan'] + tasks = monitoring_data['monitoring_tasks'] + performance_metrics = monitoring_data['performance_metrics'] + + # Always use monitoring tasks from the plan for rich data, fallback to database tasks + monitoring_tasks = [] + if monitoring_plan.get('monitoringTasks'): + # Use rich data from monitoring plan + monitoring_tasks = [ + { + 'id': i + 1, + 'component': task.get('component', ''), + 'title': task.get('title', ''), + 'description': task.get('description', ''), + 'assignee': task.get('assignee', 'ALwrity'), + 'frequency': task.get('frequency', 'Monthly'), + 'metric': task.get('metric', ''), + 'measurementMethod': task.get('measurementMethod', ''), + 'successCriteria': task.get('successCriteria', ''), + 'alertThreshold': task.get('alertThreshold', ''), + 'actionableInsights': task.get('actionableInsights', ''), + 'status': 'active', + 'last_executed': None, + 'next_execution': None + } + for i, task in enumerate(monitoring_plan.get('monitoringTasks', [])) + ] + elif tasks: + # Fallback to database tasks if plan doesn't have them + monitoring_tasks = [ + { + 'id': task.id, + 'component': task.component_name, + 'title': task.task_title, + 'description': task.task_description, + 'assignee': task.assignee, + 'frequency': task.frequency, + 'metric': task.metric, + 'measurementMethod': task.measurement_method, + 'successCriteria': task.success_criteria, + 'alertThreshold': task.alert_threshold, + 'actionableInsights': '', + 'status': task.status, + 'last_executed': task.last_executed.isoformat() if task.last_executed else None, + 'next_execution': task.next_execution.isoformat() if task.next_execution else None + } + for task in tasks + ] + + # Always use performance metrics from success metrics for rich data + extracted_metrics = {} + if monitoring_plan.get('successMetrics'): + success_metrics = monitoring_plan['successMetrics'] + extracted_metrics = { + 'traffic_growth': self._extract_percentage(success_metrics.get('trafficGrowth', {}).get('current', '0%')), + 'engagement_rate': self._extract_percentage(success_metrics.get('engagementRate', {}).get('current', '0%')), + 'conversion_rate': self._extract_percentage(success_metrics.get('conversionRate', {}).get('current', '0%')), + 'roi_ratio': self._extract_ratio(success_metrics.get('roi', {}).get('current', '0:1')), + 'content_quality_score': self._extract_percentage(success_metrics.get('contentQuality', {}).get('current', '0%')), + 'data_source': 'monitoring_plan', + 'confidence_score': 85 + } + else: + # Fallback to database metrics if plan doesn't have them + extracted_metrics = { + 'traffic_growth': performance_metrics.get('traffic_growth', 0), + 'engagement_rate': performance_metrics.get('engagement_rate', 0), + 'conversion_rate': performance_metrics.get('conversion_rate', 0), + 'roi_ratio': performance_metrics.get('roi_ratio', 0), + 'content_quality_score': performance_metrics.get('content_quality_score', 0), + 'data_source': performance_metrics.get('data_source', 'database'), + 'confidence_score': performance_metrics.get('confidence_score', 70) + } + + # Build analytics data from monitoring plan + analytics_data = { + 'performance_trends': { + 'traffic_growth': extracted_metrics.get('traffic_growth', 0), + 'engagement_rate': extracted_metrics.get('engagement_rate', 0), + 'conversion_rate': extracted_metrics.get('conversion_rate', 0), + 'roi_ratio': extracted_metrics.get('roi_ratio', 0), + 'content_quality_score': extracted_metrics.get('content_quality_score', 0) + }, + 'content_evolution': { + 'content_pillars': monitoring_plan.get('contentPillars', []), + 'content_mix': monitoring_plan.get('contentMix', {}), + 'publishing_frequency': monitoring_plan.get('publishingFrequency', ''), + 'quality_metrics': monitoring_plan.get('qualityMetrics', []) + }, + 'engagement_patterns': { + 'audience_segments': monitoring_plan.get('audienceSegments', []), + 'engagement_metrics': monitoring_plan.get('engagementMetrics', {}), + 'optimal_timing': monitoring_plan.get('optimalTiming', {}), + 'platform_performance': monitoring_plan.get('platformPerformance', {}) + }, + 'recommendations': monitoring_plan.get('recommendations', []), + 'insights': monitoring_plan.get('insights', []), + 'monitoring_data': monitoring_data, + 'monitoring_tasks': monitoring_tasks, + 'monitoring_plan': monitoring_plan, # Include full monitoring plan for rich data + 'success_metrics': monitoring_plan.get('successMetrics', {}), # Include success metrics + 'monitoring_schedule': monitoring_plan.get('monitoringSchedule', {}), # Include monitoring schedule + '_source': 'database_monitoring', + 'data_freshness': monitoring_data['updated_at'], + 'confidence_score': extracted_metrics.get('confidence_score', 85) + } + + logger.info(f"Successfully generated analytics data for strategy {strategy_id}") + return analytics_data + + except Exception as e: + logger.error(f"Error generating analytics data for strategy {strategy_id}: {e}") + return self._get_empty_analytics_data() + + def _get_empty_analytics_data(self) -> Dict[str, Any]: + """Return empty analytics data structure.""" + return { + 'performance_trends': {}, + 'content_evolution': {}, + 'engagement_patterns': {}, + 'recommendations': [], + 'insights': [], + 'monitoring_data': None, + 'monitoring_tasks': [], + '_source': 'empty', + 'data_freshness': datetime.utcnow().isoformat(), + 'confidence_score': 0 + } + + def _extract_percentage(self, value: str) -> float: + """Extract percentage value from string like '15%'.""" + try: + if isinstance(value, str) and '%' in value: + return float(value.replace('%', '')) + elif isinstance(value, (int, float)): + return float(value) + else: + return 0.0 + except (ValueError, TypeError): + return 0.0 + + def _extract_ratio(self, value: str) -> float: + """Extract ratio value from string like '3:1'.""" + try: + if isinstance(value, str) and ':' in value: + parts = value.split(':') + if len(parts) == 2: + return float(parts[0]) / float(parts[1]) + elif isinstance(value, (int, float)): + return float(value) + else: + return 0.0 + except (ValueError, TypeError): + return 0.0 + + async def update_performance_metrics(self, strategy_id: int, metrics: Dict[str, Any]) -> bool: + """Update performance metrics for a strategy.""" + try: + logger.info(f"Updating performance metrics for strategy {strategy_id}") + + performance_metrics = StrategyPerformanceMetrics( + strategy_id=strategy_id, + user_id=1, # Default user ID + metric_date=datetime.utcnow(), + traffic_growth_percentage=metrics.get('traffic_growth'), + engagement_rate_percentage=metrics.get('engagement_rate'), + conversion_rate_percentage=metrics.get('conversion_rate'), + roi_ratio=metrics.get('roi_ratio'), + content_quality_score=metrics.get('content_quality_score'), + data_source='manual_update', + confidence_score=metrics.get('confidence_score', 70) + ) + + self.db.add(performance_metrics) + self.db.commit() + + logger.info(f"Successfully updated performance metrics for strategy {strategy_id}") + return True + + except Exception as e: + logger.error(f"Error updating performance metrics for strategy {strategy_id}: {e}") + self.db.rollback() + return False diff --git a/backend/start_alwrity_backend.py b/backend/start_alwrity_backend.py index 9d094b62..460778b4 100644 --- a/backend/start_alwrity_backend.py +++ b/backend/start_alwrity_backend.py @@ -9,6 +9,7 @@ import os import sys import subprocess import time +import argparse from pathlib import Path def install_requirements(): @@ -213,18 +214,25 @@ def setup_environment(): print("āœ… Environment setup complete") -def start_backend(): +def start_backend(enable_reload=False): """Start the backend server.""" print("šŸš€ Starting ALwrity Backend...") # Set environment variables os.environ.setdefault("HOST", "0.0.0.0") os.environ.setdefault("PORT", "8000") - os.environ.setdefault("RELOAD", "true") + + # Set reload based on argument or environment variable + if enable_reload: + os.environ.setdefault("RELOAD", "true") + print(" šŸ”„ Development mode: Auto-reload enabled") + else: + os.environ.setdefault("RELOAD", "false") + print(" šŸ­ Production mode: Auto-reload disabled") host = os.getenv("HOST", "0.0.0.0") port = int(os.getenv("PORT", "8000")) - reload = os.getenv("RELOAD", "true").lower() == "true" + reload = os.getenv("RELOAD", "false").lower() == "true" print(f" šŸ“ Host: {host}") print(f" šŸ”Œ Port: {port}") @@ -242,12 +250,48 @@ def start_backend(): print(" šŸ“ˆ API Monitoring: http://localhost:8000/api/content-planning/monitoring/health") print("\nā¹ļø Press Ctrl+C to stop the server") print("=" * 60) + print("\nšŸ’” Usage:") + print(" Production mode (default): python start_alwrity_backend.py") + print(" Development mode: python start_alwrity_backend.py --dev") + print(" With auto-reload: python start_alwrity_backend.py --reload") + print("=" * 60) uvicorn.run( "app:app", host=host, port=port, reload=reload, + reload_excludes=[ + "*.pyc", + "*.pyo", + "*.pyd", + "__pycache__", + "*.log", + "*.sqlite", + "*.db", + "*.tmp", + "*.temp", + "test_*.py", + "temp_*.py", + "monitoring_data_service.py", + "test_monitoring_save.py", + "*.json", + "*.yaml", + "*.yml", + ".env*", + "logs/*", + "cache/*", + "tmp/*", + "temp/*", + "middleware/*", + "models/*", + "scripts/*" + ], + reload_includes=[ + "app.py", + "api/**/*.py", + "services/**/*.py" + ], log_level="info" ) @@ -261,6 +305,12 @@ def start_backend(): def main(): """Main function to set up and start the backend.""" + # Parse command line arguments + parser = argparse.ArgumentParser(description="ALwrity Backend Server") + parser.add_argument("--reload", action="store_true", help="Enable auto-reload for development") + parser.add_argument("--dev", action="store_true", help="Enable development mode (auto-reload)") + args = parser.parse_args() + print("šŸŽÆ ALwrity Backend Server") print("=" * 40) @@ -279,8 +329,9 @@ def main(): # Setup environment setup_environment() - # Start backend - return start_backend() + # Start backend with reload option + enable_reload = args.reload or args.dev + return start_backend(enable_reload=enable_reload) if __name__ == "__main__": success = main() diff --git a/fix_imports.py b/fix_imports.py deleted file mode 100644 index 5e144fbb..00000000 --- a/fix_imports.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -""" -Script to fix import paths in step files -""" - -import os -import re - -def fix_imports_in_file(file_path): - """Fix import paths in a file.""" - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Fix the base_step import path - # Change from ..base_step to ...base_step for subdirectories - if '/step9_content_recommendations/' in file_path or '/step10_performance_optimization/' in file_path or '/step11_strategy_alignment_validation/' in file_path or '/step12_final_calendar_assembly/' in file_path: - content = re.sub(r'from \.\.base_step import', 'from ...base_step import', content) - - with open(file_path, 'w', encoding='utf-8') as f: - f.write(content) - - print(f"āœ… Fixed imports in {file_path}") - return True - except Exception as e: - print(f"āŒ Error fixing {file_path}: {e}") - return False - -def main(): - """Main function to fix all import paths.""" - base_path = "services/calendar_generation_datasource_framework/prompt_chaining/steps" - - # Files that need fixing - files_to_fix = [ - f"{base_path}/phase3/step9_content_recommendations/step9_main.py", - f"{base_path}/phase4/step10_performance_optimization/step10_main.py", - f"{base_path}/phase4/step11_strategy_alignment_validation/step11_main.py", - f"{base_path}/phase4/step12_final_calendar_assembly/step12_main.py", - ] - - for file_path in files_to_fix: - if os.path.exists(file_path): - fix_imports_in_file(file_path) - else: - print(f"āš ļø File not found: {file_path}") - -if __name__ == "__main__": - main() diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/DataTransparencyPanel.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/DataTransparencyPanel.tsx index 67bfa2a1..30bbc6ef 100644 --- a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/DataTransparencyPanel.tsx +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/DataTransparencyPanel.tsx @@ -74,6 +74,63 @@ const DataTransparencyPanel: React.FC = ({ }; }, [refreshInterval]); + const convertMonitoringTasksToTransparencyData = (monitoringTasks: any[]) => { + try { + // Group tasks by component + const tasksByComponent = monitoringTasks.reduce((acc, task) => { + const component = task.component || 'General'; + if (!acc[component]) { + acc[component] = []; + } + acc[component].push(task); + return acc; + }, {}); + + // Convert to transparency data format + return Object.entries(tasksByComponent).map(([component, tasks]: [string, any]) => ({ + metricName: component, + currentValue: tasks.length, + unit: "tasks", + dataFreshness: { + lastUpdated: new Date().toISOString(), + updateFrequency: "Real-time", + dataSource: "Monitoring Tasks" + }, + measurementMethod: "AI-powered monitoring", + successCriteria: `${tasks.length} active monitoring tasks`, + monitoringTasks: tasks.map((task: any) => ({ + title: task.title, + description: task.description, + assignee: task.assignee, + frequency: task.frequency, + metric: task.metric, + measurementMethod: task.measurementMethod, + successCriteria: task.successCriteria, + alertThreshold: task.alertThreshold, + actionableInsights: task.actionableInsights, + status: task.status || 'active', + lastExecuted: task.last_executed, + nextExecution: task.next_execution + })), + insights: [ + `Active monitoring for ${component} with ${tasks.length} tasks`, + "AI-powered performance tracking enabled", + "Real-time alerts and notifications configured", + `Monitoring frequency: ${tasks[0]?.frequency || 'Monthly'}` + ], + recommendations: [ + "Monitor task execution status regularly", + "Review performance metrics weekly", + "Adjust thresholds based on performance trends", + `Focus on ${tasks.filter((t: any) => t.assignee === 'ALwrity').length} AI-managed tasks` + ] + })); + } catch (error) { + console.error('Error converting monitoring tasks to transparency data:', error); + return []; + } + }; + const loadTransparencyData = async () => { try { setLoading(true); @@ -87,7 +144,30 @@ const DataTransparencyPanel: React.FC = ({ return; } } catch (apiError) { - console.warn('API call failed, falling back to mock data:', apiError); + console.warn('API call failed, trying localStorage:', apiError); + // Try to load from localStorage + const analyticsData = localStorage.getItem('strategy_analytics_data'); + if (analyticsData) { + try { + const data = JSON.parse(analyticsData); + console.log('Loaded analytics data from localStorage:', data); + + // Extract monitoring tasks from analytics data + const monitoringTasks = data.monitoring_tasks || []; + console.log('Extracted monitoring tasks:', monitoringTasks); + + if (monitoringTasks.length > 0) { + // Convert monitoring tasks to transparency data format + const transparencyDataFromTasks = convertMonitoringTasksToTransparencyData(monitoringTasks); + setTransparencyData(transparencyDataFromTasks); + return; + } else { + console.warn('No monitoring tasks found in analytics data'); + } + } catch (parseError) { + console.warn('Failed to parse analytics data from localStorage:', parseError); + } + } // Continue to mock data as fallback } diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/EnhancedPerformanceVisualization.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/EnhancedPerformanceVisualization.tsx index 82108075..61c74963 100644 --- a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/EnhancedPerformanceVisualization.tsx +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/EnhancedPerformanceVisualization.tsx @@ -99,12 +99,55 @@ const EnhancedPerformanceVisualization: React.FC= 80 ? 'excellent' : + data.performance_metrics?.confidence_score >= 60 ? 'good' : 'needs_attention', + metrics: [ + { + name: 'Strategic Completeness', + score: 85, + status: 'excellent', + description: 'Strategy covers all key components', + recommendations: monitoringPlan?.recommendations || [] + }, + { + name: 'Content Quality', + score: data.performance_metrics?.content_quality_score || 75, + status: data.performance_metrics?.content_quality_score >= 80 ? 'excellent' : 'good', + description: 'Content meets quality standards', + recommendations: ['Continue monitoring content performance'] + }, + { + name: 'Engagement Metrics', + score: data.performance_metrics?.engagement_rate || 70, + status: data.performance_metrics?.engagement_rate >= 75 ? 'good' : 'needs_attention', + description: 'Audience engagement levels', + recommendations: ['Focus on improving engagement rates'] + } + ], + recommendations: monitoringPlan?.recommendations || [], + confidence_score: data.performance_metrics?.confidence_score || 75 + }; + + setQualityAnalysis(qualityData); + console.log('āœ… Quality analysis loaded from monitoring data'); + } else { + // Fallback to API call if no monitoring data + console.log('āš ļø No monitoring data found, skipping quality analysis'); + setQualityAnalysis(null); + } } catch (err: any) { - setError(err.message || 'Failed to load quality analysis'); - console.error('Error loading quality analysis:', err); + console.warn('āš ļø Error loading quality analysis from monitoring data:', err); + setQualityAnalysis(null); } finally { setLoadingQuality(false); } diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/EnhancedStrategyActivationButton.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/EnhancedStrategyActivationButton.tsx index 2cb648a2..9d8e074b 100644 --- a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/EnhancedStrategyActivationButton.tsx +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/EnhancedStrategyActivationButton.tsx @@ -17,6 +17,7 @@ import { motion, AnimatePresence, easeOut } from 'framer-motion'; import StrategyActivationModal from '../../StrategyActivationModal'; import { useNavigationOrchestrator } from '../../../../../services/navigationOrchestrator'; + interface EnhancedStrategyActivationButtonProps { strategyData: any; strategyConfirmed: boolean; @@ -45,16 +46,7 @@ const EnhancedStrategyActivationButton: React.FC { try { console.log('šŸŽÆ EnhancedStrategyActivationButton: handleSetupMonitoring called'); - // Call the actual activation function + + // Get strategy ID + const strategyId = strategyData?.id || 1; + + // Step 1: Generate monitoring plan if not provided + let finalMonitoringPlan = monitoringPlan; + if (!finalMonitoringPlan) { + console.log('šŸŽÆ Generating monitoring plan...'); + try { + const response = await fetch(`/api/content-planning/strategy/${strategyId}/generate-monitoring-plan`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }); + const planResponse = await response.json(); + finalMonitoringPlan = planResponse.data; + console.log('šŸŽÆ Monitoring plan generated:', finalMonitoringPlan); + } catch (error) { + console.warn('Could not generate monitoring plan:', error); + // Continue without monitoring plan + } + } + + // Step 2: Activate strategy with monitoring plan + console.log('šŸŽÆ Activating strategy with monitoring...'); + try { + const response = await fetch(`/api/content-planning/strategy/${strategyId}/activate-with-monitoring`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(finalMonitoringPlan) + }); + const activationResponse = await response.json(); + console.log('šŸŽÆ Strategy activated with monitoring:', activationResponse); + } catch (error) { + console.warn('Could not activate strategy with monitoring:', error); + // Continue with local activation only + } + + // Step 3: Call the local confirmation function console.log('šŸŽÆ EnhancedStrategyActivationButton: Calling onConfirmStrategy()'); await onConfirmStrategy(); console.log('šŸŽÆ EnhancedStrategyActivationButton: onConfirmStrategy() completed'); - // Update strategy state to confirmed/active - // This will trigger UI updates in parent components + // Step 4: Update analytics and monitoring data + console.log('šŸŽÆ Setting up analytics and monitoring...'); + await setupAnalyticsAndMonitoring(strategyId, finalMonitoringPlan); // Show success state setIsSuccess(true); setShowSuccessMessage(true); // Use navigation orchestrator to handle successful activation - const strategyId = strategyData?.strategy_metadata?.user_id || strategyData?.metadata?.user_id || '1'; - navigationOrchestrator.handleStrategyActivationSuccess(strategyId, strategyData); + const userId = strategyData?.strategy_metadata?.user_id || strategyData?.metadata?.user_id || '1'; + navigationOrchestrator.handleStrategyActivationSuccess(userId, strategyData); // Reset after success animation setTimeout(() => { @@ -98,6 +128,34 @@ const EnhancedStrategyActivationButton: React.FC { + try { + console.log('šŸŽÆ Setting up analytics and monitoring for strategy:', strategyId); + + // Update analytics page with monitoring data + // This will populate the analytics dashboard with the new monitoring tasks + const analyticsData = { + strategy_id: strategyId, + monitoring_plan: monitoringPlan, + activation_date: new Date().toISOString(), + status: 'active' + }; + + // Store analytics data in localStorage for the analytics page to access + localStorage.setItem('strategy_analytics_data', JSON.stringify(analyticsData)); + + // Also store monitoring tasks for the data transparency panel + const monitoringTasks = monitoringPlan?.monitoringTasks || []; + localStorage.setItem('strategy_monitoring_tasks', JSON.stringify(monitoringTasks)); + + console.log('šŸŽÆ Analytics and monitoring setup completed'); + + } catch (error) { + console.error('Error setting up analytics and monitoring:', error); + // Don't fail the activation if analytics setup fails + } + }; + // Success animation variants const successVariants = { initial: { scale: 0, opacity: 0 }, diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/MetricTransparencyCard.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/MetricTransparencyCard.tsx index e15f2afd..5a7a3008 100644 --- a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/MetricTransparencyCard.tsx +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/MetricTransparencyCard.tsx @@ -293,6 +293,33 @@ const MetricTransparencyCard: React.FC = ({ {task.description} + + {task.frequency && ( + } + sx={{ + background: 'rgba(255,255,255,0.1)', + color: 'white', + fontSize: '0.6rem' + }} + /> + )} + {task.metric && ( + } + sx={{ + background: 'rgba(255,255,255,0.1)', + color: 'white', + fontSize: '0.6rem' + }} + /> + )} + + @@ -310,6 +337,26 @@ const MetricTransparencyCard: React.FC = ({ {task.successCriteria} + {task.alertThreshold && ( + + + Alert Threshold + + + {task.alertThreshold} + + + )} + {task.actionableInsights && ( + + + Actionable Insights + + + {task.actionableInsights} + + + )} ))} diff --git a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/TrendAnalysis.tsx b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/TrendAnalysis.tsx index 2498bf2f..7d0287b7 100644 --- a/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/TrendAnalysis.tsx +++ b/frontend/src/components/ContentPlanningDashboard/components/StrategyIntelligence/components/TrendAnalysis.tsx @@ -57,11 +57,51 @@ const TrendAnalysis: React.FC = ({ try { setLoading(true); - // Call the API to get trend data - const response = await strategyMonitoringApi.getTrendData(strategyId, timeRange); - setTrendData(response.data); + // Try to get trend data from monitoring data first + const monitoringData = localStorage.getItem('strategy_analytics_data'); + + if (monitoringData) { + const data = JSON.parse(monitoringData); + const performanceMetrics = data.performance_metrics; + + // Generate trend data from monitoring metrics + const trendData: TrendData[] = [ + { + date: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], // 30 days ago + traffic_growth: (performanceMetrics?.traffic_growth || 0) - 5, + engagement_rate: (performanceMetrics?.engagement_rate || 0) - 3, + conversion_rate: (performanceMetrics?.conversion_rate || 0) - 2, + content_quality_score: (performanceMetrics?.content_quality_score || 0) - 5, + strategy_adoption_rate: 75 + }, + { + date: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], // 15 days ago + traffic_growth: (performanceMetrics?.traffic_growth || 0) - 2, + engagement_rate: (performanceMetrics?.engagement_rate || 0) - 1, + conversion_rate: (performanceMetrics?.conversion_rate || 0) - 1, + content_quality_score: (performanceMetrics?.content_quality_score || 0) - 2, + strategy_adoption_rate: 80 + }, + { + date: new Date().toISOString().split('T')[0], // Today + traffic_growth: performanceMetrics?.traffic_growth || 0, + engagement_rate: performanceMetrics?.engagement_rate || 0, + conversion_rate: performanceMetrics?.conversion_rate || 0, + content_quality_score: performanceMetrics?.content_quality_score || 0, + strategy_adoption_rate: 85 + } + ]; + + setTrendData(trendData); + console.log('āœ… Trend data loaded from monitoring data'); + } else { + // Fallback to empty data if no monitoring data + console.log('āš ļø No monitoring data found, using empty trend data'); + setTrendData([]); + } } catch (error) { - console.error('Error loading trend data:', error); + console.warn('āš ļø Error loading trend data from monitoring data:', error); + setTrendData([]); } finally { setLoading(false); } diff --git a/frontend/src/components/ContentPlanningDashboard/tabs/AnalyticsTab.tsx b/frontend/src/components/ContentPlanningDashboard/tabs/AnalyticsTab.tsx index a6359b62..9863dca2 100644 --- a/frontend/src/components/ContentPlanningDashboard/tabs/AnalyticsTab.tsx +++ b/frontend/src/components/ContentPlanningDashboard/tabs/AnalyticsTab.tsx @@ -12,7 +12,8 @@ import { CircularProgress, LinearProgress, Tabs, - Tab + Tab, + Button } from '@mui/material'; import { TrendingUp as TrendingUpIcon, @@ -21,7 +22,8 @@ import { Assessment as AssessmentIcon, Visibility as VisibilityIcon, Timeline as TimelineIcon, - AutoAwesome as AutoAwesomeIcon + AutoAwesome as AutoAwesomeIcon, + Refresh as RefreshIcon } from '@mui/icons-material'; import { useContentPlanningStore } from '../../../stores/contentPlanningStore'; import { contentPlanningApi } from '../../../services/contentPlanningApi'; @@ -53,18 +55,18 @@ function TabPanel(props: TabPanelProps) { const AnalyticsTab: React.FC = () => { const { - performanceMetrics, - aiInsights, currentStrategy, - loading, - error, - loadAIInsights, - loadAIRecommendations + error: storeError } = useContentPlanningStore(); const [analyticsData, setAnalyticsData] = useState(null); const [dataLoading, setDataLoading] = useState(false); + const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState(0); + + // Cache for analytics data to prevent redundant calls + const [lastLoadTime, setLastLoadTime] = useState(0); + const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes useEffect(() => { loadAnalyticsData(); @@ -72,35 +74,89 @@ const AnalyticsTab: React.FC = () => { const loadAnalyticsData = async () => { try { + // Check if we have recent cached data to avoid redundant calls + const now = Date.now(); + if (analyticsData && (now - lastLoadTime) < CACHE_DURATION) { + console.log('šŸŽÆ Using cached analytics data (cache valid for', Math.round((CACHE_DURATION - (now - lastLoadTime)) / 1000), 'seconds)'); + return; + } + setDataLoading(true); + setError(null); - console.log('Loading analytics data...'); + console.log('šŸŽÆ Loading analytics data...'); - // Load AI insights and recommendations - await Promise.all([ - loadAIInsights(), - loadAIRecommendations() - ]); + // Get strategy ID + const strategyId = Number(currentStrategy?.id) || currentStrategy?.user_id || 1; - // Load analytics data from backend - const response = await contentPlanningApi.getAIAnalyticsSafe(); + // First, try to load from database (monitoring data) + try { + console.log('šŸŽÆ Fetching analytics data from database...'); + const response = await fetch(`/api/content-planning/strategy/${strategyId}/analytics-data`); + + if (response.ok) { + const result = await response.json(); + const dbAnalyticsData = result.data; + + console.log('āœ… Analytics data from database:', dbAnalyticsData); + setAnalyticsData(dbAnalyticsData); + setLastLoadTime(Date.now()); + return; + } else { + console.warn('āš ļø Database fetch failed, trying cache...'); + } + } catch (dbError) { + console.warn('āš ļø Database fetch error, trying cache:', dbError); + } - console.log('Analytics Response:', response); + // Fallback to cached monitoring data + const strategyAnalyticsData = localStorage.getItem('strategy_analytics_data'); + const monitoringTasks = localStorage.getItem('strategy_monitoring_tasks'); - if (response) { + console.log('šŸŽÆ Checking cached monitoring data...'); + + if (strategyAnalyticsData && monitoringTasks) { + console.log('āœ… Found cached monitoring data'); + + const cachedData = JSON.parse(strategyAnalyticsData); + const tasks = JSON.parse(monitoringTasks); + const analyticsData = { - performance_trends: response.performance_trends || {}, - content_evolution: response.content_evolution || {}, - engagement_patterns: response.engagement_patterns || {}, - recommendations: response.recommendations || [], - insights: response.insights || [] + performance_trends: cachedData.monitoring_plan?.performance_metrics || {}, + content_evolution: cachedData.monitoring_plan?.content_evolution || {}, + engagement_patterns: cachedData.monitoring_plan?.engagement_patterns || {}, + recommendations: cachedData.monitoring_plan?.recommendations || [], + insights: cachedData.monitoring_plan?.insights || [], + monitoring_data: cachedData, + monitoring_tasks: tasks, + _source: 'cached_monitoring' }; - console.log('Analytics Data:', analyticsData); + console.log('āœ… Analytics Data from cache:', analyticsData); setAnalyticsData(analyticsData); + setLastLoadTime(Date.now()); + + } else { + // No data available + console.log('āš ļø No monitoring data found in database or cache'); + const emptyData = { + performance_trends: {}, + content_evolution: {}, + engagement_patterns: {}, + recommendations: [], + insights: [], + monitoring_data: null, + monitoring_tasks: [], + _source: 'empty' + }; + + setAnalyticsData(emptyData); + setLastLoadTime(Date.now()); + setError('No monitoring data available. Please activate a strategy first.'); } } catch (error) { - console.error('Error loading analytics data:', error); + console.error('āŒ Error loading analytics data:', error); + setError('Failed to load analytics data. Please try again.'); } finally { setDataLoading(false); } @@ -110,6 +166,12 @@ const AnalyticsTab: React.FC = () => { setActiveTab(newValue); }; + const handleRefresh = () => { + console.log('šŸ”„ Manual refresh requested'); + setLastLoadTime(0); // Reset cache + loadAnalyticsData(); + }; + const getPerformanceColor = (value: number) => { if (value >= 80) return 'success'; if (value >= 60) return 'warning'; @@ -121,15 +183,48 @@ const AnalyticsTab: React.FC = () => { return ( - - Analytics Dashboard - + + + Analytics Dashboard + + + - {error && ( - - {error} - - )} + {(error || storeError) && ( + + {error || storeError} + + )} + + {/* Data Source Indicator */} + {analyticsData && ( + + } + > + Data source: {analyticsData._source === 'database_monitoring' ? 'Monitoring database' : + analyticsData._source === 'cached_monitoring' ? 'Local cache' : + analyticsData._source === 'empty' ? 'No monitoring data available' : 'Unknown source'} + + )} {/* Tabs Navigation */} @@ -191,38 +286,38 @@ const AnalyticsTab: React.FC = () => { - {performanceMetrics ? ( + {analyticsData && analyticsData.performance_trends ? ( Engagement Rate - - {performanceMetrics.engagement}% + + {analyticsData.performance_trends.engagement_rate || 0}% - Reach + Traffic Growth - {performanceMetrics.reach.toLocaleString()} + {analyticsData.performance_trends.traffic_growth || 0}% Conversion Rate - - {performanceMetrics.conversion}% + + {analyticsData.performance_trends.conversion_rate || 0}% - ROI + Content Quality - ${performanceMetrics.roi.toLocaleString()} + {analyticsData.performance_trends.content_quality_score || 0}/100 @@ -243,18 +338,18 @@ const AnalyticsTab: React.FC = () => { - {aiInsights && aiInsights.length > 0 ? ( + {analyticsData && analyticsData.insights && analyticsData.insights.length > 0 ? ( - {aiInsights.slice(0, 3).map((insight, index) => ( + {analyticsData.insights.slice(0, 3).map((insight: any, index: number) => ( - {insight.title} + {insight.title || `Insight ${index + 1}`} - {insight.description} + {insight.description || insight} @@ -263,7 +358,7 @@ const AnalyticsTab: React.FC = () => { ) : ( - No AI insights available + No insights available )} diff --git a/frontend/src/services/navigationOrchestrator.ts b/frontend/src/services/navigationOrchestrator.ts index 36484138..f3228a0d 100644 --- a/frontend/src/services/navigationOrchestrator.ts +++ b/frontend/src/services/navigationOrchestrator.ts @@ -220,7 +220,23 @@ class NavigationOrchestrator { strategicIntelligence: strategyData.strategicIntelligence || {} }; - // Navigate to calendar wizard + // Store strategy context for analytics page + this.preserveContext('strategy', strategyContext); + + // Navigate to analytics page first to show monitoring setup + const navigate = this.getNavigateFunction(); + if (navigate) { + navigate('/content-planning', { + state: { + activeTab: 2, // Analytics tab + strategyContext, + fromStrategyActivation: true, + showMonitoringSetup: true + } + }); + } + + // Also preserve context for calendar wizard navigation this.navigateToCalendarWizard(strategyId, strategyContext); } diff --git a/frontend/src/services/strategyMonitoringApi.ts b/frontend/src/services/strategyMonitoringApi.ts index 3727b001..26040a43 100644 --- a/frontend/src/services/strategyMonitoringApi.ts +++ b/frontend/src/services/strategyMonitoringApi.ts @@ -1,4 +1,4 @@ -import { apiClient } from '../api/client'; +import { apiClient, aiApiClient } from '../api/client'; import { useState } from 'react'; export interface MonitoringTask { @@ -154,7 +154,7 @@ export const strategyMonitoringApi = { */ async getTrendData(strategyId: number, timeRange: string = '30d'): Promise<{ success: boolean; data: any; message: string }> { try { - const response = await apiClient.get(`/api/content-planning/strategy/${strategyId}/trend-data?time_range=${timeRange}`); + const response = await aiApiClient.get(`/api/content-planning/strategy/${strategyId}/trend-data?time_range=${timeRange}`); return response.data; } catch (error: any) { console.error('Error getting trend data:', error); @@ -196,7 +196,7 @@ export const strategyMonitoringApi = { // Quality Analysis API methods async getQualityAnalysis(strategyId: number): Promise<{ success: boolean; data: any; message: string }> { try { - const response = await apiClient.post(`/api/content-planning/quality-analysis/${strategyId}/analyze`); + const response = await aiApiClient.post(`/api/content-planning/quality-analysis/${strategyId}/analyze`); return response.data; } catch (error: any) { console.error('Error fetching quality analysis:', error);