Spaces:
Sleeping
Sleeping
import gradio as gr | |
from transformers import pipeline | |
from datetime import datetime | |
import matplotlib.pyplot as plt | |
import io | |
import base64 | |
from langdetect import detect | |
import qrcode | |
from wordcloud import WordCloud | |
import nltk | |
from nltk.tokenize import sent_tokenize | |
from better_profanity import profanity | |
import tempfile | |
import os | |
from PIL import Image # For PIL image handling | |
# Download NLTK data | |
nltk.download('punkt', quiet=True) | |
nltk.download('punkt_tab', quiet=True) | |
# Model options | |
models = { | |
"DistilBERT": "distilbert-base-uncased-finetuned-sst-2-english", | |
"Twitter RoBERTa": "cardiffnlp/twitter-roberta-base-sentiment-latest" | |
} | |
analyzer = None | |
history = [] | |
sentiment_scores = [] | |
feedback_log = [] | |
# Load the selected or custom model | |
def load_model(model_name, custom_model_path=None): | |
global analyzer | |
if custom_model_path: | |
analyzer = pipeline("sentiment-analysis", model=custom_model_path) | |
return f"Loaded custom model from {custom_model_path}" | |
analyzer = pipeline("sentiment-analysis", model=models[model_name]) | |
return f"Loaded {model_name} model." | |
# Highlight words | |
def highlight_words(text, sentiment, pos_words, neg_words): | |
words = text.split() | |
highlighted = [] | |
pos_list = pos_words.split(",") if pos_words else ["love", "great", "happy", "awesome", "good"] | |
neg_list = neg_words.split(",") if neg_words else ["hate", "bad", "terrible", "awful", "sad"] | |
for word in words: | |
if sentiment == "POSITIVE" and word.lower() in [w.strip().lower() for w in pos_list]: | |
highlighted.append(f"**{word}**") | |
elif sentiment == "NEGATIVE" and word.lower() in [w.strip().lower() for w in neg_list]: | |
highlighted.append(f"**{word}**") | |
else: | |
highlighted.append(word) | |
return " ".join(highlighted) | |
# Sentiment analysis with context | |
def analyze_sentiment(text, model_name, pos_words, neg_words, intensity, custom_model_path=None, source="manual", compare_text=None): | |
if not text or not text.strip(): | |
return "Error: Please enter some text.", "", "", None, None, "", None, "" | |
try: | |
if analyzer is None: | |
load_model(model_name, custom_model_path) | |
# Language and profanity check | |
lang = detect(text) | |
lang_note = " (Warning: Text may not be in English)" if lang != "en" else "" | |
profanity_note = " (Warning: Inappropriate language detected)" if profanity.contains_profanity(text) else "" | |
# Contextual analysis | |
sentences = sent_tokenize(text) | |
results = [] | |
for sent in sentences: | |
result = analyzer(sent)[0] | |
label, score = result['label'], result['score'] | |
if score < intensity: | |
label = "NEUTRAL" | |
results.append(f"{sent} -> {label} ({score:.2f})") | |
combined_result = analyzer(text)[0] | |
label, score = combined_result['label'], combined_result['score'] | |
if score < intensity: | |
label, emoji = "NEUTRAL", "π" | |
else: | |
emoji = "π" if "POSITIVE" in label.upper() else "π" if "NEGATIVE" in label.upper() else "π" | |
confidence_note = " (Low confidence)" if score < 0.7 else "" | |
sentiment_result = f"Overall: {label} {emoji} (Confidence: {score:.2f}{confidence_note}{lang_note}{profanity_note})\n" + "\n".join(results) | |
highlighted_text = highlight_words(text, label, pos_words, neg_words) | |
# History and scores | |
timestamp = datetime.now().strftime('%H:%M:%S') | |
history.append(f"[{timestamp}] {source}: {text} -> {sentiment_result.splitlines()[0]}") | |
sentiment_scores.append((timestamp, 1 if "POSITIVE" in label.upper() else -1 if "NEGATIVE" in label.upper() else 0)) | |
history_str = "\n".join([h for h in history[-5:]]) | |
# Visuals | |
trend_img = generate_timeline() | |
wordcloud_img = generate_wordcloud(text) | |
qr_img = generate_qr(f"https://example.com/share?text={text}&result={sentiment_result.splitlines()[0].replace(' ', '+')}") | |
# Comparative analysis | |
compare_result = "" | |
if compare_text: | |
comp_result = analyzer(compare_text)[0] | |
comp_label, comp_score = comp_result['label'], comp_result['score'] | |
comp_emoji = "π" if "POSITIVE" in comp_label.upper() else "π" if "NEGATIVE" in comp_label.upper() else "π" | |
compare_result = f"Comparison: {comp_label} {comp_emoji} (Confidence: {comp_score:.2f})" | |
return sentiment_result, highlighted_text, history_str, trend_img, wordcloud_img, qr_img, compare_result, "" | |
except Exception as e: | |
print(e) | |
return f"Error: {str(e)}", "", "", None, None, "", "", "" | |
# Fetch X post (simulated) | |
def fetch_x_post(x_url, model_name, pos_words, neg_words, intensity, custom_model_path): | |
sample_text = "Sample X post from " + x_url | |
return analyze_sentiment(sample_text, model_name, pos_words, neg_words, intensity, custom_model_path, source="X post") | |
# Generate timeline (return PIL Image) | |
def generate_timeline(): | |
if not sentiment_scores: | |
return None | |
times, scores = zip(*sentiment_scores[-10:]) | |
plt.figure(figsize=(6, 3)) | |
plt.plot(times, scores, marker='o', linestyle='-', color='b') | |
plt.title("Sentiment Timeline") | |
plt.xlabel("Time") | |
plt.ylabel("Sentiment") | |
plt.ylim(-1.5, 1.5) | |
plt.xticks(rotation=45) | |
plt.tight_layout() | |
buf = io.BytesIO() | |
plt.savefig(buf, format="png") | |
buf.seek(0) | |
img = Image.open(buf) # Standard PIL Image | |
plt.close() | |
return img | |
# Generate word cloud (return PIL Image) | |
def generate_wordcloud(text): | |
wordcloud = WordCloud(width=400, height=200, background_color="white").generate(text) | |
return wordcloud.to_image() # Standard PIL Image | |
# Generate QR code (return standard PIL Image) | |
def generate_qr(url): | |
qr = qrcode.QRCode(version=1, box_size=10, border=4) | |
qr.add_data(url) | |
qr.make(fit=True) | |
qr_img = qr.make_image(fill="black", back_color="white") # Returns qrcode.image.pil.PilImage | |
buf = io.BytesIO() | |
qr_img.save(buf, format="PNG") | |
buf.seek(0) | |
return Image.open(buf) # Convert to standard PIL Image | |
# Export history with proper file handling | |
def export_history(): | |
if not history: | |
return None | |
with tempfile.NamedTemporaryFile(delete=False, suffix=".txt", mode="w") as temp_file: | |
temp_file.write("\n".join(history)) | |
temp_path = temp_file.name | |
return temp_path | |
# Log feedback | |
def log_feedback(rating): | |
feedback_log.append(f"[{datetime.now().strftime('%H:%M:%S')}] Rating: {rating}/5") | |
return f"Feedback received! ({len(feedback_log)} total)" | |
# Theme toggle function | |
def toggle_theme(light_mode): | |
return "Theme switched to " + ("Light" if light_mode else "Dark") + ". Please refresh the page to apply." | |
# Gradio interface | |
with gr.Blocks(theme=gr.themes.Monochrome()) as interface: | |
gr.Markdown("# Sentify") | |
gr.Markdown("Next-level sentiment analysis with context, comparison, and more!") | |
with gr.Row(): | |
with gr.Column(scale=2): | |
model_dropdown = gr.Dropdown(choices=list(models.keys()), label="Select Model", value="DistilBERT") | |
custom_model = gr.File(label="Upload Custom Model (optional)", file_types=[".bin", ".pt"]) | |
text_input = gr.Textbox(label="Enter text or X URL", placeholder="Type text or paste an X URL...") | |
compare_input = gr.Textbox(label="Compare with (optional)", placeholder="Enter second text...") | |
audio_input = gr.Audio(label="Or Speak Your Text", type="filepath") | |
pos_words = gr.Textbox(label="Custom Positive Words", placeholder="love, great") | |
neg_words = gr.Textbox(label="Custom Negative Words", placeholder="hate, bad") | |
intensity_slider = gr.Slider(0.5, 1.0, value=0.7, label="Sentiment Intensity Threshold") | |
x_button = gr.Button("Analyze X Post") | |
with gr.Column(scale=3): | |
sentiment_output = gr.Textbox(label="Sentiment Result (Contextual)") | |
highlighted_output = gr.Textbox(label="Highlighted Text") | |
history_output = gr.Textbox(label="Analysis History (Last 5)", lines=5) | |
trend_output = gr.Image(label="Sentiment Timeline") | |
wordcloud_output = gr.Image(label="Word Cloud") | |
qr_output = gr.Image(label="Shareable QR Code") | |
compare_output = gr.Textbox(label="Comparative Analysis") | |
with gr.Row(): | |
export_button = gr.Button("Export History") | |
export_file = gr.File(label="Download History") | |
theme_toggle = gr.Checkbox(label="Light Mode", value=False) | |
theme_status = gr.Textbox(label="Theme Status", value="Dark (default)") | |
feedback_slider = gr.Slider(1, 5, step=1, label="Rate this analysis (1-5)") | |
feedback_output = gr.Textbox(label="Feedback Status") | |
gr.Examples( | |
examples=["I love this app! Itβs great.", "This is awful and sad.", "https://x.com/sample/post"], | |
inputs=[text_input] | |
) | |
# Event handlers | |
def audio_to_text(audio_file, model_name, pos_words, neg_words, intensity, custom_model_path): | |
text = "Simulated speech: I feel great today" if audio_file else "" | |
return analyze_sentiment(text, model_name, pos_words, neg_words, intensity, custom_model_path, source="audio") | |
text_input.change( | |
fn=analyze_sentiment, | |
inputs=[text_input, model_dropdown, pos_words, neg_words, intensity_slider, custom_model], | |
outputs=[sentiment_output, highlighted_output, history_output, trend_output, wordcloud_output, qr_output, compare_output, feedback_output] | |
) | |
x_button.click( | |
fn=fetch_x_post, | |
inputs=[text_input, model_dropdown, pos_words, neg_words, intensity_slider, custom_model], | |
outputs=[sentiment_output, highlighted_output, history_output, trend_output, wordcloud_output, qr_output, compare_output, feedback_output] | |
) | |
audio_input.change( | |
fn=audio_to_text, | |
inputs=[audio_input, model_dropdown, pos_words, neg_words, intensity_slider, custom_model], | |
outputs=[sentiment_output, highlighted_output, history_output, trend_output, wordcloud_output, qr_output, compare_output, feedback_output] | |
) | |
export_button.click(fn=export_history, inputs=None, outputs=export_file) | |
theme_toggle.change(fn=toggle_theme, inputs=theme_toggle, outputs=theme_status) | |
feedback_slider.change(fn=log_feedback, inputs=feedback_slider, outputs=feedback_output) | |
# Launch the app | |
interface.launch() |