Spaces:
Runtime error
Runtime error
File size: 13,121 Bytes
6f0bbbb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
def plot_sentiment_pie(results, title="Reels Sentiment Analysis"):
"""
Creates a pie chart from sentiment analysis results and returns the matplotlib figure.
Args:
results: Counter object or dict with 'positive', 'neutral', 'negative' keys
title: Chart title
Returns:
Matplotlib Figure object, or None if no data.
"""
labels = ['Positive', 'Neutral', 'Negative']
sizes = [results.get('positive', 0), results.get('neutral', 0), results.get('negative', 0)]
if sum(sizes) == 0:
return None
colors = ['#4CAF50', '#FFC107', '#F44336']
explode = (0.05, 0, 0.05)
fig, ax = plt.subplots(figsize=(8, 6))
filtered_labels = [label for i, label in enumerate(labels) if sizes[i] > 0]
filtered_sizes = [size for size in sizes if size > 0]
filtered_colors = [colors[i] for i, size in enumerate(sizes) if size > 0]
explode_map = {'Positive': 0.05, 'Neutral': 0, 'Negative': 0.05}
filtered_explode = [explode_map.get(label, 0) for label in filtered_labels]
ax.pie(filtered_sizes, explode=filtered_explode, labels=filtered_labels, colors=filtered_colors,
autopct='%1.1f%%', shadow=True, startangle=140,
textprops={'fontsize': 12, 'color': 'black'})
ax.axis('equal')
plt.title(title, fontsize=16, pad=20)
plt.tight_layout()
# Return the figure object instead of saving to bytes
return fig
def plot_category_distribution(counter, title="Reels Content Distribution"):
"""
Generate pie chart from category counts and returns the matplotlib figure.
Args:
counter: Counter object with category counts.
title: Chart title.
Returns:
Matplotlib Figure object, or None if no data.
"""
labels = []
sizes = []
total = sum(counter.values())
if total == 0:
return None
threshold = total * 0.02
other_count = 0
sorted_categories = counter.most_common()
for category, count in sorted_categories:
if count >= threshold and category != "other":
labels.append(category.replace('_', ' ').title())
sizes.append(count)
elif category == "other":
other_count += count
else:
other_count += count
if other_count > 0:
labels.append("Other")
sizes.append(other_count)
if not sizes:
return None
fig, ax = plt.subplots(figsize=(10, 8))
colors = plt.cm.viridis(np.linspace(0, 1, len(sizes)))
ax.pie(
sizes,
labels=labels,
autopct='%1.1f%%',
startangle=140,
colors=colors,
wedgeprops={'edgecolor': 'white', 'linewidth': 1},
textprops={'fontsize': 11, 'color': 'black'}
)
plt.title(title, pad=20, fontsize=15)
plt.axis('equal')
plt.tight_layout()
# Return the figure object instead of saving to bytes
return fig
# The rest of the Gradio Blocks interface definition and function linking
# should remain the same, as the analyze_reels_gradio function already
# calls these plotting functions and is intended to return the figure objects now.
# Global variables to maintain state across Gradio calls
global cl
global explore_reels_list
global sentiment_analyzer_instance
global content_classifier_pipeline
# Initialize sentiment analyzer if not already done (can be done here or lazily in analyze_reels_gradio)
# Doing it here ensures the model is loaded when this cell runs, potentially reducing latency on first analyze click.
try:
sentiment_analyzer_instance = ReelSentimentAnalyzer()
print("Sentiment Analyzer initialized.")
# Optional: Train Hindi model if needed and data is available
# sample_train_data = [...] # Define your training data
# sentiment_analyzer_instance.train_hindi_model(sample_train_data)
except Exception as e:
print(f"Error initializing Sentiment Analyzer globally: {e}")
sentiment_analyzer_instance = None
# Initialize content classifier pipeline if not already done (can be done here or lazily)
try:
print("Initializing Content Classifier Pipeline globally...")
content_classifier_pipeline = pipeline(
"zero-shot-classification",
model="facebook/bart-large-mnli",
device=0 if torch.cuda.is_available() else -1 # Use GPU if available
)
print("Content Classifier Pipeline Initialized.")
except Exception as e:
print(f"Error initializing Content Classifier globally: {e}")
content_classifier_pipeline = None
def login_gradio(username):
"""Gradio-compatible login function."""
global cl
try:
PASSWORD = userdata.get('password')
except Exception as e:
return f"Error accessing password secret: {e}"
if not PASSWORD:
return "Error: Instagram password not found in Colab secrets."
cl = Client()
try:
cl.login(username, PASSWORD)
return f"Successfully logged in as {username}"
except Exception as e:
cl = None # Ensure cl is None on failure
return f"Error during login: {e}"
def fetch_reels_gradio():
"""Gradio-compatible function to fetch explore reels."""
global cl
global explore_reels_list
if cl is None:
explore_reels_list = [] # Ensure list is empty on failure
return "Error: Not logged in. Please log in first."
try:
# Fetch a limited number of reels for demonstration purposes
# You might want to make this number configurable later
fetched_reels = cl.explore_reels()[:100] # Fetch up to 100 for analysis
explore_reels_list = fetched_reels
if explore_reels_list:
return f"Successfully fetched {len(explore_reels_list)} explore reels."
else:
explore_reels_list = [] # Ensure it's an empty list
return "Fetched 0 explore reels."
except Exception as e:
explore_reels_list = [] # Ensure it's an empty list on error
return f"Error fetching explore reels: {e}"
def analyze_reels_gradio(max_to_analyze):
"""Gradio-compatible function to analyze fetched reels and generate plots."""
global explore_reels_list
global sentiment_analyzer_instance
global content_classifier_pipeline
if not explore_reels_list:
# Return None for plots if no reels
return "Error: No reels fetched yet. Please fetch reels first.", None, None
# Ensure max_to_analyze does not exceed the number of fetched reels
num_reels_to_process = min(max_to_analyze, len(explore_reels_list))
reels_to_analyze = explore_reels_list[:num_reels_to_process]
if not reels_to_analyze:
return "Error: No reels available to analyze.", None, None
# Check if analyzers are initialized
if sentiment_analyzer_instance is None:
return "Error: Sentiment Analyzer not initialized.", None, None
if content_classifier_pipeline is None:
return "Error: Content Classifier not initialized.", None, None
analysis_status_messages = []
sentiment_plot_figure = None # Changed to figure
content_plot_figure = None # Changed to figure
# Perform Sentiment Analysis
try:
analysis_status_messages.append(f"Starting Sentiment Analysis for {len(reels_to_analyze)} reels...")
sentiment_results, detailed_sentiment_results = sentiment_analyzer_instance.analyze_reels(
reels_to_analyze,
max_to_analyze=len(reels_to_analyze) # Pass the actual number being processed
)
# Call the updated plotting function that returns a figure
sentiment_plot_figure = plot_sentiment_pie(sentiment_results, title=f"Sentiment of {len(reels_to_analyze)} Instagram Reels")
analysis_status_messages.append("Sentiment Analysis Complete.")
except Exception as e:
analysis_status_messages.append(f"Error during Sentiment Analysis: {e}")
sentiment_plot_figure = None # Ensure plot is None on error
# Perform Content Categorization
try:
analysis_status_messages.append(f"Starting Content Categorization for {len(reels_to_analyze)} reels...")
category_counts = Counter()
# Re-implement content analysis slightly to fit this flow using the global pipeline
print(f"\n⏳ Analyzing content for {len(reels_to_analyze)} reels...")
for i, reel in enumerate(reels_to_analyze, 1):
caption = getattr(reel, 'caption_text', '') or getattr(reel, 'caption', '') or ''
# Use the global classifier pipeline
category, details = classify_reel_content(caption)
category_counts[category] += 1
print("\n✅ Content Analysis complete!")
print("\n📊 Category Counts:")
for category, count in category_counts.most_common():
print(f"- {category.replace('_', ' ').title()}: {count}")
# Call the updated plotting function that returns a figure
content_plot_figure = plot_category_distribution(category_counts)
analysis_status_messages.append("Content Categorization Complete.")
except Exception as e:
analysis_status_messages.append(f"Error during Content Analysis: {e}")
content_plot_figure = None # Ensure plot is None on error
final_status_message = "\n".join(analysis_status_messages)
# Return the figure objects
return final_status_message, sentiment_plot_figure, content_plot_figure
# --- Gradio Blocks Interface ---
with gr.Blocks() as demo:
gr.Markdown("# Instagram Reels Analysis")
with gr.Row():
username_input = gr.Textbox(label="Instagram Username")
login_button = gr.Button("Login")
login_status_output = gr.Label(label="Login Status")
with gr.Row():
fetch_button = gr.Button("Fetch Reels")
fetch_status_output = gr.Label(label="Fetch Status")
with gr.Row():
max_reels_input = gr.Slider(minimum=1, maximum=100, value=10, step=1, label="Number of Reels to Analyze")
analyze_button = gr.Button("Analyze Reels")
analyze_status_output = gr.Label(label="Analysis Status")
with gr.Row():
# Sentiment Analysis Outputs
with gr.Column():
gr.Markdown("## Sentiment Analysis")
sentiment_plot_output = gr.Plot(label="Sentiment Distribution")
# Content Analysis Outputs
with gr.Column():
gr.Markdown("## Content Analysis")
content_plot_output = gr.Plot(label="Content Distribution")
# Link login and fetch buttons (assuming login_gradio and fetch_reels_gradio are defined)
# Redefine login_gradio and fetch_reels_gradio here within the Blocks context
# to ensure they are linked correctly, even though they were defined above.
# This is a common pattern in Gradio Blocks.
def login_gradio_blocks(username):
"""Gradio-compatible login function for Blocks."""
global cl
try:
PASSWORD = userdata.get('password')
except Exception as e:
return f"Error accessing password secret: {e}"
if not PASSWORD:
return "Error: Instagram password not found in Colab secrets."
cl = Client()
try:
cl.login(username, PASSWORD)
return f"Successfully logged in as {username}"
except Exception as e:
cl = None # Ensure cl is None on failure
return f"Error during login: {e}"
def fetch_reels_gradio_blocks():
"""Gradio-compatible function to fetch explore reels for Blocks."""
global cl
global explore_reels_list
if cl is None:
explore_reels_list = [] # Ensure list is empty on failure
return "Error: Not logged in. Please log in first."
try:
# Fetch a limited number of reels for demonstration purposes
# You might want to make this number configurable later
fetched_reels = cl.explore_reels()[:100] # Fetch up to 100 for analysis
explore_reels_list = fetched_reels
if explore_reels_list:
return f"Successfully fetched {len(explore_reels_list)} explore reels."
else:
explore_reels_list = [] # Ensure it's an empty list
return "Fetched 0 explore reels."
except Exception as e:
explore_reels_list = [] # Ensure it's an empty list on error
return f"Error fetching explore reels: {e}"
login_button.click(
fn=login_gradio_blocks,
inputs=username_input,
outputs=login_status_output
)
fetch_button.click(
fn=fetch_reels_gradio_blocks,
inputs=None, # No direct inputs needed for fetching
outputs=fetch_status_output
)
# Link the Analyze button to the analysis function
analyze_button.click(
fn=analyze_reels_gradio,
inputs=max_reels_input, # Input is the slider value
outputs=[analyze_status_output, sentiment_plot_output, content_plot_output] # Outputs are status and the two plots
)
# The demo is now fully defined. It can be launched in the next step.
# demo.launch()
|