File size: 17,028 Bytes
3bc0305
3dc2123
c13ab36
b516a41
3dc2123
9d5b24b
 
 
51ebb55
94adeb8
3dc2123
 
c13ab36
51ebb55
fbfc3a2
3dc2123
51ebb55
 
 
fbfc3a2
3dc2123
7e669a3
51ebb55
7e669a3
51ebb55
17c021b
 
 
7e669a3
 
 
 
 
 
51ebb55
7e669a3
 
51ebb55
7e669a3
 
51ebb55
7e669a3
 
 
17c021b
7e669a3
 
 
51ebb55
7e669a3
 
51ebb55
17c021b
51ebb55
 
 
7e669a3
 
 
17c021b
7e669a3
 
 
17c021b
 
 
7e669a3
 
 
17c021b
7e669a3
 
532ce4d
17c021b
7e669a3
17c021b
7e669a3
 
17c021b
532ce4d
17c021b
7e669a3
17c021b
 
7e669a3
 
 
532ce4d
 
 
 
 
51ebb55
7e669a3
532ce4d
7e669a3
532ce4d
51ebb55
532ce4d
17c021b
7e669a3
51ebb55
7e669a3
 
17c021b
7e669a3
 
51ebb55
 
17c021b
 
51ebb55
7e669a3
 
51ebb55
17c021b
7e669a3
 
 
 
17c021b
7e669a3
 
 
 
 
cd5851e
 
7e669a3
 
 
 
51ebb55
7e669a3
 
 
 
 
 
 
 
51ebb55
 
7e669a3
 
 
 
51ebb55
17c021b
532ce4d
 
 
 
51ebb55
7e669a3
 
 
 
 
532ce4d
 
 
17c021b
7e669a3
 
 
 
b516a41
532ce4d
b516a41
17c021b
 
 
7e669a3
 
 
17c021b
532ce4d
 
 
 
 
b516a41
cd5851e
 
532ce4d
17c021b
b516a41
17c021b
 
 
7e669a3
cd5851e
 
 
532ce4d
cd5851e
532ce4d
cd5851e
 
 
 
7e669a3
17c021b
 
7e669a3
 
51ebb55
7e669a3
 
51ebb55
 
 
 
7e669a3
 
 
 
 
 
 
 
 
 
 
 
 
532ce4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51ebb55
 
 
 
 
 
 
 
8a03f45
51ebb55
810cb59
51ebb55
8a03f45
51ebb55
810cb59
51ebb55
8a03f45
51ebb55
810cb59
51ebb55
 
 
810cb59
51ebb55
532ce4d
 
51ebb55
532ce4d
51ebb55
 
3dc2123
51ebb55
532ce4d
 
 
 
3dce035
51ebb55
 
532ce4d
 
 
 
3dce035
51ebb55
 
532ce4d
51ebb55
 
 
532ce4d
51ebb55
 
 
 
 
 
 
532ce4d
5dc3c79
51ebb55
532ce4d
 
 
 
3dce035
51ebb55
810cb59
 
b516a41
 
 
 
 
 
 
 
 
 
 
 
810cb59
 
51ebb55
532ce4d
17c021b
810cb59
8a03f45
 
 
b516a41
8a03f45
 
 
b516a41
8a03f45
 
 
22c09c7
 
 
532ce4d
22c09c7
cd5851e
 
532ce4d
cd5851e
 
b516a41
 
 
 
cd5851e
b516a41
 
 
 
 
 
 
 
 
532ce4d
cd5851e
810cb59
532ce4d
22c09c7
 
 
 
 
8a03f45
9d5b24b
8a03f45
51ebb55
 
8a03f45
51ebb55
8a03f45
51ebb55
 
8a03f45
532ce4d
51ebb55
 
9d5b24b
51ebb55
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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
import gradio as gr
from transformers import pipeline
import os
import random

# Añade esto para verificar la versión de Gradio en tiempo de ejecución
print(f"Gradio version at runtime: {gr.__version__}")

# --- Model Loading ---
MODEL_ID = "Light-Dav/sentiment-analysis-full-project"

try:
    sentiment_analyzer = pipeline("sentiment-analysis", model=MODEL_ID, top_k=None)
    model_loaded_successfully = True
    print("Sentiment analysis model loaded successfully.")
