File size: 10,611 Bytes
16fec7b
e0b5483
d816f20
 
 
 
 
 
 
 
 
 
 
 
 
16fec7b
d816f20
 
 
78e3cae
d816f20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78e3cae
d816f20
 
 
 
 
 
 
c9a1d9b
d816f20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16fec7b
d816f20
c9a1d9b
d816f20
16fec7b
d816f20
 
16fec7b
d816f20
16fec7b
d816f20
 
 
 
16fec7b
 
 
 
 
d816f20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1cd86c4
d816f20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16fec7b
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

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()