adnaan05 KhaqanNasir commited on
Commit
dbdcde9
·
verified ·
1 Parent(s): 7b8ebcc

Update src/app.py (#18)

Browse files

- Update src/app.py (d49dd8a2a1db9de373664fa2fc53aa508e51f7bb)


Co-authored-by: Muhammad Khaqan Nasir <[email protected]>

Files changed (1) hide show
  1. src/app.py +113 -74
src/app.py CHANGED
@@ -35,11 +35,11 @@ from src.models.hybrid_model import HybridFakeNewsDetector
35
  from src.config.config import *
36
  from src.data.preprocessor import TextPreprocessor
37
 
38
- # Custom CSS for clean, modern styling
39
  st.markdown("""
40
  <style>
41
  /* Import Google Fonts */
42
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
43
 
44
  /* Global Styles */
45
  * {
@@ -50,9 +50,9 @@ st.markdown("""
50
 
51
  .stApp {
52
  font-family: 'Inter', sans-serif;
53
- background: #f8fafc;
54
  min-height: 100vh;
55
- color: #2d3748;
56
  }
57
 
58
  /* Hide Streamlit elements */
@@ -64,37 +64,45 @@ st.markdown("""
64
 
65
  /* Container */
66
  .container {
67
- max-width: 1200px;
68
  margin: 0 auto;
69
- padding: 2rem;
70
  }
71
 
72
  /* Header */
73
  .header {
74
  background: #ffffff;
75
- padding: 1.5rem 2rem;
76
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
77
  position: sticky;
78
  top: 0;
79
  z-index: 1000;
80
  }
81
 
82
  .header-title {
83
- font-size: 1.8rem;
84
- font-weight: 700;
85
  color: #1a202c;
 
 
 
86
  }
87
 
88
  /* Hero Section */
89
  .hero {
90
  display: flex;
91
- gap: 2rem;
92
- margin-bottom: 3rem;
 
 
 
 
 
93
  }
94
 
95
  .hero-left {
96
  flex: 1;
97
- padding: 2rem;
98
  }
99
 
100
  .hero-right {
@@ -108,95 +116,117 @@ st.markdown("""
108
  max-width: 100%;
109
  height: auto;
110
  border-radius: 12px;
111
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
 
 
 
 
 
112
  }
113
 
114
  .hero-title {
115
- font-size: 2.5rem;
116
- font-weight: 700;
117
  color: #1a202c;
118
- margin-bottom: 1rem;
 
119
  }
120
 
121
  .hero-text {
122
- font-size: 1.1rem;
123
  color: #4a5568;
124
- line-height: 1.6;
 
125
  }
126
 
127
  /* About Section */
128
  .about-section {
129
  margin-bottom: 3rem;
130
  text-align: center;
 
131
  }
132
 
133
  .about-title {
134
- font-size: 2rem;
135
- font-weight: 600;
136
  color: #1a202c;
137
  margin-bottom: 1rem;
138
  }
139
 
140
  .about-text {
141
- font-size: 1rem;
142
  color: #4a5568;
143
  line-height: 1.6;
144
- max-width: 800px;
145
  margin: 0 auto;
146
  }
147
 
148
  /* Input Section */
149
  .input-container {
150
- max-width: 800px;
151
  margin: 0 auto;
 
 
 
 
152
  }
153
 
154
  .stTextArea > div > div > textarea {
155
- border-radius: 12px !important;
156
- border: 1px solid #e2e8f0 !important;
157
- padding: 1rem !important;
158
  font-size: 1rem !important;
159
  font-family: 'Inter', sans-serif !important;
160
- background: #ffffff !important;
161
- min-height: 150px !important;
 
162
  }
163
 
164
  .stTextArea > div > div > textarea:focus {
165
- border-color: #667eea !important;
166
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
167
  outline: none !important;
168
  }
169
 
 
 
 
 
 
170
  .stButton > button {
171
- background: #667eea !important;
172
  color: white !important;
173
- border-radius: 8px !important;
174
- padding: 0.75rem 2rem !important;
175
- font-size: 1rem !important;
176
- font-weight: 500 !important;
177
  font-family: 'Inter', sans-serif !important;
178
  transition: all 0.3s ease !important;
 
179
  width: 100% !important;
 
180
  }
181
 
182
  .stButton > button:hover {
183
- background: #5a6fd8 !important;
184
  transform: translateY(-2px) !important;
 
185
  }
186
 
187
  /* Results Section */
188
  .results-container {
189
  margin-top: 2rem;
190
- padding: 1.5rem;
191
  background: #ffffff;
192
  border-radius: 12px;
193
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
194
  }
195
 
196
  .result-card {
197
  padding: 1.5rem;
198
- border-radius: 12px;
199
- border-left: 4px solid transparent;
 
200
  }
201
 
202
  .fake-news {
@@ -205,36 +235,41 @@ st.markdown("""
205
  }
206
 
207
  .real-news {
208
- background: #f0fff4;
209
- border-left-color: #38a169;
210
  }
211
 
212
  .prediction-badge {
213
  font-weight: 600;
214
- font-size: 1rem;
215
  margin-bottom: 1rem;
 
 
 
216
  }
217
 
218
  .confidence-score {
219
  font-weight: 600;
220
  margin-left: auto;
 
221
  }
222
 
223
  /* Chart Containers */
224
  .chart-container {
225
  padding: 1.5rem;
226
- border-radius: 12px;
227
  background: #ffffff;
228
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
229
- margin: 1rem 0;
230
  }
231
 
232
  /* Footer */
233
  .footer {
234
- margin-top: 3rem;
235
- padding: 1rem;
236
  text-align: center;
237
- border-top: 1px solid #e2e8f0;
 
238
  }
239
  </style>
240
  """, unsafe_allow_html=True)
@@ -304,17 +339,18 @@ def plot_confidence(probabilities):
304
  text=[f'{p:.1%}' for p in probabilities.values()],
305
  textposition='auto',
306
  marker=dict(
307
- color=['#38a169', '#ef4444'],
308
  line=dict(color='#ffffff', width=1),
309
  ),
310
  )
311
  ])
