import gradio as gr from typing import Dict, Any, List, Optional import pandas as pd import logging logger = logging.getLogger(__name__) def create_enhanced_okr_tab(): """ Creates a modern, visually appealing OKR tab with improved layout and styling. This version includes robust support for Gradio's dark mode with multiple detection methods and fallback mechanisms. Returns: gr.HTML: The Gradio HTML component that will display the formatted OKRs. """ # Enhanced CSS for modern OKR styling with improved Dark Mode support okr_custom_css = """ """ with gr.Column(elem_classes=["okr-root-column"]): # Inject custom CSS and the enhanced theme-syncing JS gr.HTML(okr_custom_css) # Main OKR display area with enhanced styling okr_display_html = gr.HTML( value=get_initial_okr_display(), elem_classes=["okr-display"] ) return okr_display_html def get_initial_okr_display() -> str: """ Returns the initial HTML display for the OKR tab, showing a loading state. Returns: str: HTML string for the initial OKR display. """ return """
🎯 AI-Generated OKRs & Strategic Tasks
Intelligent objectives and key results based on your LinkedIn analytics
-
Objectives
-
Key Results
-
Tasks
-
High Priority
Loading OKR Analysis
Generating intelligent objectives and actionable tasks from your LinkedIn data...
""" def get_empty_okr_state() -> str: """Returns empty state HTML for when no OKRs are available, using the new styles.""" return """
🎯 AI-Generated OKRs & Strategic Tasks
Intelligent objectives and key results based on your LinkedIn analytics
0
Objectives
0
Key Results
0
Tasks
0
High Priority
📋
No OKRs Available
OKR analysis has not been generated yet or no data is available.
Please ensure your LinkedIn data has been loaded and the AI analysis has completed.
""" def format_okrs_for_enhanced_display(reconstruction_cache: dict) -> str: """ Enhanced formatting function that creates beautiful HTML for OKR display from the reconstruction cache dictionary, compatible with new styling. Args: reconstruction_cache (dict): The dictionary containing 'actionable_okrs'. Returns: str: A comprehensive HTML string representing the OKRs, or an empty state HTML. """ if not reconstruction_cache: logger.warning("No reconstruction cache found for display.") return get_empty_okr_state() actionable_okrs = {} # Extract actionable_okrs from the cache for report_id, report_data in reconstruction_cache.items(): if isinstance(report_data, dict) and 'actionable_okrs' in report_data: actionable_okrs = report_data['actionable_okrs'] break if not actionable_okrs: logger.warning("No 'actionable_okrs' found in reconstruction cache for display.") return get_empty_okr_state() okrs_list = actionable_okrs.get("okrs", []) if not okrs_list: logger.info("No OKRs found in 'actionable_okrs' list.") return get_empty_okr_state() # Calculate statistics for the stats bar total_objectives = len(okrs_list) total_key_results = sum(len(okr.get('key_results', [])) for okr in okrs_list if isinstance(okr, dict)) total_tasks = sum( len(kr.get('tasks', [])) for okr in okrs_list if isinstance(okr, dict) for kr in okr.get('key_results', []) if isinstance(kr, dict) ) high_priority_tasks = sum( 1 for okr in okrs_list if isinstance(okr, dict) for kr in okr.get('key_results', []) if isinstance(kr, dict) for task in kr.get('tasks', []) if isinstance(task, dict) and task.get('priority', '').lower() == 'high' ) # --- Start HTML Generation --- html_parts = [f"""
🎯AI-Generated OKRs & Strategic Tasks
Intelligent objectives and key results based on your LinkedIn analytics
{total_objectives}
Objectives
{total_key_results}
Key Results
{total_tasks}
Tasks
{high_priority_tasks}
High Priority
"""] # --- Loop through Objectives --- for okr_idx, okr_data in enumerate(okrs_list): if not isinstance(okr_data, dict): continue objective = okr_data.get('description', f"Unnamed Objective {okr_idx + 1}") timeline = okr_data.get('timeline', 'N/A') owner = okr_data.get('owner', 'N/A') html_parts.append(f"""
Objective {okr_idx + 1}: {objective}
Timeline: {timeline}
👤 Owner: {owner}
""") key_results = okr_data.get('key_results', []) if not key_results: html_parts.append('
No Key Results
No key results defined for this objective.
') else: # --- Loop through Key Results --- for kr_idx, kr_data in enumerate(key_results): if not isinstance(kr_data, dict): continue kr_desc = kr_data.get('description', f"Unnamed Key Result {kr_idx + 1}") html_parts.append(f"""
Key Result {kr_idx + 1}: {kr_desc}
""") if kr_data.get('target_metric') and kr_data.get('target_value'): html_parts.append(f'
Target: {kr_data.get("target_metric")} → {kr_data.get("target_value")}
') if kr_data.get('key_result_type'): html_parts.append(f'
Type: {kr_data.get("key_result_type")}
') if kr_data.get('data_subject'): html_parts.append(f'
Data Subject: {kr_data.get("data_subject")}
') html_parts.append('
') # Close kr-metrics & kr-header tasks = kr_data.get('tasks', []) html_parts.append('
') if tasks: html_parts.append('
📋Associated Tasks
') # --- Loop through Tasks --- for task_idx, task_data in enumerate(tasks): if not isinstance(task_data, dict): continue task_desc = task_data.get('description', f"Unnamed Task {task_idx + 1}") priority = task_data.get('priority', 'Medium').lower() priority_class = f"priority-{priority}" html_parts.append(f"""
{task_idx + 1}. {task_desc}
{priority.upper()}
Category:{task_data.get('category', 'N/A')}
Effort:{task_data.get('effort', 'N/A')}
Timeline:{task_data.get('timeline', 'N/A')}
Responsible:{task_data.get('responsible_party', 'N/A')}
""") detail_items = { 'Deliverable': task_data.get('deliverable'), 'Success Metrics': task_data.get('success_criteria_metrics'), 'Why': task_data.get('why'), 'Priority Justification': task_data.get('priority_justification'), 'Dependencies': task_data.get('dependencies') } detail_lines = [f'{k}: {v}' for k, v in detail_items.items() if v] if detail_lines: html_parts.append(f'
{"
".join(detail_lines)}
') html_parts.append('
') # Close task-item else: html_parts.append('
No tasks defined for this key result.
') html_parts.append('
') # Close tasks-section & key-result html_parts.append('
') # Close key-results-container & okr-objective html_parts.append('
') # Close okr-content & okr-container return ''.join(html_parts)