Spaces:
Running
Running
Create analytics_tab_setup.py
Browse files- ui/analytics_tab_setup.py +202 -0
ui/analytics_tab_setup.py
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ui/analytics_tab_setup.py
|
2 |
+
import gradio as gr
|
3 |
+
from collections import OrderedDict
|
4 |
+
from ui.ui_generators import (
|
5 |
+
build_analytics_tab_plot_area,
|
6 |
+
# BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON # These are used by handlers, not directly in setup
|
7 |
+
)
|
8 |
+
import logging
|
9 |
+
|
10 |
+
def get_plot_configurations():
|
11 |
+
"""Return the plot configuration list."""
|
12 |
+
# This configuration should ideally live in a config file or be passed if it's dynamic
|
13 |
+
plot_configs = [
|
14 |
+
{"label": "Numero di Follower nel Tempo", "id": "followers_count", "section": "Dinamiche dei Follower"},
|
15 |
+
{"label": "Tasso di Crescita Follower", "id": "followers_growth_rate", "section": "Dinamiche dei Follower"},
|
16 |
+
{"label": "Follower per Località", "id": "followers_by_location", "section": "Demografia Follower"},
|
17 |
+
{"label": "Follower per Ruolo (Funzione)", "id": "followers_by_role", "section": "Demografia Follower"},
|
18 |
+
{"label": "Follower per Settore", "id": "followers_by_industry", "section": "Demografia Follower"},
|
19 |
+
{"label": "Follower per Anzianità", "id": "followers_by_seniority", "section": "Demografia Follower"},
|
20 |
+
{"label": "Tasso di Engagement nel Tempo", "id": "engagement_rate", "section": "Approfondimenti Performance Post"},
|
21 |
+
{"label": "Copertura nel Tempo", "id": "reach_over_time", "section": "Approfondimenti Performance Post"},
|
22 |
+
{"label": "Visualizzazioni nel Tempo", "id": "impressions_over_time", "section": "Approfondimenti Performance Post"},
|
23 |
+
{"label": "Reazioni (Like) nel Tempo", "id": "likes_over_time", "section": "Approfondimenti Performance Post"},
|
24 |
+
{"label": "Click nel Tempo", "id": "clicks_over_time", "section": "Engagement Dettagliato Post nel Tempo"},
|
25 |
+
{"label": "Condivisioni nel Tempo", "id": "shares_over_time", "section": "Engagement Dettagliato Post nel Tempo"},
|
26 |
+
{"label": "Commenti nel Tempo", "id": "comments_over_time", "section": "Engagement Dettagliato Post nel Tempo"},
|
27 |
+
{"label": "Ripartizione Commenti per Sentiment", "id": "comments_sentiment", "section": "Engagement Dettagliato Post nel Tempo"},
|
28 |
+
{"label": "Frequenza Post", "id": "post_frequency_cs", "section": "Analisi Strategia Contenuti"},
|
29 |
+
{"label": "Ripartizione Contenuti per Formato", "id": "content_format_breakdown_cs", "section": "Analisi Strategia Contenuti"},
|
30 |
+
{"label": "Ripartizione Contenuti per Argomenti", "id": "content_topic_breakdown_cs", "section": "Analisi Strategia Contenuti"},
|
31 |
+
{"label": "Volume Menzioni nel Tempo (Dettaglio)", "id": "mention_analysis_volume", "section": "Analisi Menzioni (Dettaglio)"},
|
32 |
+
{"label": "Ripartizione Menzioni per Sentiment (Dettaglio)", "id": "mention_analysis_sentiment", "section": "Analisi Menzioni (Dettaglio)"}
|
33 |
+
]
|
34 |
+
# IMPORTANT: Review if 'mention_analysis_volume' and 'mention_analysis_sentiment' plots
|
35 |
+
# can still be generated without the dedicated mentions data processing.
|
36 |
+
# If not, they should also be removed from plot_configs.
|
37 |
+
# For now, I am assuming they might draw from a general data pool in token_state.
|
38 |
+
assert len(plot_configs) == 19, "Mancata corrispondenza in plot_configs e grafici attesi. (If mentions plots were removed, adjust this number)"
|
39 |
+
return plot_configs
|
40 |
+
|
41 |
+
def _process_plot_area_result(ui_elements_tuple, unique_ordered_sections):
|
42 |
+
"""Process the result from build_analytics_tab_plot_area."""
|
43 |
+
plot_ui_objects = {}
|
44 |
+
section_titles_map = {}
|
45 |
+
|
46 |
+
if isinstance(ui_elements_tuple, tuple) and len(ui_elements_tuple) == 2:
|
47 |
+
plot_ui_objects, section_titles_map = ui_elements_tuple
|
48 |
+
|
49 |
+
# Validate section titles map
|
50 |
+
if not all(sec_name in section_titles_map for sec_name in unique_ordered_sections):
|
51 |
+
logging.error("section_titles_map from build_analytics_tab_plot_area is incomplete.")
|
52 |
+
for sec_name in unique_ordered_sections:
|
53 |
+
if sec_name not in section_titles_map:
|
54 |
+
section_titles_map[sec_name] = gr.Markdown(f"### {sec_name} (Error Placeholder)")
|
55 |
+
else:
|
56 |
+
logging.error("build_analytics_tab_plot_area did not return a tuple of (plot_ui_objects, section_titles_map).")
|
57 |
+
# Fallback: ui_elements_tuple might be just plot_ui_objects if build_analytics_tab_plot_area changed
|
58 |
+
plot_ui_objects = ui_elements_tuple if isinstance(ui_elements_tuple, dict) else {}
|
59 |
+
for sec_name in unique_ordered_sections:
|
60 |
+
section_titles_map[sec_name] = gr.Markdown(f"### {sec_name} (Error Placeholder)")
|
61 |
+
|
62 |
+
return plot_ui_objects, section_titles_map
|
63 |
+
|
64 |
+
def _setup_action_panel():
|
65 |
+
"""Set up the action panel with insights chat and formula display."""
|
66 |
+
gr.Markdown("### 💡 Azioni Contestuali Grafico")
|
67 |
+
|
68 |
+
insights_chatbot_ui = gr.Chatbot(
|
69 |
+
label="Chat Insights",
|
70 |
+
type="messages",
|
71 |
+
height=450,
|
72 |
+
bubble_full_width=False,
|
73 |
+
visible=False,
|
74 |
+
show_label=False,
|
75 |
+
placeholder="L'analisi AI del grafico apparirà qui. Fai domande di approfondimento!"
|
76 |
+
)
|
77 |
+
|
78 |
+
insights_chat_input_ui = gr.Textbox(
|
79 |
+
label="La tua domanda:",
|
80 |
+
placeholder="Chiedi all'AI riguardo a questo grafico...",
|
81 |
+
lines=2,
|
82 |
+
visible=False,
|
83 |
+
show_label=False
|
84 |
+
)
|
85 |
+
|
86 |
+
with gr.Row(visible=False) as insights_suggestions_row_ui:
|
87 |
+
insights_suggestion_1_btn = gr.Button(value="Suggerimento 1", size="sm", min_width=50)
|
88 |
+
insights_suggestion_2_btn = gr.Button(value="Suggerimento 2", size="sm", min_width=50)
|
89 |
+
insights_suggestion_3_btn = gr.Button(value="Suggerimento 3", size="sm", min_width=50)
|
90 |
+
|
91 |
+
formula_display_markdown_ui = gr.Markdown(
|
92 |
+
"I dettagli sulla formula/metodologia appariranno qui.",
|
93 |
+
visible=False
|
94 |
+
)
|
95 |
+
|
96 |
+
formula_close_hint_md = gr.Markdown(
|
97 |
+
"<p style='font-size:0.9em; text-align:center; margin-top:10px;'>"
|
98 |
+
"<em>Click the active ƒ button on the plot again to close this panel.</em></p>",
|
99 |
+
visible=False
|
100 |
+
)
|
101 |
+
|
102 |
+
return {
|
103 |
+
'insights_chatbot_ui': insights_chatbot_ui,
|
104 |
+
'insights_chat_input_ui': insights_chat_input_ui,
|
105 |
+
'insights_suggestions_row_ui': insights_suggestions_row_ui,
|
106 |
+
'insights_suggestion_1_btn': insights_suggestion_1_btn,
|
107 |
+
'insights_suggestion_2_btn': insights_suggestion_2_btn,
|
108 |
+
'insights_suggestion_3_btn': insights_suggestion_3_btn,
|
109 |
+
'formula_display_markdown_ui': formula_display_markdown_ui,
|
110 |
+
'formula_close_hint_md': formula_close_hint_md
|
111 |
+
}
|
112 |
+
|
113 |
+
def setup_analytics_tab():
|
114 |
+
"""Set up the analytics tab with all its components and return component references."""
|
115 |
+
|
116 |
+
gr.Markdown("## 📈 Analisi Performance LinkedIn")
|
117 |
+
gr.Markdown("Seleziona un intervallo di date per i grafici. Clicca i pulsanti (💣 Insights, ƒ Formula, 🧭 Esplora) su un grafico per azioni.")
|
118 |
+
|
119 |
+
analytics_status_md = gr.Markdown("Stato analisi grafici...")
|
120 |
+
|
121 |
+
# Date filter controls
|
122 |
+
with gr.Row():
|
123 |
+
date_filter_selector = gr.Radio(
|
124 |
+
["Sempre", "Ultimi 7 Giorni", "Ultimi 30 Giorni", "Intervallo Personalizzato"],
|
125 |
+
label="Seleziona Intervallo Date per Grafici",
|
126 |
+
value="Sempre",
|
127 |
+
scale=3
|
128 |
+
)
|
129 |
+
with gr.Column(scale=2):
|
130 |
+
custom_start_date_picker = gr.DateTime(
|
131 |
+
label="Data Inizio",
|
132 |
+
visible=False,
|
133 |
+
include_time=False,
|
134 |
+
type="datetime" # Ensure this matches expected type by handlers/plotters
|
135 |
+
)
|
136 |
+
custom_end_date_picker = gr.DateTime(
|
137 |
+
label="Data Fine",
|
138 |
+
visible=False,
|
139 |
+
include_time=False,
|
140 |
+
type="datetime" # Ensure this matches
|
141 |
+
)
|
142 |
+
|
143 |
+
apply_filter_btn = gr.Button("🔍 Applica Filtro & Aggiorna Grafici", variant="primary")
|
144 |
+
|
145 |
+
# Set up date picker visibility toggle
|
146 |
+
def toggle_custom_date_pickers(selection):
|
147 |
+
is_custom = selection == "Intervallo Personalizzato"
|
148 |
+
return gr.update(visible=is_custom), gr.update(visible=is_custom)
|
149 |
+
|
150 |
+
date_filter_selector.change(
|
151 |
+
fn=toggle_custom_date_pickers,
|
152 |
+
inputs=[date_filter_selector],
|
153 |
+
outputs=[custom_start_date_picker, custom_end_date_picker]
|
154 |
+
)
|
155 |
+
|
156 |
+
# Plot configurations
|
157 |
+
plot_configs = get_plot_configurations()
|
158 |
+
unique_ordered_sections = list(OrderedDict.fromkeys(pc["section"] for pc in plot_configs))
|
159 |
+
num_unique_sections = len(unique_ordered_sections)
|
160 |
+
|
161 |
+
# State components (defined in main app.py, but listed here for clarity of what this tab might conceptually own or interact with)
|
162 |
+
# active_panel_action_state = gr.State(None) # Managed by main app
|
163 |
+
# explored_plot_id_state = gr.State(None) # Managed by main app
|
164 |
+
|
165 |
+
# Build plot area
|
166 |
+
plot_ui_objects = {}
|
167 |
+
section_titles_map = {}
|
168 |
+
global_actions_column_ui = None # Define it to ensure it's in scope
|
169 |
+
action_components = {} # Define it
|
170 |
+
|
171 |
+
with gr.Row(equal_height=False):
|
172 |
+
with gr.Column(scale=8) as plots_area_col:
|
173 |
+
# This function is expected to create the actual plot components (e.g., gr.Plot)
|
174 |
+
# and their associated action buttons (bomb, formula, explore)
|
175 |
+
# It should return a dictionary of these plot UI objects and section title markdowns
|
176 |
+
ui_elements_tuple = build_analytics_tab_plot_area(plot_configs)
|
177 |
+
plot_ui_objects, section_titles_map = _process_plot_area_result(
|
178 |
+
ui_elements_tuple, unique_ordered_sections
|
179 |
+
)
|
180 |
+
|
181 |
+
# Action panel (global for the tab, shown/hidden based on plot interactions)
|
182 |
+
with gr.Column(scale=4, visible=False) as global_actions_col: # Assign to variable
|
183 |
+
global_actions_column_ui = global_actions_col # Store the column itself
|
184 |
+
action_components = _setup_action_panel()
|
185 |
+
|
186 |
+
# Package all components for return, so handlers can connect to them
|
187 |
+
components = {
|
188 |
+
'analytics_status_md': analytics_status_md,
|
189 |
+
'date_filter_selector': date_filter_selector,
|
190 |
+
'custom_start_date_picker': custom_start_date_picker,
|
191 |
+
'custom_end_date_picker': custom_end_date_picker,
|
192 |
+
'apply_filter_btn': apply_filter_btn,
|
193 |
+
'plot_configs': plot_configs, # Pass config for handlers
|
194 |
+
'unique_ordered_sections': unique_ordered_sections, # Pass for handlers
|
195 |
+
'num_unique_sections': num_unique_sections, # Pass for handlers
|
196 |
+
'plot_ui_objects': plot_ui_objects, # Key for handlers to connect events
|
197 |
+
'section_titles_map': section_titles_map, # Key for handlers to update visibility
|
198 |
+
'global_actions_column_ui': global_actions_column_ui, # The visibility of this entire column is controlled
|
199 |
+
**action_components # Spread the dict from _setup_action_panel
|
200 |
+
}
|
201 |
+
|
202 |
+
return components
|