312
  fig.update_layout(
313
- title={'text': 'Prediction Confidence', 'x': 0.5, 'xanchor': 'center'},
314
- xaxis=dict(title='Classification'),
315
- yaxis=dict(title='Probability', range=[0, 1], tickformat='.0%'),
316
  template='plotly_white',
317
- height=300
 
318
  )
319
  return fig
320
 
@@ -325,7 +361,7 @@ def plot_attention(text, attention_weights):
325
  if isinstance(attention_weights, (list, np.ndarray)):
326
  attention_weights = np.array(attention_weights).flatten()
327
  normalized_weights = attention_weights / max(attention_weights) if max(attention_weights) > 0 else attention_weights
328
- colors = [f'rgba(102, 126, 234, {0.3 + 0.7 * float(w)})' for w in normalized_weights]
329
  fig = go.Figure(data=[
330
  go.Bar(
331
  x=tokens,
@@ -336,11 +372,12 @@ def plot_attention(text, attention_weights):
336
  )
337
  ])
338
  fig.update_layout(
339
- title={'text': 'Attention Weights', 'x': 0.5, 'xanchor': 'center'},
340
- xaxis=dict(title='Words', tickangle=45),
341
- yaxis=dict(title='Attention Score'),
342
- template='plotly_white',
343
- height=400
 
344
  )
345
  return fig
346
 
@@ -349,7 +386,7 @@ def main():
349
  st.markdown("""
350
  <div class="header">
351
  <div class="container">
352
- <h1 class="header-title">TruthCheck</h1>
353
  </div>
354
  </div>
355
  """, unsafe_allow_html=True)
@@ -359,9 +396,9 @@ def main():
359
  <div class="container">
