Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -33,12 +33,16 @@ from analytics_plot_generator import (
|
|
33 |
generate_reach_over_time_plot,
|
34 |
generate_impressions_over_time_plot,
|
35 |
create_placeholder_plot, # For initializing plots
|
36 |
-
# --- Import new plot functions ---
|
37 |
generate_likes_over_time_plot,
|
38 |
-
generate_clicks_over_time_plot,
|
39 |
generate_shares_over_time_plot,
|
40 |
generate_comments_over_time_plot,
|
41 |
-
generate_comments_sentiment_breakdown_plot
|
|
|
|
|
|
|
|
|
42 |
)
|
43 |
|
44 |
# Configure logging
|
@@ -52,7 +56,11 @@ def update_analytics_plots(token_state_value, date_filter_option, custom_start_d
|
|
52 |
logging.info(f"Updating analytics plots. Filter: {date_filter_option}, Custom Start: {custom_start_date}, Custom End: {custom_end_date}")
|
53 |
|
54 |
# --- Increased number of expected plots ---
|
55 |
-
|
|
|
|
|
|
|
|
|
56 |
|
57 |
if not token_state_value or not token_state_value.get("token"):
|
58 |
message = "β Access denied. No token. Cannot generate analytics."
|
@@ -61,8 +69,9 @@ def update_analytics_plots(token_state_value, date_filter_option, custom_start_d
|
|
61 |
return [message] + placeholder_figs
|
62 |
|
63 |
try:
|
64 |
-
# prepare_filtered_analytics_data might need to be updated if new DFs are required for new plots
|
65 |
-
#
|
|
|
66 |
(filtered_merged_posts_df,
|
67 |
filtered_mentions_df,
|
68 |
date_filtered_follower_stats_df,
|
@@ -72,12 +81,14 @@ def update_analytics_plots(token_state_value, date_filter_option, custom_start_d
|
|
72 |
token_state_value, date_filter_option, custom_start_date, custom_end_date
|
73 |
)
|
74 |
|
75 |
-
#
|
76 |
-
#
|
77 |
-
#
|
78 |
-
#
|
79 |
-
#
|
80 |
-
#
|
|
|
|
|
81 |
|
82 |
except Exception as e:
|
83 |
error_msg = f"β Error preparing analytics data: {e}"
|
@@ -89,16 +100,17 @@ def update_analytics_plots(token_state_value, date_filter_option, custom_start_d
|
|
89 |
date_column_mentions = token_state_value.get("config_date_col_mentions", "date")
|
90 |
# config_date_col_followers_source = token_state_value.get("config_date_col_followers", "date")
|
91 |
|
92 |
-
|
93 |
logging.info(f"Data for plotting - Filtered Merged Posts: {len(filtered_merged_posts_df)} rows, Filtered Mentions: {len(filtered_mentions_df)} rows.")
|
94 |
logging.info(f"Date-Filtered Follower Stats: {len(date_filtered_follower_stats_df)} rows, Raw Follower Stats: {len(raw_follower_stats_df)} rows.")
|
95 |
|
96 |
try:
|
97 |
-
# Existing plots
|
98 |
plot_posts_activity = generate_posts_activity_plot(filtered_merged_posts_df, date_column=date_column_posts)
|
99 |
plot_engagement_type = generate_engagement_type_plot(filtered_merged_posts_df)
|
100 |
-
|
101 |
-
|
|
|
|
|
102 |
|
103 |
plot_followers_count = generate_followers_count_over_time_plot(
|
104 |
date_filtered_follower_stats_df,
|
@@ -120,23 +132,28 @@ def update_analytics_plots(token_state_value, date_filter_option, custom_start_d
|
|
120 |
plot_reach_over_time = generate_reach_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, reach_col='clickCount')
|
121 |
plot_impressions_over_time = generate_impressions_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, impressions_col='impressionCount')
|
122 |
|
123 |
-
#
|
124 |
plot_likes_over_time = generate_likes_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, likes_col='likeCount')
|
125 |
plot_clicks_over_time = generate_clicks_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, clicks_col='clickCount')
|
126 |
plot_shares_over_time = generate_shares_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, shares_col='shareCount')
|
127 |
plot_comments_over_time = generate_comments_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, comments_col='commentCount')
|
128 |
|
129 |
-
#
|
130 |
-
# If `filtered_merged_posts_df` is passed and lacks 'comment_sentiment' column, the plot function will show a placeholder.
|
131 |
-
# If you have a specific df for this, e.g., `filtered_comments_with_sentiment_df` from `prepare_filtered_analytics_data` (if modified)
|
132 |
-
# or from `token_state_value.get("bubble_comments_sentiment_df")`, use that one.
|
133 |
-
# For this example, we assume `filtered_merged_posts_df` is passed and the plot function handles it.
|
134 |
plot_comments_sentiment_breakdown = generate_comments_sentiment_breakdown_plot(
|
135 |
-
filtered_merged_posts_df,
|
136 |
-
sentiment_column='
|
137 |
-
# The plot function will show a placeholder if this column isn't suitable or found.
|
138 |
)
|
139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
|
141 |
message = f"π Analytics updated for period: {date_filter_option}"
|
142 |
if date_filter_option == "Custom Range":
|
@@ -145,36 +162,45 @@ def update_analytics_plots(token_state_value, date_filter_option, custom_start_d
|
|
145 |
message += f" (From: {s_display} To: {e_display})"
|
146 |
|
147 |
all_generated_plots = [
|
148 |
-
plot_posts_activity, plot_engagement_type,
|
|
|
149 |
plot_followers_count, plot_followers_growth_rate,
|
150 |
plot_followers_by_location, plot_followers_by_role, plot_followers_by_industry, plot_followers_by_seniority,
|
151 |
plot_engagement_rate, plot_reach_over_time, plot_impressions_over_time,
|
152 |
-
#
|
153 |
plot_likes_over_time, plot_clicks_over_time,
|
154 |
plot_shares_over_time, plot_comments_over_time,
|
155 |
-
plot_comments_sentiment_breakdown
|
|
|
|
|
|
|
|
|
|
|
156 |
]
|
157 |
num_plots_generated = sum(1 for p in all_generated_plots if p is not None and not isinstance(p, str))
|
158 |
-
logging.info(f"Successfully generated {num_plots_generated}
|
159 |
|
160 |
# Ensure the number of returned plots matches num_expected_plots, padding with placeholders if necessary
|
161 |
-
# This is crucial if some plot functions might return None on error and we need to match the Gradio outputs list length
|
162 |
final_plots_list = []
|
163 |
-
for p in all_generated_plots:
|
164 |
-
if
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
|
|
|
|
|
|
|
|
|
|
169 |
|
170 |
# If fewer plots were generated than expected (e.g. due to early exit or major error in a plot function)
|
171 |
while len(final_plots_list) < num_expected_plots:
|
172 |
logging.warning(f"Padding missing plot with placeholder. Expected {num_expected_plots}, got {len(final_plots_list)} so far.")
|
173 |
final_plots_list.append(create_placeholder_plot(title="Missing Plot", message="Plot could not be generated."))
|
174 |
if len(final_plots_list) > num_expected_plots + 5: # Safety break
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
|
179 |
return [message] + final_plots_list[:num_expected_plots] # Ensure correct number of outputs
|
180 |
|
@@ -196,11 +222,15 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
|
|
196 |
"bubble_mentions_df": pd.DataFrame(),
|
197 |
"bubble_follower_stats_df": pd.DataFrame(),
|
198 |
# Consider adding "bubble_comments_sentiment_df": pd.DataFrame() if you plan to fetch this data
|
|
|
|
|
199 |
"fetch_count_for_api": 0,
|
200 |
"url_user_token_temp_storage": None,
|
201 |
"config_date_col_posts": "published_at",
|
202 |
"config_date_col_mentions": "date",
|
203 |
-
"config_date_col_followers": "date"
|
|
|
|
|
204 |
})
|
205 |
|
206 |
gr.Markdown("# π LinkedIn Organization Dashboard")
|
@@ -280,10 +310,11 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
|
|
280 |
posts_activity_plot = gr.Plot(label="Posts Activity Over Time")
|
281 |
engagement_type_plot = gr.Plot(label="Post Engagement Types")
|
282 |
|
|
|
283 |
gr.Markdown("### Mentions Overview (Filtered by Date)")
|
284 |
with gr.Row():
|
285 |
-
mentions_activity_plot = gr.Plot(label="Mentions Activity Over Time")
|
286 |
-
mention_sentiment_plot = gr.Plot(label="Mention Sentiment Distribution")
|
287 |
|
288 |
gr.Markdown("### Follower Dynamics")
|
289 |
with gr.Row():
|
@@ -301,35 +332,57 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
|
|
301 |
gr.Markdown("### Post Performance Insights (Filtered by Date)")
|
302 |
with gr.Row():
|
303 |
engagement_rate_plot = gr.Plot(label="Engagement Rate Over Time")
|
304 |
-
reach_over_time_plot = gr.Plot(label="Reach Over Time (Clicks)")
|
305 |
-
with gr.Row():
|
306 |
impressions_over_time_plot = gr.Plot(label="Impressions Over Time")
|
307 |
-
# New plots will start here, keeping 2 per row
|
308 |
likes_over_time_plot = gr.Plot(label="Reactions (Likes) Over Time")
|
309 |
|
310 |
gr.Markdown("### Detailed Post Engagement Over Time (Filtered by Date)")
|
311 |
with gr.Row():
|
312 |
-
clicks_over_time_plot = gr.Plot(label="Clicks Over Time")
|
313 |
shares_over_time_plot = gr.Plot(label="Shares Over Time")
|
314 |
with gr.Row():
|
315 |
comments_over_time_plot = gr.Plot(label="Comments Over Time")
|
316 |
-
# For the 5th new plot, "Breakdown of Comments by Sentiment"
|
317 |
-
# It will be alone in this row, or you can add another plot next to it later.
|
318 |
comments_sentiment_plot = gr.Plot(label="Breakdown of Comments by Sentiment")
|
319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
|
321 |
analytics_plot_outputs = [
|
322 |
-
analytics_status_md,
|
323 |
-
|
|
|
324 |
followers_count_plot, followers_growth_rate_plot,
|
325 |
followers_by_location_plot, followers_by_role_plot,
|
326 |
followers_by_industry_plot, followers_by_seniority_plot,
|
327 |
engagement_rate_plot, reach_over_time_plot, impressions_over_time_plot,
|
328 |
-
#
|
329 |
likes_over_time_plot, clicks_over_time_plot,
|
330 |
shares_over_time_plot, comments_over_time_plot,
|
331 |
-
comments_sentiment_plot
|
|
|
|
|
|
|
|
|
332 |
]
|
|
|
|
|
|
|
333 |
|
334 |
apply_filter_btn.click(
|
335 |
fn=update_analytics_plots,
|
|
|
33 |
generate_reach_over_time_plot,
|
34 |
generate_impressions_over_time_plot,
|
35 |
create_placeholder_plot, # For initializing plots
|
36 |
+
# --- Import existing new plot functions ---
|
37 |
generate_likes_over_time_plot,
|
38 |
+
generate_clicks_over_time_plot, # Note: can be same as reach
|
39 |
generate_shares_over_time_plot,
|
40 |
generate_comments_over_time_plot,
|
41 |
+
generate_comments_sentiment_breakdown_plot,
|
42 |
+
# --- Import NEW plot functions for Content Strategy ---
|
43 |
+
generate_post_frequency_plot,
|
44 |
+
generate_content_format_breakdown_plot,
|
45 |
+
generate_content_topic_breakdown_plot
|
46 |
)
|
47 |
|
48 |
# Configure logging
|
|
|
56 |
logging.info(f"Updating analytics plots. Filter: {date_filter_option}, Custom Start: {custom_start_date}, Custom End: {custom_end_date}")
|
57 |
|
58 |
# --- Increased number of expected plots ---
|
59 |
+
# Original 13 + 5 engagement = 18
|
60 |
+
# New Content Strategy (3: freq, format, topics)
|
61 |
+
# New Mention Analysis (2: volume, sentiment - these reuse existing plot objects but are new UI slots)
|
62 |
+
# Total = 18 + 3 + 2 = 23
|
63 |
+
num_expected_plots = 23
|
64 |
|
65 |
if not token_state_value or not token_state_value.get("token"):
|
66 |
message = "β Access denied. No token. Cannot generate analytics."
|
|
|
69 |
return [message] + placeholder_figs
|
70 |
|
71 |
try:
|
72 |
+
# prepare_filtered_analytics_data might need to be updated if new DFs are required for new plots
|
73 |
+
# (e.g. if 'media_type' or 'eb_labels' are not in 'bubble_posts_df' and need special handling)
|
74 |
+
# For now, we assume 'filtered_merged_posts_df' contains these columns.
|
75 |
(filtered_merged_posts_df,
|
76 |
filtered_mentions_df,
|
77 |
date_filtered_follower_stats_df,
|
|
|
81 |
token_state_value, date_filter_option, custom_start_date, custom_end_date
|
82 |
)
|
83 |
|
84 |
+
# Ensure 'media_type' and 'eb_labels' exist in filtered_merged_posts_df for new plots,
|
85 |
+
# or handle their absence gracefully in the plot functions themselves (which they do).
|
86 |
+
# Example: Add dummy columns if they might be missing, for robust testing:
|
87 |
+
# if 'media_type' not in filtered_merged_posts_df.columns:
|
88 |
+
# filtered_merged_posts_df['media_type'] = 'Unknown'
|
89 |
+
# if 'eb_labels' not in filtered_merged_posts_df.columns:
|
90 |
+
# filtered_merged_posts_df['eb_labels'] = None
|
91 |
+
|
92 |
|
93 |
except Exception as e:
|
94 |
error_msg = f"β Error preparing analytics data: {e}"
|
|
|
100 |
date_column_mentions = token_state_value.get("config_date_col_mentions", "date")
|
101 |
# config_date_col_followers_source = token_state_value.get("config_date_col_followers", "date")
|
102 |
|
|
|
103 |
logging.info(f"Data for plotting - Filtered Merged Posts: {len(filtered_merged_posts_df)} rows, Filtered Mentions: {len(filtered_mentions_df)} rows.")
|
104 |
logging.info(f"Date-Filtered Follower Stats: {len(date_filtered_follower_stats_df)} rows, Raw Follower Stats: {len(raw_follower_stats_df)} rows.")
|
105 |
|
106 |
try:
|
107 |
+
# Existing plots (13)
|
108 |
plot_posts_activity = generate_posts_activity_plot(filtered_merged_posts_df, date_column=date_column_posts)
|
109 |
plot_engagement_type = generate_engagement_type_plot(filtered_merged_posts_df)
|
110 |
+
|
111 |
+
# These two will be used for the new "Mention Analysis" section as well
|
112 |
+
fig_mentions_activity_shared = generate_mentions_activity_plot(filtered_mentions_df, date_column=date_column_mentions)
|
113 |
+
fig_mention_sentiment_shared = generate_mention_sentiment_plot(filtered_mentions_df)
|
114 |
|
115 |
plot_followers_count = generate_followers_count_over_time_plot(
|
116 |
date_filtered_follower_stats_df,
|
|
|
132 |
plot_reach_over_time = generate_reach_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, reach_col='clickCount')
|
133 |
plot_impressions_over_time = generate_impressions_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, impressions_col='impressionCount')
|
134 |
|
135 |
+
# Additional Engagement plots (5)
|
136 |
plot_likes_over_time = generate_likes_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, likes_col='likeCount')
|
137 |
plot_clicks_over_time = generate_clicks_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, clicks_col='clickCount')
|
138 |
plot_shares_over_time = generate_shares_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, shares_col='shareCount')
|
139 |
plot_comments_over_time = generate_comments_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts, comments_col='commentCount')
|
140 |
|
141 |
+
# Assuming 'comment_sentiment' column might exist, or 'sentiment' as fallback (handled in plot function)
|
|
|
|
|
|
|
|
|
142 |
plot_comments_sentiment_breakdown = generate_comments_sentiment_breakdown_plot(
|
143 |
+
filtered_merged_posts_df,
|
144 |
+
sentiment_column='comment_sentiment'
|
|
|
145 |
)
|
146 |
|
147 |
+
# --- Generate NEW plots for Content Strategy (3) ---
|
148 |
+
# Assuming 'media_type' and 'eb_labels' are in filtered_merged_posts_df
|
149 |
+
# The plot functions themselves have fallbacks/placeholders if columns are missing.
|
150 |
+
media_type_col_name = token_state_value.get("config_media_type_col", "media_type") # Example if configurable
|
151 |
+
eb_labels_col_name = token_state_value.get("config_eb_labels_col", "eb_labels") # Example if configurable
|
152 |
+
|
153 |
+
plot_post_frequency = generate_post_frequency_plot(filtered_merged_posts_df, date_column=date_column_posts)
|
154 |
+
plot_content_format_breakdown = generate_content_format_breakdown_plot(filtered_merged_posts_df, format_col=media_type_col_name)
|
155 |
+
plot_content_topic_breakdown = generate_content_topic_breakdown_plot(filtered_merged_posts_df, topics_col=eb_labels_col_name)
|
156 |
+
|
157 |
|
158 |
message = f"π Analytics updated for period: {date_filter_option}"
|
159 |
if date_filter_option == "Custom Range":
|
|
|
162 |
message += f" (From: {s_display} To: {e_display})"
|
163 |
|
164 |
all_generated_plots = [
|
165 |
+
plot_posts_activity, plot_engagement_type,
|
166 |
+
fig_mentions_activity_shared, fig_mention_sentiment_shared, # Original mention plots
|
167 |
plot_followers_count, plot_followers_growth_rate,
|
168 |
plot_followers_by_location, plot_followers_by_role, plot_followers_by_industry, plot_followers_by_seniority,
|
169 |
plot_engagement_rate, plot_reach_over_time, plot_impressions_over_time,
|
170 |
+
# Add new engagement plot objects to the list
|
171 |
plot_likes_over_time, plot_clicks_over_time,
|
172 |
plot_shares_over_time, plot_comments_over_time,
|
173 |
+
plot_comments_sentiment_breakdown,
|
174 |
+
# --- Add NEW Content Strategy plot objects ---
|
175 |
+
plot_post_frequency, plot_content_format_breakdown, plot_content_topic_breakdown,
|
176 |
+
# --- Add plots for the NEW "Mention Analysis" section (reusing figures) ---
|
177 |
+
fig_mentions_activity_shared, # Reused figure for new UI slot
|
178 |
+
fig_mention_sentiment_shared # Reused figure for new UI slot
|
179 |
]
|
180 |
num_plots_generated = sum(1 for p in all_generated_plots if p is not None and not isinstance(p, str))
|
181 |
+
logging.info(f"Successfully generated {num_plots_generated} plot figures for {num_expected_plots} UI slots.")
|
182 |
|
183 |
# Ensure the number of returned plots matches num_expected_plots, padding with placeholders if necessary
|
|
|
184 |
final_plots_list = []
|
185 |
+
for i, p in enumerate(all_generated_plots): # Iterate up to the expected number of plots
|
186 |
+
if i < num_expected_plots: # Ensure we don't exceed the expected number of outputs
|
187 |
+
if p is not None and not isinstance(p, str): # isinstance check for safety
|
188 |
+
final_plots_list.append(p)
|
189 |
+
else:
|
190 |
+
logging.warning(f"A plot generation failed or returned unexpected type for slot {i}, using placeholder. Plot: {p}")
|
191 |
+
final_plots_list.append(create_placeholder_plot(title="Plot Error", message="Failed to generate this plot."))
|
192 |
+
else:
|
193 |
+
logging.warning(f"Generated more plot figures ({len(all_generated_plots)}) than expected UI slots ({num_expected_plots}). Truncating.")
|
194 |
+
break
|
195 |
+
|
196 |
|
197 |
# If fewer plots were generated than expected (e.g. due to early exit or major error in a plot function)
|
198 |
while len(final_plots_list) < num_expected_plots:
|
199 |
logging.warning(f"Padding missing plot with placeholder. Expected {num_expected_plots}, got {len(final_plots_list)} so far.")
|
200 |
final_plots_list.append(create_placeholder_plot(title="Missing Plot", message="Plot could not be generated."))
|
201 |
if len(final_plots_list) > num_expected_plots + 5: # Safety break
|
202 |
+
logging.error("Too many placeholders added, breaking loop.")
|
203 |
+
break
|
|
|
204 |
|
205 |
return [message] + final_plots_list[:num_expected_plots] # Ensure correct number of outputs
|
206 |
|
|
|
222 |
"bubble_mentions_df": pd.DataFrame(),
|
223 |
"bubble_follower_stats_df": pd.DataFrame(),
|
224 |
# Consider adding "bubble_comments_sentiment_df": pd.DataFrame() if you plan to fetch this data
|
225 |
+
# Add keys for new data if needed by prepare_filtered_analytics_data, e.g.
|
226 |
+
# "bubble_posts_with_content_details_df": pd.DataFrame(),
|
227 |
"fetch_count_for_api": 0,
|
228 |
"url_user_token_temp_storage": None,
|
229 |
"config_date_col_posts": "published_at",
|
230 |
"config_date_col_mentions": "date",
|
231 |
+
"config_date_col_followers": "date",
|
232 |
+
"config_media_type_col": "media_type", # For new plot
|
233 |
+
"config_eb_labels_col": "eb_labels" # For new plot
|
234 |
})
|
235 |
|
236 |
gr.Markdown("# π LinkedIn Organization Dashboard")
|
|
|
310 |
posts_activity_plot = gr.Plot(label="Posts Activity Over Time")
|
311 |
engagement_type_plot = gr.Plot(label="Post Engagement Types")
|
312 |
|
313 |
+
# Original Mentions Overview - these plots will also be used for the "Mention Analysis" section below
|
314 |
gr.Markdown("### Mentions Overview (Filtered by Date)")
|
315 |
with gr.Row():
|
316 |
+
mentions_activity_plot = gr.Plot(label="Mentions Activity Over Time") # Will be updated by fig_mentions_activity_shared
|
317 |
+
mention_sentiment_plot = gr.Plot(label="Mention Sentiment Distribution") # Will be updated by fig_mention_sentiment_shared
|
318 |
|
319 |
gr.Markdown("### Follower Dynamics")
|
320 |
with gr.Row():
|
|
|
332 |
gr.Markdown("### Post Performance Insights (Filtered by Date)")
|
333 |
with gr.Row():
|
334 |
engagement_rate_plot = gr.Plot(label="Engagement Rate Over Time")
|
335 |
+
reach_over_time_plot = gr.Plot(label="Reach Over Time (Clicks)")
|
336 |
+
with gr.Row():
|
337 |
impressions_over_time_plot = gr.Plot(label="Impressions Over Time")
|
|
|
338 |
likes_over_time_plot = gr.Plot(label="Reactions (Likes) Over Time")
|
339 |
|
340 |
gr.Markdown("### Detailed Post Engagement Over Time (Filtered by Date)")
|
341 |
with gr.Row():
|
342 |
+
clicks_over_time_plot = gr.Plot(label="Clicks Over Time") # Can be same as reach
|
343 |
shares_over_time_plot = gr.Plot(label="Shares Over Time")
|
344 |
with gr.Row():
|
345 |
comments_over_time_plot = gr.Plot(label="Comments Over Time")
|
|
|
|
|
346 |
comments_sentiment_plot = gr.Plot(label="Breakdown of Comments by Sentiment")
|
347 |
|
348 |
+
# --- NEW: Content Strategy Analysis ---
|
349 |
+
gr.Markdown("### π Content Strategy Analysis (Filtered by Date)")
|
350 |
+
with gr.Row():
|
351 |
+
post_frequency_cs_plot = gr.Plot(label="Post Frequency") # New plot component
|
352 |
+
content_format_breakdown_cs_plot = gr.Plot(label="Breakdown of Content by Format") # New
|
353 |
+
with gr.Row():
|
354 |
+
content_topic_breakdown_cs_plot = gr.Plot(label="Breakdown of Content by Topics") # New (might need more width)
|
355 |
+
# You can add another plot here or make the topic plot wider if needed, e.g. by itself in a row.
|
356 |
+
# For now, placing it here. If it's too cramped:
|
357 |
+
# content_topic_breakdown_cs_plot = gr.Plot(label="Breakdown of Content by Topics", elem_id="topic_plot_wide") # and use CSS for width if needed
|
358 |
+
|
359 |
+
# --- NEW: Mention Analysis (reusing plots from above) ---
|
360 |
+
gr.Markdown("### π¬ Mention Analysis (Filtered by Date)")
|
361 |
+
with gr.Row():
|
362 |
+
mention_analysis_volume_plot = gr.Plot(label="Mentions Volume Over Time") # New UI slot, uses fig_mentions_activity_shared
|
363 |
+
mention_analysis_sentiment_plot = gr.Plot(label="Breakdown of Mentions by Sentiment") # New UI slot, uses fig_mention_sentiment_shared
|
364 |
+
|
365 |
|
366 |
analytics_plot_outputs = [
|
367 |
+
analytics_status_md,
|
368 |
+
posts_activity_plot, engagement_type_plot,
|
369 |
+
mentions_activity_plot, mention_sentiment_plot, # Original mention plots
|
370 |
followers_count_plot, followers_growth_rate_plot,
|
371 |
followers_by_location_plot, followers_by_role_plot,
|
372 |
followers_by_industry_plot, followers_by_seniority_plot,
|
373 |
engagement_rate_plot, reach_over_time_plot, impressions_over_time_plot,
|
374 |
+
# Add new engagement plot components to the output list
|
375 |
likes_over_time_plot, clicks_over_time_plot,
|
376 |
shares_over_time_plot, comments_over_time_plot,
|
377 |
+
comments_sentiment_plot,
|
378 |
+
# --- Add NEW Content Strategy plot components ---
|
379 |
+
post_frequency_cs_plot, content_format_breakdown_cs_plot, content_topic_breakdown_cs_plot,
|
380 |
+
# --- Add NEW Mention Analysis plot components (these will receive the reused figures) ---
|
381 |
+
mention_analysis_volume_plot, mention_analysis_sentiment_plot
|
382 |
]
|
383 |
+
# Expected length: 1 (status) + 13 (original plots) + 5 (new engagement) + 3 (content strategy) + 2 (mention analysis) = 24
|
384 |
+
# The update_analytics_plots function returns message + 23 plots. So len(analytics_plot_outputs) should be 24.
|
385 |
+
# Current count: 1 + 2 + 2 + 2 + 4 + 3 + 5 + 3 + 2 = 24. Correct.
|
386 |
|
387 |
apply_filter_btn.click(
|
388 |
fn=update_analytics_plots,
|