|
import gradio as gr |
|
import pandas as pd |
|
import os |
|
import logging |
|
from collections import defaultdict |
|
import matplotlib |
|
matplotlib.use('Agg') |
|
|
|
|
|
from utils.gradio_utils import get_url_user_token |
|
|
|
|
|
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 |
|
) |
|
|
|
from services.analytics_tab_module import AnalyticsTab |
|
|
|
|
|
from services.state_manager import load_data_from_bubble |
|
from ui.ui_generators import ( |
|
build_analytics_tab_plot_area, |
|
build_home_tab_ui, |
|
create_enhanced_report_tab, |
|
BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON |
|
) |
|
|
|
|
|
from ui.config import custom_title_css |
|
|
|
|
|
from ui.okr_ui_generator import create_enhanced_okr_tab, format_okrs_for_enhanced_display, get_initial_okr_display |
|
from ui.analytics_plot_generator import update_analytics_plots_figures, create_placeholder_plot |
|
from formulas import PLOT_FORMULAS |
|
|
|
|
|
from features.chatbot.chatbot_prompts import get_initial_insight_prompt_and_suggestions |
|
from features.chatbot.chatbot_handler import generate_llm_response |
|
|
|
|
|
try: |
|
|
|
from run_agentic_pipeline import load_and_display_agentic_results |
|
|
|
from services.report_data_handler import fetch_and_reconstruct_data_from_bubble |
|
|
|
from ui.insights_ui_generator import ( |
|
format_report_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 |
|
|
|
def load_and_display_agentic_results(*args, **kwargs): |
|
|
|
|
|
empty_header_html = """ |
|
<div class="report-title">π Comprehensive Analysis Report</div> |
|
<div class="report-subtitle">AI-Generated Insights from Your LinkedIn Data</div> |
|
<div class="status-badge">Generated from Bubble.io</div> |
|
""" |
|
empty_body_markdown = """ |
|
<div class="empty-state"> |
|
<div class="empty-state-icon">π</div> |
|
<div class="empty-state-title">No Report Selected</div> |
|
<div class="empty-state-description"> |
|
Please select a report from the library above to view its detailed analysis and insights. |
|
</div> |
|
</div> |
|
""" |
|
|
|
|
|
|
|
return ( |
|
gr.update(value="Modules not loaded."), |
|
gr.update(choices=[], value=None), |
|
gr.update(choices=[], value=[]), |
|
gr.update(value="Modules not loaded."), |
|
None, |
|
[], |
|
[], |
|
gr.update(value=empty_header_html), |
|
gr.update(value=empty_body_markdown), |
|
{}, |
|
gr.update(value=get_initial_okr_display()), |
|
gr.update(value={}) |
|
) |
|
def fetch_and_reconstruct_data_from_bubble(*args, **kwargs): |
|
return None, {} |
|
def format_report_for_display(report_data): |
|
|
|
return {'header_html': '<h1>Agentic modules not loaded.</h1>', 'body_markdown': 'Report display unavailable.'} |
|
|
|
|
|
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"), |
|
title="LinkedIn Organization Dashboard") as app: |
|
|
|
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(), |
|
"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" |
|
}) |
|
|
|
|
|
chat_histories_st = gr.State({}) |
|
current_chat_plot_id_st = gr.State(None) |
|
plot_data_for_chatbot_st = gr.State({}) |
|
|
|
|
|
orchestration_raw_results_st = gr.State(None) |
|
|
|
key_results_for_selection_st = gr.State([]) |
|
selected_key_result_ids_st = gr.State([]) |
|
|
|
|
|
reconstruction_cache_st = gr.State({}) |
|
|
|
actionable_okrs_data_st = gr.State({}) |
|
|
|
|
|
|
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
gr.HTML(""" |
|
<div style=" |
|
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); |
|
border-radius: 15px; |
|
padding: 40px 20px; |
|
margin: 20px 0; |
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); |
|
border: 2px solid #e2e8f0; |
|
text-align: center; |
|
"> |
|
<div style=" |
|
font-size: 48px; |
|
font-weight: 800; |
|
color: #0077B5; |
|
margin: 0 0 15px 0; |
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.1); |
|
"> |
|
<span style="font-size: 40px; margin-right: 10px;">π</span>LinkedIn Organization Dashboard |
|
</div> |
|
<div style=" |
|
font-size: 18px; |
|
color: #666666; |
|
margin: 0; |
|
font-weight: 400; |
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
|
line-height: 1.4; |
|
"> |
|
Advanced Analytics & AI-Powered Insights for Your LinkedIn Performance |
|
</div> |
|
</div> |
|
""") |
|
|
|
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="", visible=False) |
|
|
|
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 and controls status_box visibility. |
|
""" |
|
status_msg, new_state = load_data_from_bubble(url_token, org_urn_val, current_state) |
|
|
|
|
|
|
|
|
|
show_status_box = False |
|
if new_state.get("token") is None: |
|
show_status_box = True |
|
elif "Error" in status_msg or "Warning" in status_msg: |
|
show_status_box = True |
|
|
|
|
|
return gr.update(value=status_msg, visible=show_status_box), new_state |
|
|
|
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 |
|
) |
|
|
|
def update_report_display(selected_report_id: str, current_token_state: dict): |
|
""" |
|
Updates the report header and body display when a new report is selected. |
|
This function now expects format_report_for_display to return a dict with |
|
'header_html' and 'body_markdown'. |
|
""" |
|
|
|
empty_header_html = """ |
|
<div class="report-title">π Comprehensive Analysis Report</div> |
|
<div class="report-subtitle">AI-Generated Insights from Your LinkedIn Data</div> |
|
<div class="status-badge">Generated from Bubble.io</div> |
|
""" |
|
empty_body_markdown_no_selection = """ |
|
<div class="empty-state"> |
|
<div class="empty-state-icon">π</div> |
|
<div class="empty-state-title">Select a Report</div> |
|
<div class="empty-state-description"> |
|
Choose a report from the dropdown above to view its detailed analysis and insights. |
|
</div> |
|
</div> |
|
""" |
|
empty_body_markdown_no_data = """ |
|
<div class="empty-state"> |
|
<div class="empty-state-icon">β οΈ</div> |
|
<div class="empty-state-title">Data Not Available</div> |
|
<div class="empty-state-description"> |
|
Analysis data is not loaded or is empty. Please try refreshing the page. |
|
</div> |
|
</div> |
|
""" |
|
empty_body_markdown_not_found = lambda _id: f""" |
|
<div class="empty-state"> |
|
<div class="empty-state-icon">β</div> |
|
<div class="empty-state-title">Report Not Found</div> |
|
<div class="empty-state-description"> |
|
Report with ID '{_id}' was not found in the database. |
|
</div> |
|
</div> |
|
""" |
|
|
|
if not selected_report_id: |
|
|
|
return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_no_selection) |
|
|
|
agentic_df = current_token_state.get("bubble_agentic_analysis_data") |
|
if agentic_df is None or agentic_df.empty: |
|
|
|
return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_no_data) |
|
|
|
selected_report_series_df = agentic_df[agentic_df['_id'] == selected_report_id] |
|
if selected_report_series_df.empty: |
|
|
|
return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_not_found(selected_report_id)) |
|
|
|
selected_report_series = selected_report_series_df.iloc[0] |
|
|
|
|
|
formatted_content_parts = format_report_for_display(selected_report_series) |
|
|
|
|
|
return ( |
|
gr.update(value=formatted_content_parts['header_html']), |
|
gr.update(value=formatted_content_parts['body_markdown']) |
|
) |
|
|
|
|
|
with gr.Tabs() as tabs: |
|
|
|
with gr.TabItem("π Home", id="tab_home"): |
|
|
|
|
|
btn_graphs, btn_reports, btn_okr, btn_help = build_home_tab_ui() |
|
|
|
|
|
btn_graphs.click(fn=lambda: gr.update(selected="tab_analytics_module"), outputs=tabs) |
|
btn_reports.click(fn=lambda: gr.update(selected="tab_agentic_report"), outputs=tabs) |
|
btn_okr.click(fn=lambda: gr.update(selected="tab_agentic_okrs"), outputs=tabs) |
|
|
|
|
|
|
|
analytics_tab_instance.create_tab_ui() |
|
|
|
|
|
|
|
|
|
with gr.TabItem("π AI Analysis Reports", id="tab_agentic_report", visible=AGENTIC_MODULES_LOADED): |
|
|
|
|
|
|
|
agentic_pipeline_status_md, report_selector_dd, report_header_html_display, report_body_markdown_display = \ |
|
create_enhanced_report_tab(AGENTIC_MODULES_LOADED) |
|
|
|
|
|
with gr.TabItem("π― OKRs & Action Items", id="tab_agentic_okrs", visible=AGENTIC_MODULES_LOADED): |
|
|
|
gr.Markdown("## π― AI Generated OKRs and Actionable Tasks (from Bubble.io)") |
|
gr.Markdown("Basato sull'analisi AI, l'agente ha proposto i seguenti OKR.") |
|
|
|
if not AGENTIC_MODULES_LOADED: |
|
gr.Markdown("π΄ **Error:** Agentic modules could not be loaded.") |
|
|
|
|
|
with gr.Column(visible=False): |
|
gr.Markdown("### Suggested Key Results (OLD UI - HIDDEN)") |
|
key_results_cbg = gr.CheckboxGroup(label="Select Key Results", choices=[], value=[], interactive=True) |
|
gr.Markdown("### Detailed OKRs and Tasks (OLD UI - HIDDEN)") |
|
okr_detail_display_md = gr.Markdown("I dettagli OKR appariranno qui.") |
|
|
|
|
|
enhanced_okr_display_html = create_enhanced_okr_tab() |
|
|
|
|
|
|
|
|
|
if AGENTIC_MODULES_LOADED: |
|
report_selector_dd.change( |
|
fn=update_report_display, |
|
|
|
inputs=[report_selector_dd, token_state], |
|
outputs=[report_header_html_display, report_body_markdown_display], |
|
show_progress="minimal" |
|
) |
|
|
|
|
|
|
|
agentic_display_outputs = [ |
|
agentic_pipeline_status_md, |
|
report_selector_dd, |
|
key_results_cbg, |
|
okr_detail_display_md, |
|
orchestration_raw_results_st, |
|
selected_key_result_ids_st, |
|
key_results_for_selection_st, |
|
report_header_html_display, |
|
report_body_markdown_display, |
|
reconstruction_cache_st, |
|
enhanced_okr_display_html, |
|
actionable_okrs_data_st |
|
] |
|
|
|
|
|
initial_load_event = org_urn_display.change( |
|
fn=lambda: gr.update(value="Loading data...", visible=True), |
|
inputs=[], |
|
outputs=[status_box], |
|
show_progress="full" |
|
).then( |
|
fn=initial_data_load_sequence, |
|
inputs=[url_user_token_display, org_urn_display, token_state], |
|
outputs=[status_box, token_state], |
|
show_progress="full" |
|
) |
|
|
|
initial_load_event.then( |
|
fn=analytics_tab_instance._refresh_analytics_graphs_ui, |
|
inputs=[token_state, analytics_tab_instance.date_filter_selector, analytics_tab_instance.custom_start_date_picker, |
|
analytics_tab_instance.custom_end_date_picker, chat_histories_st], |
|
outputs=analytics_tab_instance.graph_refresh_outputs_list, |
|
show_progress="full" |
|
).then( |
|
fn=load_and_display_agentic_results, |
|
inputs=[token_state, reconstruction_cache_st], |
|
|
|
outputs=agentic_display_outputs, |
|
show_progress="minimal" |
|
).then( |
|
fn=format_okrs_for_enhanced_display, |
|
inputs=[reconstruction_cache_st], |
|
outputs=[enhanced_okr_display_html], |
|
show_progress="minimal" |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR): |
|
logging.warning(f"WARNING: '{LINKEDIN_CLIENT_ID_ENV_VAR}' is not set.") |
|
if not all(os.environ.get(var) for var in [BUBBLE_APP_NAME_ENV_VAR, BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR]): |
|
logging.warning("WARNING: One or more Bubble environment variables are not set.") |
|
if not AGENTIC_MODULES_LOADED: |
|
logging.warning("CRITICAL: Agentic modules failed to load.") |
|
if not os.environ.get("GEMINI_API_KEY"): |
|
logging.warning("WARNING: 'GEMINI_API_KEY' is not set.") |
|
|
|
app.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)), debug=True) |
|
|