360
  <div class="hero">
361
  <div class="hero-left">
362
- <h2 class="hero-title">Advanced Fake News Detection</h2>
363
  <p class="hero-text">
364
- Use our AI-powered tool to verify news articles instantly. Powered by BERT and BiLSTM, TruthCheck provides accurate, transparent analysis of news authenticity.
365
  </p>
366
  </div>
367
  <div class="hero-right">
@@ -377,7 +414,7 @@ def main():
377
  <div class="about-section">
378
  <h2 class="about-title">About TruthCheck</h2>
379
  <p class="about-text">
380
- TruthCheck leverages a hybrid BERT-BiLSTM model to detect fake news with high accuracy. Simply paste a news article, and our AI will analyze its authenticity, providing confidence scores and attention insights.
381
  </p>
382
  </div>
383
  </div>
@@ -386,9 +423,9 @@ def main():
386
  # Input Section
387
  st.markdown('<div class="container"><div class="input-container">', unsafe_allow_html=True)
388
  news_text = st.text_area(
389
- "Paste News Article",
390
- height=150,
391
- placeholder="Paste your news article here for AI analysis...",
392
  key="news_input"
393
  )
394
  st.markdown('</div>', unsafe_allow_html=True)
@@ -397,7 +434,7 @@ def main():
397
  st.markdown('<div class="container">', unsafe_allow_html=True)
398
  col1, col2, col3 = st.columns([1, 2, 1])
399
  with col2:
400
- analyze_button = st.button("Analyze Article", key="analyze_button")
401
  st.markdown('</div>', unsafe_allow_html=True)
402
 
403
  if analyze_button:
@@ -408,20 +445,20 @@ def main():
408
  st.markdown('<div class="container"><div class="results-container">', unsafe_allow_html=True)
409
 
410
  # Prediction Result
411
- col1, col2 = st.columns([1, 1])
412
  with col1:
413
  if result['label'] == 'FAKE':
414
  st.markdown(f'''
415
  <div class="result-card fake-news">
416
- <div class="prediction-badge">FAKE NEWS DETECTED <span class="confidence-score">{result["confidence"]:.1%}</span></div>
417
- <p>Our AI model has identified this content as likely misinformation based on linguistic patterns and content analysis.</p>
418
  </div>
419
  ''', unsafe_allow_html=True)
420
  else:
421
  st.markdown(f'''
422
  <div class="result-card real-news">
423
- <div class="prediction-badge">AUTHENTIC NEWS <span class="confidence-score">{result["confidence"]:.1%}</span></div>
424
- <p>This content appears to be legitimate news based on professional writing style and factual consistency.</p>
425
  </div>
426
  ''', unsafe_allow_html=True)
427
 
@@ -435,7 +472,9 @@ def main():
435
  st.plotly_chart(plot_attention(news_text, result['attention_weights']), use_container_width=True)
436
  st.markdown('</div></div></div>', unsafe_allow_html=True)
437
  except Exception as e:
 
438
  st.error(f"Error: {str(e)}. Please try again or contact support.")
 
439
  else:
440
  st.markdown('<div class="container">', unsafe_allow_html=True)
441
  st.error("Please enter a news article (at least 10 words) for analysis.")
@@ -444,7 +483,7 @@ def main():
444
  # Footer
445
  st.markdown("""
446
  <div class="footer">
447
- <p style="text-align: center; font-weight: 600; font-size: 16px;">💻 Developed with ❤️ using Streamlit | © 2024</p>
448
  </div>
449
  """, unsafe_allow_html=True)
450
 
 
35
  from src.config.config import *
36
  from src.data.preprocessor import TextPreprocessor
37
 
