import gradio as gr import pandas as pd import os import logging from collections import defaultdict import matplotlib matplotlib.use('Agg') # Set backend for Matplotlib # --- Module Imports --- from utils.gradio_utils import get_url_user_token # Functions from newly created/refactored modules from config import ( PLOT_ID_TO_FORMULA_KEY_MAP, LINKEDIN_CLIENT_ID_ENV_VAR, BUBBLE_APP_NAME_ENV_VAR, BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR ) # UPDATED: Using the new data loading function from the refactored state manager from services.state_manager import load_data_from_bubble from ui.ui_generators import ( # display_main_dashboard, # Removed: Dashboard content is now in app.py directly build_analytics_tab_plot_area, BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON ) from ui.analytics_plot_generator import update_analytics_plots_figures, create_placeholder_plot from formulas import PLOT_FORMULAS # --- CHATBOT MODULE IMPORTS --- from features.chatbot.chatbot_prompts import get_initial_insight_prompt_and_suggestions from features.chatbot.chatbot_handler import generate_llm_response # --- AGENTIC PIPELINE (DISPLAY ONLY) IMPORTS --- try: # This is the main function called on initial load to populate the agentic tabs from run_agentic_pipeline import load_and_display_agentic_results # This function is now called when a new report is selected from the dropdown from services.report_data_handler import fetch_and_reconstruct_data_from_bubble # UI formatting functions from ui.insights_ui_generator import ( format_report_for_display, extract_key_results_for_selection, format_single_okr_for_display ) AGENTIC_MODULES_LOADED = True except ImportError as e: logging.error(f"Could not import agentic pipeline display modules: {e}. Tabs 3 and 4 will be disabled.") AGENTIC_MODULES_LOADED = False # Placeholder functions to prevent app from crashing if imports fail def load_and_display_agentic_results(*args, **kwargs): return "Modules not loaded.", gr.update(), "Modules not loaded.", "Modules not loaded.", None, [], [], "Error", {} def fetch_and_reconstruct_data_from_bubble(*args, **kwargs): return None, {} def format_report_for_display(report_data): return "Agentic modules not loaded. Report display unavailable." def extract_key_results_for_selection(okr_data): return [] def format_single_okr_for_display(okr_data, **kwargs): return "Agentic modules not loaded. OKR display unavailable." # --- ANALYTICS TAB MODULE IMPORT --- from services.analytics_tab_module import AnalyticsTab # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s') # API Key Setup user_provided_api_key = os.environ.get("GEMINI_API_KEY") if user_provided_api_key: os.environ["GOOGLE_API_KEY"] = user_provided_api_key logging.info("GOOGLE_API_KEY environment variable has been set from GEMINI_API_KEY.") else: logging.error("CRITICAL ERROR: The API key environment variable 'GEMINI_API_KEY' was not found.") with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"), title="LinkedIn Organization Dashboard") as app: # --- STATE MANAGEMENT --- token_state = gr.State(value={ "token": None, "client_id": None, "org_urn": None, "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(), "bubble_mentions_df": pd.DataFrame(), "bubble_follower_stats_df": pd.DataFrame(), "bubble_agentic_analysis_data": pd.DataFrame(), # To store agentic results from Bubble "url_user_token_temp_storage": None, "config_date_col_posts": "published_at", "config_date_col_mentions": "date", "config_date_col_followers": "date", "config_media_type_col": "media_type", "config_eb_labels_col": "li_eb_label" }) # States for analytics tab chatbot chat_histories_st = gr.State({}) current_chat_plot_id_st = gr.State(None) plot_data_for_chatbot_st = gr.State({}) # States for agentic results display orchestration_raw_results_st = gr.State(None) key_results_for_selection_st = gr.State([]) selected_key_result_ids_st = gr.State([]) # --- NEW: Session-specific cache for reconstructed OKR data --- reconstruction_cache_st = gr.State({}) # --- UI LAYOUT --- gr.Markdown("# ๐ LinkedIn Organization Dashboard") url_user_token_display = gr.Textbox(label="User Token (Hidden)", interactive=False, visible=False) org_urn_display = gr.Textbox(label="Org URN (Hidden)", interactive=False, visible=False) status_box = gr.Textbox(label="Status", interactive=False, value="Initializing...") app.load(fn=get_url_user_token, inputs=None, outputs=[url_user_token_display, org_urn_display], api_name="get_url_params", show_progress=False) def initial_data_load_sequence(url_token, org_urn_val, current_state): """ Handles the initial data loading from Bubble. No longer generates dashboard HTML as the Home tab is now static. """ status_msg, new_state = load_data_from_bubble(url_token, org_urn_val, current_state) # dashboard_content = display_main_dashboard(new_state) # Removed this line return status_msg, new_state # Removed dashboard_content from outputs analytics_icons = {'bomb': BOMB_ICON, 'explore': EXPLORE_ICON, 'formula': FORMULA_ICON, 'active': ACTIVE_ICON} analytics_tab_instance = AnalyticsTab( token_state=token_state, chat_histories_st=chat_histories_st, current_chat_plot_id_st=current_chat_plot_id_st, plot_data_for_chatbot_st=plot_data_for_chatbot_st, plot_id_to_formula_map=PLOT_ID_TO_FORMULA_KEY_MAP, plot_formulas_data=PLOT_FORMULAS, icons=analytics_icons, fn_build_plot_area=build_analytics_tab_plot_area, fn_update_plot_figures=update_analytics_plots_figures, fn_create_placeholder_plot=create_placeholder_plot, fn_get_initial_insight=get_initial_insight_prompt_and_suggestions, fn_generate_llm_response=generate_llm_response ) # --- FIXED: New handler only updates the report display --- def update_report_display(selected_report_id: str, current_token_state: dict): """ Updates only the report display markdown when a new report is selected. The OKR visualization remains unchanged as it's loaded initially. """ if not selected_report_id: return gr.update(value="*Please select a report to view its details.*") agentic_df = current_token_state.get("bubble_agentic_analysis_data") if agentic_df is None or agentic_df.empty: return gr.update(value="*Analysis data not loaded or is empty.*") selected_report_series_df = agentic_df[agentic_df['_id'] == selected_report_id] if selected_report_series_df.empty: return gr.update(value=f"*Error: Report with ID '{selected_report_id}' not found.*") # Extract the report data and format it for display selected_report_series = selected_report_series_df.iloc[0] report_markdown = format_report_for_display(selected_report_series) return report_markdown with gr.Tabs() as tabs: # --- NEW HOME TAB --- with gr.TabItem("1๏ธโฃ Home", id="tab_home"): gr.Markdown("""
This powerful tool is designed to help you **measure and enhance your employer brand** on LinkedIn. By leveraging comprehensive analytics, you can dive into your data to understand trends, track performance, and gain actionable insights to improve your presence and attractiveness as an employer.
Explore the sections below to get a comprehensive overview of your LinkedIn presence and unlock the full potential of your employer branding efforts.
Dive into detailed visualizations of your LinkedIn data. This section provides dynamic charts and interactive plots that help you understand trends and variations in posts, mentions, and follower statistics over time. Identify patterns and make data-driven decisions.
Access comprehensive quarterly and weekly reports of your employer brand performance. These pre-generated reports offer in-depth summaries and insights, providing a clear snapshot of your progress over specific periods.
Discover Objectives and Key Results (OKRs) generated by AI, along with actionable tasks. This section provides concrete recommendations tailored to improve your employer brand, helping you translate insights into measurable actions.