except Exception as e:
    print(f"Error loading model: {e}")
    sentiment_analyzer = None
    model_loaded_successfully = False
    print("Sentiment analysis model failed to load. Please check MODEL_ID and network connection.")

# --- Custom CSS with the NEW COLOR PALETTE and MAXIMUM COMPACTNESS ---
custom_css = """
/* RESETEO BÁSICO Y FONDOS GENERALES */
body {
    background-color: #1E2B38; /* Fondo General Oscuro */
    color: #FFFFFF; /* Blanco para texto principal */
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    padding: 0;
    margin: 0;
    overflow: hidden; /* Ocultar scrollbar si hay un pequeño desbordamiento */
    height: 100vh; /* Asegurar que el body ocupe toda la altura del viewport */
    display: flex;
    flex-direction: column;
}

/* CONTENEDOR PRINCIPAL DE GRADIO */
.gradio-container {
    box-shadow: 0 2px 4px rgba(0, 122, 204, 0.1); /* Sombra más sutil */
    border-radius: 6px; /* Borde más pequeño */
    overflow: hidden;
    background-color: #1E2B38; /* Fondo de la tarjeta, coincide con el body */
    padding: 10px; /* Reducir padding general del contenedor */
    margin-bottom: 5px; /* Reducir margen inferior */
    border: 1px solid #007ACC; /* Borde sutil con Azul Oscuro */
    flex-grow: 1; /* Permite que el contenedor ocupe el espacio disponible */
    display: flex;
    flex-direction: column;
}

/* AJUSTES DE TÍTULOS Y PÁRRAFOS */
h1, h2, h3 {
    color: #00BFFF; /* Azul Brillante para títulos */
    text-align: center;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    animation: fadeIn 1s ease-in-out;
    margin-top: 5px; /* Margen superior muy pequeño */
    margin-bottom: 8px; /* Margen inferior reducido */
    font-size: 1.4em; /* h1 más pequeño */
}
h2 { font-size: 1.1em; } /* h2 más pequeño */
h3 { font-size: 0.95em; } /* h3 más pequeño, casi texto normal */

p {
    color: #AAB7C4; /* Gris medio para texto secundario */
    text-align: center;
    margin-bottom: 10px; /* Margen debajo de los párrafos reducido */
    font-size: 0.8em; /* Tamaño de fuente más pequeño para párrafos */
    line-height: 1.3; /* Espaciado entre líneas para legibilidad */
}

/* COMPONENTES DE ENTRADA (TEXTBOX) */
/* Selector para el label del textbox */
.gr-textbox label, .gradio-output .label {
    color: #AAB7C4 !important; /* Gris medio para las etiquetas */
    font-weight: bold;
    font-size: 0.85em; /* Etiqueta más pequeña */
    margin-bottom: 3px; /* Espacio mínimo entre etiqueta y caja */
}
/* Selector para el textarea del textbox */
.gr-textbox textarea {
    background-color: rgba(0, 122, 204, 0.1); /* Azul Oscuro muy transparente */
    border: 1px solid #007ACC; /* Borde con Azul Oscuro */
    color: #FFFFFF; /* Texto blanco en el textarea */
    border-radius: 5px; /* Bordes más pequeños */
    padding: 6px; /* Reducir padding del textarea */
    font-size: 0.9em; /* Fuente más pequeña en el textarea */
    resize: vertical; /* Permite redimensionar verticalmente */
    min-height: 50px; /* Altura mínima */
    white-space: pre-wrap; /* Permite saltos de línea y respeta espacios */
    word-wrap: break-word; /* Rompe palabras largas */
    overflow-wrap: break-word; /* Estándar más moderno */
}


/* BOTONES PRINCIPALES */
/* Cambiado el color del botón primario */
.gr-button.primary {
    background-color: #00BFFF !important; /* ¡Azul Brillante para el botón primario (Acento)! */
    color: #1E2B38 !important; /* Texto oscuro para el botón primario */
    border-radius: 5px;
    transition: background-color 0.3s ease;
    padding: 7px 12px; /* Padding más pequeño del botón */
    font-size: 0.9em; /* Un poco más pequeño para el botón */
    font-weight: bold;
    margin-top: 8px; /* Reducir margen superior */
    margin-bottom: 5px; /* Añadir un pequeño margen inferior */
}
.gr-button.primary:hover {
    background-color: #007ACC !important; /* Azul Oscuro al pasar el ratón */
    color: #FFFFFF !important; /* Texto blanco al pasar el ratón */
}

/* COMPONENTES DE SALIDA (HTML, LABEL, JSON) */
.gradio-output {
    border: 1px solid #4A5B6C; /* Borde sutil con Gris Claro */
    border-radius: 5px; /* Borde más pequeño */
    padding: 8px; /* Reducir padding de la salida */
    margin-top: 8px; /* Reducir margen superior */
    background-color: rgba(0, 122, 204, 0.08); /* Fondo más sutil para la salida */
    color: #FFFFFF; /* Texto blanco en la salida */
    flex-grow: 1; /* Permitir que ocupe el espacio restante */
    display: flex;
    flex-direction: column;
    justify-content: center; /* Centrar verticalmente el contenido */
    min-height: 80px; /* Altura mínima para asegurar visibilidad */
    width: 100%; /* Asegurar que ocupe todo el ancho disponible */
    box-sizing: border-box; /* Incluir padding y borde en el ancho */
}
.gradio-output .label-text {
    color: #00BFFF !important; /* Color de acento para el texto del label (LABEL_1, LABEL_0, etc.) */
    font-weight: bold;
}
.gradio-output .label-score {
    color: #FFFFFF !important; /* Blanco para el score del label */
}
.gradio-output .label-container {
    padding-bottom: 5px !important; /* Reducir padding entre elementos del label */
}

/* DISPLAY DE SENTIMIENTO (DENTRO DE HTML OUTPUT) */
.sentiment-display {
    text-align: center;
    padding: 6px; /* Reducir padding */
    border-radius: 4px; /* Borde más pequeño */
    margin-top: 3px; /* Margen muy pequeño */
    font-size: 1em; /* Un poco más pequeño */
    font-weight: bold;
    color: #FFFFFF; /* Texto blanco para todos los sentimientos */
    /* Asegurar que el texto se envuelva */
    white-space: normal;
    word-wrap: break-word;
    overflow-wrap: break-word;
}
.sentiment-display p { /* Estilo específico para el párrafo de descripción */
    font-size: 0.75em; /* Tamaño más pequeño para la descripción */
    margin-top: 3px;
    margin-bottom: 0;
    line-height: 1.2;
    white-space: normal; /* Asegurar que el párrafo se envuelva */
    word-wrap: break-word;
    overflow-wrap: break-word;
}
.sentiment-positive { background-color: #28a745; } /* Mantener estos colores por claridad de sentimiento */
.sentiment-negative { background-color: #dc3545; }
.sentiment-neutral { background-color: #007BFF; }

/* NUEVOS BOTONES DE EJEMPLO DIRECTOS (NO gr.Examples) */
/* Ajustes para el texto de los botones de ejemplo */
.example-button {
    background-color: #4A5B6C !important; /* Gris Claro para los botones de ejemplo */
    color: #FFFFFF !important;
    border: 1px solid #4A5B6C;
    border-radius: 3px; /* Borde más pequeño */
    padding: 5px 8px; /* Padding más pequeño */
    margin: 2px; /* Margen mínimo entre botones */
    transition: background-color 0.3s ease;
    font-size: 0.7em; /* ¡Tamaño de fuente más pequeño para los ejemplos! */
    white-space: normal; /* ¡Permitir que el texto se rompa en varias líneas! */
    word-wrap: break-word; /* Rompe palabras largas */
    overflow-wrap: break-word; /* Estándar más moderno */
    flex-shrink: 1; /* Permitir que se encojan si es necesario */
    cursor: pointer; /* Indicar que son clickeables */
    flex-grow: 1; /* Permitir que los botones de ejemplo crezcan para llenar el espacio */
    min-width: 80px; /* Asegurar un ancho mínimo para cada botón */
    text-align: center; /* Centrar el texto dentro del botón */
}
.example-button:hover {
    background-color: #007ACC !important; /* Azul Oscuro al pasar el ratón por los ejemplos */
    border-color: #00BFFF !important;
}

/* Contenedor de los botones de ejemplo para forzar el "wrap" con flexbox */
.example-buttons-container {
    display: flex;
    flex-wrap: wrap; /* ¡Esto simula el wrap! */
    justify-content: center; /* Centrar los botones si no llenan la línea */
    align-items: stretch; /* Asegura que los botones tengan la misma altura si el texto envuelve */
    margin-top: 5px; /* Pequeño margen superior */
    margin-bottom: 10px; /* Pequeño margen inferior */
}

/* LÍNEAS DIVISORIAS */
hr {
    border-top: 1px solid #4A5B6C; /* Línea divisoria con Gris Claro */
    margin-top: 10px; /* Reducir margen superior */
    margin-bottom: 10px; /* Reducir margen inferior */
}

/* ANIMACIÓN */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* OCULTAR EL PIE DE PÁGINA DE GRADIO (CRUCIAL PARA IFRAMES PEQUEÑOS) */
footer {
    opacity: 0; /* Hacerlo invisible */
    height: 0 !important; /* Eliminar su altura */
    margin: 0 !important; /* Eliminar sus márgenes */
    padding: 0 !important; /* Eliminar su padding */
    pointer-events: none; /* No interactuable */
    visibility: hidden; /* Asegurar que no ocupe espacio visual */
}
.gradio-app > div > :last-child:not(.gradio-container) { /* Apuntar al elemento del pie de página */
    display: none !important; /* Otra forma de ocultar completamente si lo anterior no funciona */
}

/* Ajustes para el contenedor de la salida de Label (para que el texto interno envuelva) */
.gradio-output > .label {
    white-space: normal !important; /* Permitir que el texto envuelva */
    word-wrap: break-word !important; /* Romper palabras largas */
    overflow-wrap: break-word !important; /* Estándar moderno */
}
/* Asegurarse de que el texto dentro de los resultados de Gradio (si no es HTML) también envuelva */
.gradio-output > div {
    white-space: normal !important;
    word-wrap: break-word !important;
    overflow-wrap: break-word !important;
}

/* Ajustes para el texto dentro del gr.Label (Confidence Scores) si se hace visible */
.gr-label-container {
    font-size: 0.8em; /* Reducir la fuente de los scores de confianza */
}
.gr-label-container .label-text {
    font-size: 1em !important; /* Mantener la etiqueta del score legible */
}
.gr-label-container .label-score {
    font-size: 0.9em !important; /* Un poco más pequeño que la etiqueta */
}

"""