38
+ # Custom CSS for enhanced, modern styling
39
  st.markdown("""
40
  <style>
41
  /* Import Google Fonts */
42
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
43
 
44
  /* Global Styles */
45
  * {
 
50
 
51
  .stApp {
52
  font-family: 'Inter', sans-serif;
53
+ background: #ffffff;
54
  min-height: 100vh;
55
+ color: #1a202c;
56
  }
57
 
58
  /* Hide Streamlit elements */
 
64
 
65
  /* Container */
66
  .container {
67
+ max-width: 1280px;
68
  margin: 0 auto;
69
+ padding: 2rem 1.5rem;
70
  }
71
 
72
  /* Header */
73
  .header {
74
  background: #ffffff;
75
+ padding: 1rem 2rem;
76
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
77
  position: sticky;
78
  top: 0;
79
  z-index: 1000;
80
  }
81
 
82
  .header-title {
83
+ font-size: 2rem;
84
+ font-weight: 800;
85
  color: #1a202c;
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 0.5rem;
89
  }
90
 
91
  /* Hero Section */
92
  .hero {
93
  display: flex;
94
+ align-items: center;
95
+ gap: 3rem;
96
+ margin-bottom: 4rem;
97
+ background: linear-gradient(135deg, #f8fafc 0%, #edf2f7 100%);
98
+ padding: 4rem 2rem;
99
+ border-radius: 16px;
100
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
101
  }
102
 
103
  .hero-left {
104
  flex: 1;
105
+ padding: 1rem;
106
  }
107
 
108
  .hero-right {
 
116
  max-width: 100%;
117
  height: auto;
118
  border-radius: 12px;
119
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
120
+ transition: transform 0.3s ease;
121
+ }
122
+
123
+ .hero-right img:hover {
124
+ transform: scale(1.02);
125
  }
126
 
127
  .hero-title {
128
+ font-size: 3rem;
129
+ font-weight: 800;
130
  color: #1a202c;
131
+ margin-bottom: 1.5rem;
132
+ line-height: 1.2;
133
  }
134
 
135
  .hero-text {
136
+ font-size: 1.2rem;
137
  color: #4a5568;
138
+ line-height: 1.7;
139
+ max-width: 500px;
140
  }
141
 
142
  /* About Section */
143
  .about-section {
144
  margin-bottom: 3rem;
145
  text-align: center;
146
+ padding: 2rem;
147
  }
148
 
149
  .about-title {
150
+ font-size: 2.2rem;
151
+ font-weight: 700;
152
  color: #1a202c;
153
  margin-bottom: 1rem;
154
  }
155
 
156
  .about-text {
157
+ font-size: 1.1rem;
158
  color: #4a5568;
159
  line-height: 1.6;
160
+ max-width: 700px;
161
  margin: 0 auto;
162
  }
163
 
164
  /* Input Section */
165
  .input-container {
166
+ max-width: 900px;
167
  margin: 0 auto;
168
+ padding: 1.5rem;
169
+ background: #ffffff;
170
+ border-radius: 12px;
171
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
172
  }
173
 
174
  .stTextArea > div > div > textarea {
175
+ border-radius: 10px !important;
176
+ border: 1px solid #d1d5db !important;
177
+ padding: 1.2rem !important;
178
  font-size: 1rem !important;
179
  font-family: 'Inter', sans-serif !important;
180
+ background: #f9fafb !important;
181
+ min-height: 180px !important;
182
+ transition: all 0.3s ease !important;
183
  }
184
 
185
  .stTextArea > div > div > textarea:focus {
186
+ border-color: #6366f1 !important;
187
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1) !important;
188
  outline: none !important;
189
  }
190
 
191
+ .stTextArea > div > div > textarea::placeholder {
192
+ color: #9ca3af !important;
193
+ }
194
+
195
+ /* Button Styling */
196
  .stButton > button {
197
+ background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%) !important;
198
  color: white !important;
199
+ border-radius: 10px !important;
200
+ padding: 0.8rem 2.5rem !important;
201
+ font-size: 1.1rem !important;
202
+ font-weight: 600 !important;
203
  font-family: 'Inter', sans-serif !important;
204
  transition: all 0.3s ease !important;
205
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3) !important;
206
  width: 100% !important;
207
+ border: none !important;
208
  }
209
 
210
  .stButton > button:hover {
211
+ background: linear-gradient(135deg, #4f46e5 0%, #4338ca 100%) !important;
212
  transform: translateY(-2px) !important;
213
+ box-shadow: 0 6px 16px rgba(99, 102, 241, 0.4) !important;
214
  }
215
 
216
  /* Results Section */
217
  .results-container {
218
  margin-top: 2rem;
219
+ padding: 2rem;
220
  background: #ffffff;
221
  border-radius: 12px;
222
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
223
  }
224
 
225
  .result-card {
226
  padding: 1.5rem;
227
+ border-radius: 10px;
228
+ border-left: 5px solid transparent;
229
+ margin-bottom: 1rem;
230
  }
231
 
232
  .fake-news {
 
235
  }
236
 
237
  .real-news {
238
+ background: #ecfdf5;
239
+ border-left-color: #10b981;
240
  }
241
 
242
  .prediction-badge {
243
  font-weight: 600;
244
+ font-size: 1.1rem;
245
  margin-bottom: 1rem;
246
+ display: flex;
247
+ align-items: center;
248
+ gap: 0.5rem;
249
  }
250
 
251
  .confidence-score {
252
  font-weight: 600;
253
  margin-left: auto;
254
+ font-size: 1.1rem;
255
  }
256
 
257
  /* Chart Containers */
258
  .chart-container {
259
  padding: 1.5rem;
260
+ border-radius: 10px;
261
  background: #ffffff;
262
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
263
+ margin: 1.5rem 0;
264
  }
265
 
266
  /* Footer */
267
  .footer {
268
+ margin-top: 4rem;
269
+ padding: 1.5rem;
270
  text-align: center;
271
+ border-top: 1px solid #e5e7eb;
272
+ background: #f8fafc;
273
  }
274
  </style>
275
  """, unsafe_allow_html=True)
 
