Masfiq Mahmud commited on
Commit
ea78f87
·
1 Parent(s): ccb3666

Add sentiment analysis feature using Gemini API and update UI for sentiment display

Browse files
Files changed (4) hide show
  1. .gitignore +1 -0
  2. app.py +58 -1
  3. static/style.css +31 -0
  4. templates/index.html +18 -0
.gitignore CHANGED
@@ -4,6 +4,7 @@
4
  __pycache__/
5
  *.py[cod]
6
  *$py.class
 
7
 
8
  # Distribution / packaging
9
  .Python
 
4
  __pycache__/
5
  *.py[cod]
6
  *$py.class
7
+ model_files/
8
 
9
  # Distribution / packaging
10
  .Python
app.py CHANGED
@@ -5,6 +5,8 @@ import torch
5
  import numpy as np
6
  from flask import Flask, request, jsonify, render_template
7
  import subprocess # For calling gdown
 
 
8
 
9
  os.environ['HF_HUB_DISABLE_SYMLINKS_WARNING'] = '1'
10
  import torch.nn as nn
@@ -333,6 +335,57 @@ def process_input(text, t5_tokenizer, encoder_tokenizer, char_to_id, current_max
333
  }
334
  # --- END HELPER METHODS ---
335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
 
337
  app = Flask(__name__)
338
 
@@ -412,7 +465,11 @@ def translate_text():
412
  translation = CFG.t5_tokenizer.decode(
413
  generated_ids[0], skip_special_tokens=True, clean_up_tokenization_spaces=True
414
  ).strip()
415
- return jsonify({"translation": translation})
 
 
 
 
416
  except Exception as e:
417
  print(f"Error during translation: {e}")
418
  # import traceback
 
5
  import numpy as np
6
  from flask import Flask, request, jsonify, render_template
7
  import subprocess # For calling gdown
8
+ import google.generativeai as genai
9
+ import time
10
 
11
  os.environ['HF_HUB_DISABLE_SYMLINKS_WARNING'] = '1'
12
  import torch.nn as nn
 
335
  }
336
  # --- END HELPER METHODS ---
337
 
338
+ # --- Gemini Configuration ---
339
+ GEMINI_API_KEY = "AIzaSyDmXVXoliNUt3Pnx4_lR2aOezLaItpIXz0"
340
+ try:
341
+ genai.configure(api_key=GEMINI_API_KEY)
342
+ except Exception as e:
343
+ print(f"Could not configure Gemini: {e}")
344
+
345
+ def get_sentiment_from_gemini(text: str, retries: int = 3, delay: int = 5) -> str:
346
+ """
347
+ Analyzes the sentiment of a given text using the Gemini API.
348
+ """
349
+ if not text.strip():
350
+ return "Neutral"
351
+
352
+ try:
353
+ model = genai.GenerativeModel(model_name="gemini-1.5-flash")
354
+
355
+ prompt = f"""Analyze the sentiment of the following Bengali text. Your response must be a single word: 'Positive', 'Negative', or 'Neutral'.
356
+ Text: "{text}"
357
+ Sentiment:"""
358
+
359
+ for attempt in range(retries):
360
+ try:
361
+ response = model.generate_content(
362
+ prompt,
363
+ generation_config=genai.types.GenerationConfig(
364
+ candidate_count=1,
365
+ max_output_tokens=5,
366
+ temperature=0.1
367
+ )
368
+ )
369
+ sentiment = response.text.strip().capitalize()
370
+
371
+ if sentiment in ["Positive", "Negative", "Neutral"]:
372
+ print(f"Gemini sentiment for '{text[:30]}...': {sentiment}")
373
+ return sentiment
374
+ else:
375
+ print(f"Gemini returned unexpected sentiment: '{sentiment}'. Attempt {attempt + 1}/{retries}")
376
+
377
+ except Exception as e:
378
+ print(f"Error calling Gemini API on attempt {attempt + 1}/{retries}: {e}")
379
+
380
+ if attempt < retries - 1:
381
+ print(f"Retrying in {delay} seconds...")
382
+ time.sleep(delay)
383
+
384
+ print("Gemini sentiment analysis failed after all retries.")
385
+ return "Unknown"
386
+ except Exception as e:
387
+ print(f"An error occurred during Gemini model initialization: {e}")
388
+ return "Error"
389
 
390
  app = Flask(__name__)
391
 
 
465
  translation = CFG.t5_tokenizer.decode(
466
  generated_ids[0], skip_special_tokens=True, clean_up_tokenization_spaces=True
467
  ).strip()
468
+
469
+ # Get sentiment analysis from Gemini
470
+ sentiment = get_sentiment_from_gemini(translation)
471
+
472
+ return jsonify({"translation": translation, "sentiment": sentiment})
473
  except Exception as e:
