import gradio as gr import datetime from typing import Dict, List, Any, Union, Optional import random import os import json # Import utilities from utils.storage import load_data, save_data, safe_get from utils.state import generate_id, get_timestamp, record_activity from utils.ai_models import analyze_sentiment, summarize_text from utils.config import FILE_PATHS from utils.logging import setup_logger from utils.error_handling import handle_exceptions from utils.data_analysis import ( filter_data_by_time_period, create_completion_rate_chart, create_status_distribution_chart, create_priority_distribution_chart, create_time_series_chart, create_completion_time_chart, create_tags_distribution_chart, create_activity_heatmap, create_calendar_heatmap, create_sentiment_chart, create_model_usage_distribution, create_model_usage_over_time ) # Initialize logger logger = setup_logger(__name__) @handle_exceptions def create_analytics_page(state: Dict[str, Any]) -> None: """ Create the Analytics page with data visualizations and insights Args: state: Application state """ logger.info("Creating analytics page") # Create the analytics page layout with gr.Column(elem_id="analytics-page"): gr.Markdown("# 📊 Analytics") gr.Markdown("*Insights and visualizations of your productivity data*") # Time period selector with gr.Row(): time_period = gr.Dropdown( choices=["Last 7 Days", "Last 30 Days", "Last 90 Days", "All Time"], value="Last 30 Days", label="Time Period", elem_id="time-period-selector" ) refresh_btn = gr.Button("Refresh Data") # Dashboard tabs with gr.Tabs(): # Task Analytics with gr.TabItem("Tasks"): with gr.Row(): # Task completion rate with gr.Column(scale=1): task_completion_chart = gr.Plot( label="Task Completion Rate", elem_id="task-completion-chart" ) # Task status distribution with gr.Column(scale=1): task_status_chart = gr.Plot( label="Task Status Distribution", elem_id="task-status-chart" ) with gr.Row(): # Task priority distribution with gr.Column(scale=1): task_priority_chart = gr.Plot( label="Task Priority Distribution", elem_id="task-priority-chart" ) # Task creation over time with gr.Column(scale=1): task_creation_chart = gr.Plot( label="Task Creation Over Time", elem_id="task-creation-chart" ) with gr.Row(): # Task completion time with gr.Column(scale=1): task_completion_time_chart = gr.Plot( label="Average Completion Time", elem_id="task-completion-time-chart" ) # Task tags distribution with gr.Column(scale=1): task_tags_chart = gr.Plot( label="Task Tags Distribution", elem_id="task-tags-chart" ) # Notes Analytics with gr.TabItem("Notes"): with gr.Row(): # Notes creation over time with gr.Column(scale=1): notes_creation_chart = gr.Plot( label="Notes Creation Over Time", elem_id="notes-creation-chart" ) # Notes length distribution with gr.Column(scale=1): notes_length_chart = gr.Plot( label="Notes Length Distribution", elem_id="notes-length-chart" ) with gr.Row(): # Notes tags distribution with gr.Column(scale=1): notes_tags_chart = gr.Plot( label="Notes Tags Distribution", elem_id="notes-tags-chart" ) # Notes sentiment analysis with gr.Column(scale=1): notes_sentiment_chart = gr.Plot( label="Notes Sentiment Analysis", elem_id="notes-sentiment-chart" ) # Goals Analytics with gr.TabItem("Goals"): with gr.Row(): # Goal completion rate with gr.Column(scale=1): goal_completion_chart = gr.Plot( label="Goal Completion Rate", elem_id="goal-completion-chart" ) # Goal progress distribution with gr.Column(scale=1): goal_progress_chart = gr.Plot( label="Goal Progress Distribution", elem_id="goal-progress-chart" ) with gr.Row(): # Goal creation over time with gr.Column(scale=1): goal_creation_chart = gr.Plot( label="Goal Creation Over Time", elem_id="goal-creation-chart" ) # Goal completion time with gr.Column(scale=1): goal_completion_time_chart = gr.Plot( label="Average Goal Completion Time", elem_id="goal-completion-time-chart" ) # Activity Analytics with gr.TabItem("Activity"): with gr.Row(): # Activity by day of week with gr.Column(scale=1): activity_dow_chart = gr.Plot( label="Activity by Day of Week", elem_id="activity-dow-chart" ) # Activity by hour of day with gr.Column(scale=1): activity_hour_chart = gr.Plot( label="Activity by Hour of Day", elem_id="activity-hour-chart" ) with gr.Row(): # Activity by type with gr.Column(scale=1): activity_type_chart = gr.Plot( label="Activity by Type", elem_id="activity-type-chart" ) # Activity over time with gr.Column(scale=1): activity_time_chart = gr.Plot( label="Activity Over Time", elem_id="activity-time-chart" ) # AI Usage Analytics with gr.TabItem("AI Usage"): with gr.Row(): # AI model usage distribution with gr.Column(scale=1): ai_model_chart = gr.Plot( label="AI Model Usage Distribution", elem_id="ai-model-chart" ) # AI usage over time with gr.Column(scale=1): ai_usage_chart = gr.Plot( label="AI Usage Over Time", elem_id="ai-usage-chart" ) # Key metrics with gr.Row(): with gr.Column(scale=1): gr.Markdown("### Key Metrics") metrics_md = gr.Markdown("*Loading metrics...*") # Function to update metrics @handle_exceptions def update_metrics(period): """Update metrics based on selected time period""" logger.debug(f"Updating metrics for period: {period}") # Get data tasks = safe_get(state, "tasks", []) notes = safe_get(state, "notes", []) goals = safe_get(state, "goals", []) activity = safe_get(state, "activity_feed", []) # Calculate metrics total_tasks = len(tasks) completed_tasks = len([t for t in tasks if safe_get(t, "completed", False)]) total_notes = len(notes) total_goals = len(goals) completed_goals = len([g for g in goals if safe_get(g, "completed", False)]) total_activity = len(activity) # Format metrics metrics = [ f"**Total Tasks:** {total_tasks}", f"**Completed Tasks:** {completed_tasks}", f"**Task Completion Rate:** {(completed_tasks/total_tasks*100):.1f}% if total_tasks > 0 else '0%'", f"**Total Notes:** {total_notes}", f"**Total Goals:** {total_goals}", f"**Goal Completion Rate:** {(completed_goals/total_goals*100):.1f}% if total_goals > 0 else '0%'", f"**Total Activities:** {total_activity}" ] return "\n\n".join(metrics) # Function to update all charts @handle_exceptions def update_charts(period): """Update all charts based on selected time period""" logger.debug(f"Updating charts for period: {period}") # Get data tasks = safe_get(state, "tasks", []) notes = safe_get(state, "notes", []) goals = safe_get(state, "goals", []) activity = safe_get(state, "activity_feed", []) # Filter data by time period filtered_tasks = filter_data_by_time_period(tasks, period) filtered_notes = filter_data_by_time_period(notes, period) filtered_goals = filter_data_by_time_period(goals, period) filtered_activity = filter_data_by_time_period(activity, period) # Update task charts task_completion_fig = create_completion_rate_chart( filtered_tasks, title="Task Completion Rate", completed_key="completed" ) task_status_fig = create_status_distribution_chart( filtered_tasks, title="Task Status Distribution" ) task_priority_fig = create_priority_distribution_chart( filtered_tasks, title="Task Priority Distribution" ) task_creation_fig = create_time_series_chart( filtered_tasks, title="Task Creation Over Time", timestamp_key="created_at" ) task_completion_time_fig = create_completion_time_chart( filtered_tasks, title="Task Completion Time Distribution", created_key="created_at", completed_key="completed_at" ) task_tags_fig = create_tags_distribution_chart( filtered_tasks, title="Task Tags Distribution", tags_key="tags" ) # Update notes charts notes_creation_fig = create_time_series_chart( filtered_notes, title="Notes Creation Over Time", timestamp_key="created_at" ) # Create notes length distribution chart notes_with_length = [] for note in filtered_notes: content = safe_get(note, "content", "") if content: notes_with_length.append({ **note, "length": len(content) }) # Sort notes by length notes_with_length.sort(key=lambda x: x["length"]) # Create a simple bar chart for notes length import plotly.graph_objects as go notes_length_fig = go.Figure(data=go.Bar( x=[i for i in range(len(notes_with_length))], y=[note["length"] for note in notes_with_length], marker_color="#4CAF50" )) notes_length_fig.update_layout( title="Notes Length Distribution", xaxis_title="Note Index", yaxis_title="Character Count", margin=dict(l=20, r=20, t=40, b=20), height=300 ) notes_tags_fig = create_tags_distribution_chart( filtered_notes, title="Notes Tags Distribution", tags_key="tags" ) notes_sentiment_fig = create_sentiment_chart( filtered_notes, title="Notes Sentiment Analysis", content_key="content", timestamp_key="created_at" ) # Update goals charts goal_completion_fig = create_completion_rate_chart( filtered_goals, title="Goal Completion Rate", completed_key="completed" ) # Create goal progress distribution chart # This is a placeholder - you might want to implement a more specific chart goal_progress_fig = create_status_distribution_chart( filtered_goals, title="Goal Progress Distribution", status_key="status" ) goal_creation_fig = create_time_series_chart( filtered_goals, title="Goal Creation Over Time", timestamp_key="created_at" ) goal_completion_time_fig = create_completion_time_chart( filtered_goals, title="Goal Completion Time Distribution", created_key="created_at", completed_key="completed_at" ) # Update activity charts activity_dow_hour_fig = create_activity_heatmap( filtered_activity, title="Activity by Day and Hour", timestamp_key="timestamp" ) # Split the heatmap into two separate charts for day of week and hour of day import numpy as np import plotly.graph_objects as go # Day of week activity day_counts = np.zeros(7) # 7 days for item in filtered_activity: timestamp = item.get("timestamp") if not timestamp: continue # Convert timestamp to datetime if isinstance(timestamp, str): try: date = datetime.datetime.fromisoformat(timestamp.replace('Z', '+00:00')) except ValueError: continue else: date = datetime.datetime.fromtimestamp(timestamp) # Get day of week (0 = Monday, 6 = Sunday) day_of_week = date.weekday() day_counts[day_of_week] += 1 days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] activity_dow_fig = go.Figure(data=go.Bar( x=days, y=day_counts, marker_color="#2196F3" )) activity_dow_fig.update_layout( title="Activity by Day of Week", xaxis_title="Day", yaxis_title="Count", margin=dict(l=20, r=20, t=40, b=20), height=300 ) # Hour of day activity hour_counts = np.zeros(24) # 24 hours for item in filtered_activity: timestamp = item.get("timestamp") if not timestamp: continue # Convert timestamp to datetime if isinstance(timestamp, str): try: date = datetime.datetime.fromisoformat(timestamp.replace('Z', '+00:00')) except ValueError: continue else: date = datetime.datetime.fromtimestamp(timestamp) # Get hour hour = date.hour hour_counts[hour] += 1 hours = [f"{h:02d}:00" for h in range(24)] activity_hour_fig = go.Figure(data=go.Bar( x=hours, y=hour_counts, marker_color="#9C27B0" )) activity_hour_fig.update_layout( title="Activity by Hour of Day", xaxis_title="Hour", yaxis_title="Count", margin=dict(l=20, r=20, t=40, b=20), height=300 ) # Activity by type activity_types = {} for item in filtered_activity: activity_type = safe_get(item, "type", "unknown") # Clean up the activity type for better display display_type = activity_type.replace("_", " ").title() activity_types[display_type] = activity_types.get(display_type, 0) + 1 # Sort by count sorted_types = sorted(activity_types.items(), key=lambda x: x[1], reverse=True) types = [t[0] for t in sorted_types[:10]] # Top 10 types type_counts = [t[1] for t in sorted_types[:10]] activity_type_fig = go.Figure(data=go.Bar( x=types, y=type_counts, marker_color="#FF9800" )) activity_type_fig.update_layout( title="Activity by Type", xaxis_title="Type", yaxis_title="Count", margin=dict(l=20, r=20, t=40, b=20), height=300 ) # Activity over time activity_time_fig = create_time_series_chart( filtered_activity, title="Activity Over Time", timestamp_key="timestamp" ) # Update AI usage charts ai_model_fig = create_model_usage_distribution( filtered_activity, title="AI Model Usage Distribution" ) ai_usage_fig = create_model_usage_over_time( filtered_activity, title="AI Usage Over Time", timestamp_key="timestamp" ) # Return all updated charts return ( task_completion_fig, task_status_fig, task_priority_fig, task_creation_fig, task_completion_time_fig, task_tags_fig, notes_creation_fig, notes_length_fig, notes_tags_fig, notes_sentiment_fig, goal_completion_fig, goal_progress_fig, goal_creation_fig, goal_completion_time_fig, activity_dow_fig, activity_hour_fig, activity_type_fig, activity_time_fig, ai_model_fig, ai_usage_fig ) # Set up refresh button refresh_btn.click( fn=lambda period: (update_metrics(period), *update_charts(period)), inputs=[time_period], outputs=[ metrics_md, task_completion_chart, task_status_chart, task_priority_chart, task_creation_chart, task_completion_time_chart, task_tags_chart, notes_creation_chart, notes_length_chart, notes_tags_chart, notes_sentiment_chart, goal_completion_chart, goal_progress_chart, goal_creation_chart, goal_completion_time_chart, activity_dow_chart, activity_hour_chart, activity_type_chart, activity_time_chart, ai_model_chart, ai_usage_chart ] ) # Initialize metrics and charts metrics_md.value = update_metrics("Last 30 Days") ( task_completion_chart.value, task_status_chart.value, task_priority_chart.value, task_creation_chart.value, task_completion_time_chart.value, task_tags_chart.value, notes_creation_chart.value, notes_length_chart.value, notes_tags_chart.value, notes_sentiment_chart.value, goal_completion_chart.value, goal_progress_chart.value, goal_creation_chart.value, goal_completion_time_chart.value, activity_dow_chart.value, activity_hour_chart.value, activity_type_chart.value, activity_time_chart.value, ai_model_chart.value, ai_usage_chart.value ) = update_charts("Last 30 Days")