339
  text=[f'{p:.1%}' for p in probabilities.values()],
340
  textposition='auto',
341
  marker=dict(
342
+ color=['#10b981', '#ef4444'],
343
  line=dict(color='#ffffff', width=1),
344
  ),
345
  )
346
  ])
347
  fig.update_layout(
348
+ title={'text': 'Prediction Confidence', 'x': 0.5, 'xanchor': 'center', 'font': {'size': 20}},
349
+ xaxis=dict(title='Classification', titlefont={'size': 14}, tickfont={'size': 12}),
350
+ yaxis=dict(title='Probability', range=[0, 1], tickformat='.0%', titlefont={'size': 14}, tickfont={'size': 12}),
351
  template='plotly_white',
352
+ height=350,
353
+ margin=dict(t=80, b=80)
354
  )
355
  return fig
356
 
 
361
  if isinstance(attention_weights, (list, np.ndarray)):
362
  attention_weights = np.array(attention_weights).flatten()
363
  normalized_weights = attention_weights / max(attention_weights) if max(attention_weights) > 0 else attention_weights
364
+ colors = [f'rgba(99, 102, 241, {0.4 + 0.6 * float(w)})' for w in normalized_weights]
365
  fig = go.Figure(data=[
366
  go.Bar(
367
  x=tokens,
 
372
  )
373
  ])
374
  fig.update_layout(
375
+ title={'text': 'Attention Weights', 'x': 0.5, 'xanchor': 'center', 'font': {'size': 20}},
376
+ xaxis=dict(title='Words', tickangle=45, titlefont={'size': 14}, tickfont={'size': 12}),
377
+ yaxis=dict(title='Attention Score', titlefont={'size': 14}, tickfont={'size': 12}),
378
+ template='plotly_white/pubs/DeepSearch/2025-07-25/4f0e5e9c-7e3f-4d87-9b50-3d7f1c7f5e6a.txt',
379
+ height=400,
380
+ margin=dict(t=80, b=100)
381
  )