474
  print(f"Error during translation: {e}")
475
  # import traceback
static/style.css CHANGED
@@ -185,6 +185,37 @@ textarea:focus {
185
  line-height: 1.6;
186
  }
187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  /* Loading Spinner */
189
  .loading-spinner {
190
  display: flex;
 
185
  line-height: 1.6;
186
  }
187
 
188
+ /* Sentiment Badge Styles */
189
+ #sentimentContainer {
190
+ display: flex;
191
+ align-items: center;
192
+ gap: 8px;
193
+ font-size: 1em;
194
+ color: var(--text-color);
195
+ }
196
+
197
+ .sentiment-badge {
198
+ display: inline-block;
199
+ padding: 4px 12px;
200
+ border-radius: 16px;
201
+ font-size: 0.9em;
202
+ font-weight: 500;
203
+ color: white;
204
+ text-transform: capitalize;
205
+ }
206
+
207
+ .sentiment-positive {
208
+ background-color: #2e7d32;
209
+ }
210
+
211
+ .sentiment-negative {
212
+ background-color: #c62828;
213
+ }
214
+
215
+ .sentiment-neutral {
216
+ background-color: #616161;
217
+ }
218
+
219
  /* Loading Spinner */
220
  .loading-spinner {
221
  display: flex;
templates/index.html CHANGED
@@ -53,6 +53,9 @@
53
  </div>
54
  <div class="translation-result">
55
  <p id="translationResult"></p>
 
 
 
56
  <div class="loading-spinner" style="display: none;">
57
  <div class="spinner"></div>
58
  </div>
@@ -70,6 +73,8 @@
70
  const copyBtn = document.getElementById('copyBtn');
71
  const translationResult = document.getElementById('translationResult');
72
  const loadingSpinner = document.querySelector('.loading-spinner');
 
 
73
 
74
  // Character counter
75
  userInput.addEventListener('input', function() {
@@ -80,6 +85,9 @@
80
  clearBtn.addEventListener('click', function() {
81
  userInput.value = '';
82
  translationResult.textContent = '';
 
 
 
83
  charCount.textContent = '0/500';
84
  });
85
 
@@ -108,6 +116,8 @@
108
  loadingSpinner.style.display = 'flex';
109
  translateBtn.disabled = true;
110
  translationResult.style.opacity = '0.5';
 
 
111
 
112
  try {
113
  const response = await fetch('/translate', {
@@ -120,6 +130,14 @@
120
 
121
  const data = await response.json();
122
  translationResult.textContent = data.translation;
 
 
 
 
 
 
 
 
123
  translationResult.style.opacity = '1';
124
  } catch (error) {
125
  console.error('Error:', error);
 
53
  </div>
54
  <div class="translation-result">
55
  <p id="translationResult"></p>
56
+ <div id="sentimentContainer" style="display: none; margin-top: 15px;">
57
+ <strong>Sentiment:</strong> <span id="sentimentResult" class="sentiment-badge"></span>
58
+ </div>
59
  <div class="loading-spinner" style="display: none;">
60
  <div class="spinner"></div>
61
  </div>
 
73
  const copyBtn = document.getElementById('copyBtn');
74
  const translationResult = document.getElementById('translationResult');
75
  const loadingSpinner = document.querySelector('.loading-spinner');
76
+ const sentimentContainer = document.getElementById('sentimentContainer');
77
+ const sentimentResult = document.getElementById('sentimentResult');
78
 
79
  // Character counter
80
  userInput.addEventListener('input', function() {
 
85
  clearBtn.addEventListener('click', function() {
86
  userInput.value = '';
87
  translationResult.textContent = '';
88
+ sentimentContainer.style.display = 'none';
89
+ sentimentResult.textContent = '';
90
+ sentimentResult.className = 'sentiment-badge';
91
  charCount.textContent = '0/500';
92
  });
93
 
 
116
  loadingSpinner.style.display = 'flex';
117
  translateBtn.disabled = true;
118
  translationResult.style.opacity = '0.5';
119
+ translationResult.textContent = '';
120
+ sentimentContainer.style.display = 'none';
121
 
122
  try {
123
  const response = await fetch('/translate', {
 
130
 
131
  const data = await response.json();
132
  translationResult.textContent = data.translation;
133
+
134
+ if (data.sentiment && data.sentiment !== 'Error' && data.sentiment !== 'Unknown') {
135
+ sentimentResult.textContent = data.sentiment;
136
+ sentimentResult.className = 'sentiment-badge'; // Reset classes
137
+ sentimentResult.classList.add(`sentiment-${data.sentiment.toLowerCase()}`);
138
+ sentimentContainer.style.display = 'flex';
139
+ }
140
+
141
  translationResult.style.opacity = '1';
142
  } catch (error) {
143
  console.error('Error:', error);