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. Removes the checkbox selector for a cleaner, always-visible design. Returns: gr.HTML: The Gradio HTML component that will display the formatted OKRs. """ # Custom CSS for modern OKR styling okr_custom_css = """ """ with gr.Column(elem_classes=["okr-root-column"]): # Inject custom CSS 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 format_okrs_for_enhanced_display(reconstruction_cache: dict) -> str: """ Enhanced formatting function that creates beautiful HTML for OKR display from the reconstruction cache dictionary. Args: reconstruction_cache (dict): The reconstruction cache containing reconstructed data. Expected to contain report data with 'actionable_okrs' key. 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() # Extract actionable_okrs from the first (and typically only) report in cache for report_id, report_data in reconstruction_cache.items(): if isinstance(report_data, dict) and 'actionable_okrs' in report_data: raw_results = {'actionable_okrs': report_data['actionable_okrs']} break else: logger.warning("No 'actionable_okrs' found in reconstruction cache for display.") return get_empty_okr_state() actionable_okrs = raw_results.get("actionable_okrs", {}) 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) total_tasks = sum( len(kr.get('tasks', [])) for okr in okrs_list for kr in okr.get('key_results', []) ) high_priority_tasks = sum( 1 for okr in okrs_list for kr in okr.get('key_results', []) for task in kr.get('tasks', []) if task.get('priority', '').lower() == 'high' ) # Build the HTML structure 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
"""] for okr_idx, okr_data in enumerate(okrs_list): if not isinstance(okr_data, dict): logger.warning(f"OKR item at index {okr_idx} is not a dictionary, skipping.") continue objective = okr_data.get('description', f"Unnamed Objective {okr_idx + 1}") timeline = okr_data.get('timeline', 'Not specified') owner = okr_data.get('owner', 'Not assigned') html_parts.append(f"""
Objective {okr_idx + 1}: {objective}
Timeline: {timeline}
👤 Owner: {owner}
""") key_results = okr_data.get('key_results', []) if not isinstance(key_results, list) or not key_results: html_parts.append('
No key results defined for this objective.
') else: for kr_idx, kr_data in enumerate(key_results): if not isinstance(kr_data, dict): logger.warning(f"Key Result item for Objective {okr_idx+1} at index {kr_idx} is not a dictionary, skipping.") continue kr_desc = kr_data.get('description', f"Unnamed Key Result {kr_idx + 1}") target_metric = kr_data.get('target_metric', '') target_value = kr_data.get('target_value', '') kr_type = kr_data.get('key_result_type', '') data_subject = kr_data.get('data_subject', '') html_parts.append(f"""
Key Result {kr_idx + 1}: {kr_desc}
""") if target_metric and target_value: html_parts.append(f'
Target: {target_metric} → {target_value}
') if kr_type: html_parts.append(f'
Type: {kr_type}
') if data_subject: html_parts.append(f'
Data Subject: {data_subject}
') html_parts.append('
') # Add tasks tasks = kr_data.get('tasks', []) if tasks and isinstance(tasks, list): html_parts.append("""
📋 Associated Tasks
""") for task_idx, task_data in enumerate(tasks): if not isinstance(task_data, dict): logger.warning(f"Task item for Key Result {kr_idx+1} at index {task_idx} is not a dictionary, skipping.") continue task_desc = task_data.get('description', f"Unnamed Task {task_idx + 1}") task_category = task_data.get('category', 'General') priority = task_data.get('priority', 'Medium').lower() effort = task_data.get('effort', 'Not specified') timeline = task_data.get('timeline', 'Not specified') responsible = task_data.get('responsible_party', 'Not assigned') priority_class = f"priority-{priority}" if priority in ['high', 'medium', 'low'] else 'priority-medium' html_parts.append(f"""
{task_idx + 1}. {task_desc}
{priority.upper()}
Category: {task_category}
Effort: {effort}
Timeline: {timeline}
Responsible: {responsible}
""") # Add additional details if available obj_deliverable = task_data.get('deliverable') success_criteria = task_data.get('success_criteria_metrics') why_proposed = task_data.get('why') priority_just = task_data.get('priority_justification') dependencies = task_data.get('dependencies') detail_lines = [] if obj_deliverable: detail_lines.append(f'Objective/Deliverable: {obj_deliverable}') if success_criteria: detail_lines.append(f'Success Metrics: {success_criteria}') if why_proposed: detail_lines.append(f'Rationale: {why_proposed}') if priority_just: detail_lines.append(f'Priority Justification: {priority_just}') if dependencies: detail_lines.append(f'Dependencies: {dependencies}') if detail_lines: html_parts.append('
') html_parts.append('
'.join(detail_lines)) html_parts.append('
') html_parts.append('
') # Close task-item html_parts.append('
') # Close tasks-section else: html_parts.append("""
📄
No tasks defined for this Key Result.
""") html_parts.append('
') # Close key-result html_parts.append('
') # Close key-results-container and okr-objective html_parts.append('
') # Close okr-content and okr-container return ''.join(html_parts) def get_empty_okr_state() -> str: """ Returns empty state HTML for when no OKRs are available. Returns: str: HTML string for the empty OKR state. """ 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.
"""