382
  return fig
383
 
 
386
  st.markdown("""
387
  <div class="header">
388
  <div class="container">
389
+ <h1 class="header-title">🛡️ TruthCheck</h1>
390
  </div>
391
  </div>
392
  """, unsafe_allow_html=True)
 
396
  <div class="container">
397
  <div class="hero">
398
  <div class="hero-left">
399
+ <h2 class="hero-title">Instant Fake News Detection</h2>
400
  <p class="hero-text">
401
+ Discover the truth behind news articles with our cutting-edge AI. Powered by a hybrid BERT-BiLSTM model, TruthCheck delivers fast, accurate, and transparent analysis of news authenticity.
402
  </p>
403
  </div>
404
  <div class="hero-right">
 
414
  <div class="about-section">
415
  <h2 class="about-title">About TruthCheck</h2>
416
  <p class="about-text">
417
+ TruthCheck combines advanced BERT and BiLSTM technologies to detect fake news with over 95% accuracy. Paste any news article below to receive a detailed analysis, including confidence scores and attention insights, in seconds.
418
  </p>
419
  </div>
420
  </div>
 
423
  # Input Section
424
  st.markdown('<div class="container"><div class="input-container">', unsafe_allow_html=True)
425
  news_text = st.text_area(
426
+ "Analyze a News Article",
427
+ height=180,
428
+ placeholder="Paste your news article here for instant AI analysis...",
429
  key="news_input"
430
  )
431
  st.markdown('</div>', unsafe_allow_html=True)
 
434
  st.markdown('<div class="container">', unsafe_allow_html=True)
435
  col1, col2, col3 = st.columns([1, 2, 1])
436
  with col2:
437
+ analyze_button = st.button("🔍 Analyze Now", key="analyze_button")
438
  st.markdown('</div>', unsafe_allow_html=True)
439
 
440
  if analyze_button:
 
445
  st.markdown('<div class="container"><div class="results-container">', unsafe_allow_html=True)
446
 
447
  # Prediction Result
448
+ col1, col2 = st.columns([1, 1], gap="medium")
449
  with col1:
450
  if result['label'] == 'FAKE':
451
  st.markdown(f'''
452
  <div class="result-card fake-news">
453
+ <div class="prediction-badge">🚨 Fake News Detected <span class="confidence-score">{result["confidence"]:.1%}</span></div>
454
+ <p>Our AI has identified this content as likely misinformation based on linguistic patterns, structural analysis, and content inconsistencies.</p>
455
  </div>
456
  ''', unsafe_allow_html=True)
457
  else:
458
  st.markdown(f'''
459
  <div class="result-card real-news">
460
+ <div class="prediction-badge">✅ Authentic News <span class="confidence-score">{result["confidence"]:.1%}</span></div>
461
+ <p>This content appears to be legitimate based on professional writing style, factual consistency, and structural integrity.</p>
462
  </div>
463
  ''', unsafe_allow_html=True)
464
 
 
472
  st.plotly_chart(plot_attention(news_text, result['attention_weights']), use_container_width=True)
473
  st.markdown('</div></div></div>', unsafe_allow_html=True)
474
  except Exception as e:
475
+ st.markdown('<div class="container">', unsafe_allow_html=True)
476
  st.error(f"Error: {str(e)}. Please try again or contact support.")
477
+ st.markdown('</div>', unsafe_allow_html=True)
478
  else:
479
  st.markdown('<div class="container">', unsafe_allow_html=True)
480
  st.error("Please enter a news article (at least 10 words) for analysis.")
 
483
  # Footer
484
  st.markdown("""
485
  <div class="footer">
486
+ <p style="text-align: center; font-weight: 600; font-size: 16px;">💻 Developed with ❤️ using Streamlit | © 2025</p>
487
  </div>
488
  """, unsafe_allow_html=True)
489