# --- Helper Function for Sentiment Interpretation ---
def interpret_sentiment(label, score):
    emoji = ""
    description = ""
    color_class = ""

    if label.lower() == "positive" or label.lower() == "label_2":
        emoji = "😊"
        description = "Positive sentiment detected."
        color_class = "sentiment-positive"
    elif label.lower() == "negative" or label.lower() == "label_0":
        emoji = "😠"
        description = "Negative sentiment detected."
        color_class = "sentiment-negative"
    elif label.lower() == "neutral" or label.lower() == "label_1":
        emoji = "😐"
        description = "Neutral sentiment detected."
        color_class = "sentiment-neutral"
    else:
        emoji = "❓"
        description = "Sentiment not determined."
        color_class = ""
        
    # El HTML de salida también debe permitir el salto de línea y reducir la fuente del párrafo
    return f"<div class='sentiment-display {color_class}'>{emoji} {label.upper()} ({score:.2f})</div>" + \
           f"<p style='color: #FFFFFF; font-size: 0.7em; margin-top: 3px; margin-bottom: 0; line-height: 1.2; white-space: normal; word-wrap: break-word;'>{description}</p>"

# --- Sentiment Analysis Function ---
def analyze_sentiment(text):
    if not model_loaded_successfully:
        return (    
            "<div class='sentiment-display'>⚠️ Model Error ⚠️</div><p style='color: #FFFFFF; font-size: 0.7em;'>Model not loaded.</p>",
            {},    
            {"error": "Model loading failed."}    
        )

    if not text.strip():
        return (    
            "<div class='sentiment-display'>✍️ Enter text ✍️</div><p style='color: #FFFFFF; font-size: 0.7em;'>Type text to analyze.</p>",
            {},    
            {"info": "No text entered."}    
        )

    try:
        results = sentiment_analyzer(text)[0]    

        results_sorted = sorted(results, key=lambda x: x['score'], reverse=True)

        top_sentiment = results_sorted[0]    
        label = top_sentiment['label']
        score = top_sentiment['score']

        confidence_scores_output = {item['label']: item['score'] for item in results}

        overall_sentiment_display = interpret_sentiment(label, score)

        return (overall_sentiment_display, confidence_scores_output, results)    
        
    except Exception as e:
        return (    
            f"<div class='sentiment-display'>❌ Error ❌</div><p style='color: #FFFFFF; font-size: 0.7em;'>Analysis failed.</p>",
            {},    
            {"error_message": str(e)}    
        )

# --- Lista completa de ejemplos para selección aleatoria ---
ALL_EXAMPLES = [
    "The product quality is absolutely outstanding and worth every penny!",
    "I found the customer support unhelpful and quite rude, a terrible experience.",
    "The new software update introduced several bugs, making it very unstable.",
    "This book is a captivating read, I couldn't put it down!",
    "The delivery was late, and the package arrived damaged.",
    "Despite the bad reviews, I thoroughly enjoyed the film and its unique plot.",
    "The instructions were unclear, leading to a lot of confusion during assembly.",
    "What a delicious meal! Every dish was prepared to perfection.",
    "I'm very disappointed with the recent policy changes; they are unfair.",
    "The new art exhibition is thought-provoking and visually stunning.",
    "Traffic was unexpectedly heavy, causing significant delays.",
    "Overall, a solid performance, though there's room for improvement."
]

# --- Gradio Interface ---
with gr.Blocks(css=custom_css, theme=None) as demo:    
    gr.Markdown("<h1 style='color: #00BFFF;'>🌌 Sentiment Analyzer 🌌</h1>")
    gr.Markdown("<p style='color: #AAB7C4;'>Analyze the emotional tone of your English text.</p>")

    with gr.Column(elem_classes="gradio-container"):
        text_input = gr.Textbox(
            lines=2,
            placeholder="Type your English text here...",
            label="Your Text",
            interactive=True,
            value=random.choice(ALL_EXAMPLES) # Establece un ejemplo aleatorio inicial
        )
        analyze_btn = gr.Button("Analyze Sentiment", variant="primary")

        # --- Definir las salidas ANTES de usarlas en los eventos de los botones ---
        overall_sentiment_output = gr.HTML(label="Overall Sentiment")
        confidence_scores_output = gr.Label(num_top_classes=3, label="Confidence Scores", visible=False)
        raw_output = gr.JSON(label="Raw Model Output", visible=False)    

        # --- Título para los ejemplos ---
        gr.Markdown("<h3 style='color: #00BFFF; margin-top: 5px; margin-bottom: 5px;'>Try examples:</h3>")
            
        # Contenedor para los botones de ejemplo para manejar el "wrap" vía CSS
        with gr.Row(elem_classes="example-buttons-container"):
            # Generar 3 botones de ejemplo aleatorios
            for example_text in random.sample(ALL_EXAMPLES, 3):
                gr.Button(
                    example_text,
                    elem_classes="example-button"
                ).click(
                    fn=lambda x: x, # Función simple para pasar el texto del botón al textbox
                    inputs=[gr.State(example_text)], # Usamos gr.State para pasar el texto del botón
                    outputs=text_input,
                ).then(
                    fn=analyze_sentiment, # Luego de cargar el texto, ejecuta el análisis
                    inputs=text_input,
                    outputs=[overall_sentiment_output, confidence_scores_output, raw_output]
                )
            
        gr.Markdown("<hr>") # Separador
        gr.Markdown("<h2 style='color: #00BFFF;'>📊 Results</h2>")
            
        # Las salidas ya están definidas arriba, ahora solo las colocamos en el layout
        # (overall_sentiment_output ya se definió, y su visibilidad y escala se controlan arriba)
        # No es necesario re-definirlas aquí, solo asegurarnos de que estén en el flujo de la interfaz
        # (Gradio las "conoce" porque ya han sido creadas).
        pass # No es necesario poner nada aquí, ya las variables fueron declaradas arriba

    # --- Event Listeners ---
    analyze_btn.click(
        fn=analyze_sentiment,
        inputs=text_input,
        outputs=[overall_sentiment_output, confidence_scores_output, raw_output]
    )
    text_input.change(
        fn=analyze_sentiment,
        inputs=text_input,
        outputs=[overall_sentiment_output, confidence_scores_output, raw_output],
        # live=True    
    )

# Launch the Gradio application
demo.launch()