syaikhipin commited on
Commit
bdcb3ee
ยท
verified ยท
1 Parent(s): 42c81e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +253 -100
app.py CHANGED
@@ -235,88 +235,139 @@ def analyze_narrative(content: str):
235
  def generate_predictions():
236
  """Generate enhanced predictions for current narratives with visualization"""
237
  try:
 
238
  predictions = demo_data.get_sample_predictions()
 
 
239
  table_html = generate_predictions_table(predictions)
 
240
 
241
  # Generate the entire dashboard
242
  dashboard_charts = create_prediction_dashboard(predictions)
 
243
 
244
  return (
245
  "๐Ÿ”ฎ Enhanced Prediction Dashboard Generated Successfully!",
246
  table_html,
247
  dashboard_charts['world_threat_map'],
248
- dashboard_charts['sentiment_distribution'],
249
  dashboard_charts['top_countries_by_threat'],
250
  dashboard_charts['threats_by_theme']
251
  )
252
  except Exception as e:
253
- print(f"Prediction generation failed: {e}")
254
- # Create empty charts for error case
255
- empty_chart = go.Figure()
256
- empty_chart.update_layout(
257
- title="No Data Available",
258
- template="plotly_dark",
259
- height=300
260
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  charts = {
262
- 'world_threat_map': empty_chart,
263
- 'sentiment_distribution': empty_chart,
264
- 'top_countries_by_threat': empty_chart,
265
- 'threats_by_theme': empty_chart
266
  }
267
- return "โŒ Prediction Generation Error", "Error generating table.", charts['world_threat_map'], charts['sentiment_distribution'], charts['top_countries_by_threat'], charts['threats_by_theme']
268
 
269
  def create_prediction_dashboard(predictions: List[Dict]):
270
  """Create a comprehensive and intuitive dashboard with multiple visualizations."""
271
  df = pd.DataFrame(predictions)
272
 
273
  # --- 1. Global Threat Map (Choropleth) ---
274
- country_threats = df.groupby('country_iso')['risk_score'].mean().reset_index()
275
- country_threats.columns = ['iso_code', 'avg_risk_score']
276
 
277
  world_threat_map = go.Figure(data=go.Choropleth(
278
  locations=country_threats['iso_code'],
279
- z=country_threats['avg_risk_score'],
280
  text=country_threats['iso_code'],
281
  colorscale='Reds',
282
  autocolorscale=False,
283
  reversescale=False,
284
  marker_line_color='darkgray',
285
  marker_line_width=0.5,
286
- colorbar_title='Avg. Risk<br>Score',
287
- hovertemplate='<b>%{text}</b><br>Avg. Risk: %{z:.2f}<extra></extra>'
288
  ))
289
  world_threat_map.update_layout(
290
- title={'text': '๐ŸŒ Global Disinformation Threat Map', 'x': 0.5, 'font': {'color': 'white'}},
 
 
 
 
 
291
  geo=dict(
292
  showframe=False,
293
- showcoastlines=False,
 
294
  projection_type='equirectangular',
295
- bgcolor='rgba(0,0,0,0)',
296
- lakecolor='rgba(0,0,0,0)'
297
  ),
298
- template="plotly_dark",
299
  height=500,
300
- margin={"r":0,"t":40,"l":0,"b":0}
 
 
 
301
  )
302
 
303
- # --- 2. Sentiment Distribution (Donut Chart) ---
304
- sentiment_counts = df['sentiment'].value_counts()
305
- sentiment_colors = {'Negative': '#ef4444', 'Positive': '#10b981', 'Neutral': '#6b7280'}
306
 
307
- sentiment_distribution = go.Figure(data=[go.Pie(
308
- labels=sentiment_counts.index,
309
- values=sentiment_counts.values,
310
  hole=.4,
311
- marker_colors=[sentiment_colors.get(s, '#374151') for s in sentiment_counts.index],
312
- hovertemplate="<b>%{label}</b><br>Count: %{value}<br>(%{percent})<extra></extra>"
 
 
313
  )])
314
- sentiment_distribution.update_layout(
315
- title={'text': '๐Ÿ˜Š Sentiment Distribution', 'x': 0.5, 'font': {'color': 'white'}},
316
- template="plotly_dark",
 
 
 
 
 
317
  height=400,
 
318
  showlegend=True,
319
- legend=dict(orientation="h", yanchor="bottom", y=-0.2, xanchor="center", x=0.5)
 
 
 
 
 
 
 
 
 
 
320
  )
321
 
322
  # --- 3. Top 10 Countries by Threat Count ---
@@ -326,18 +377,39 @@ def create_prediction_dashboard(predictions: List[Dict]):
326
  y=country_counts.index,
327
  x=country_counts.values,
328
  orientation='h',
329
- marker_color='#3b82f6',
330
  text=country_counts.values,
331
- textposition='outside',
 
332
  hovertemplate='<b>%{y}</b><br>Threat Count: %{x}<extra></extra>'
333
  ))
334
  top_countries_by_threat.update_layout(
335
- title={'text': '๐Ÿ† Top 10 Countries by Threat Count', 'x': 0.5, 'font': {'color': 'white'}},
 
 
 
 
 
336
  xaxis_title="Number of Active Threats",
337
  yaxis_title="Country",
338
- template="plotly_dark",
 
 
 
 
 
 
 
 
 
 
 
 
339
  height=400,
340
- margin=dict(l=100) # Adjust left margin for country codes
 
 
 
341
  )
342
 
343
  # --- 4. Threats by Theme (Bar Chart) ---
@@ -349,20 +421,42 @@ def create_prediction_dashboard(predictions: List[Dict]):
349
  marker_color='#8b5cf6',
350
  text=theme_counts.values,
351
  textposition='outside',
 
352
  hovertemplate='<b>%{x}</b><br>Threat Count: %{y}<extra></extra>'
353
  ))
354
  threats_by_theme.update_layout(
355
- title={'text': '๐Ÿ“Š Threats by Theme', 'x': 0.5, 'font': {'color': 'white'}},
 
 
 
 
 
356
  xaxis_title="Disinformation Theme",
357
  yaxis_title="Number of Threats",
358
- template="plotly_dark",
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  height=400,
360
- xaxis_tickangle=-45
 
 
 
361
  )
362
-
363
  return {
364
  'world_threat_map': world_threat_map,
365
- 'sentiment_distribution': sentiment_distribution,
366
  'top_countries_by_threat': top_countries_by_threat,
367
  'threats_by_theme': threats_by_theme
368
  }
@@ -370,17 +464,15 @@ def create_prediction_dashboard(predictions: List[Dict]):
370
  def generate_predictions_table(predictions: List[Dict]) -> str:
371
  """Generate HTML table for predictions with enhanced action plan integration"""
372
 
373
- # Define colors for sentiments and priorities
374
- sentiment_colors = {
375
- 'Positive': '#10b981',
376
- 'Negative': '#ef4444',
377
- 'Neutral': '#6b7280'
378
  }
379
 
380
- priority_colors = {
381
- 'High': '#dc2626',
382
- 'Medium': '#f59e0b',
383
- 'Low': '#16a34a'
384
  }
385
 
386
  header = """
@@ -388,7 +480,7 @@ def generate_predictions_table(predictions: List[Dict]) -> str:
388
  .predictions-table {
389
  width: 100%;
390
  border-collapse: collapse;
391
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
392
  background: #f8fafc;
393
  border-radius: 12px;
394
  overflow: hidden;
@@ -421,7 +513,7 @@ def generate_predictions_table(predictions: List[Dict]) -> str:
421
  transform: scale(1.002);
422
  transition: all 0.2s ease;
423
  }
424
- .sentiment-badge {
425
  padding: 4px 8px;
426
  border-radius: 6px;
427
  font-weight: 600;
@@ -429,7 +521,7 @@ def generate_predictions_table(predictions: List[Dict]) -> str:
429
  color: white;
430
  display: inline-block;
431
  }
432
- .priority-badge {
433
  padding: 4px 8px;
434
  border-radius: 6px;
435
  font-weight: 600;
@@ -490,12 +582,10 @@ def generate_predictions_table(predictions: List[Dict]) -> str:
490
  <th>๐ŸŽฏ Theme</th>
491
  <th>๐Ÿ“‹ Subject</th>
492
  <th>๐Ÿ“ Summary</th>
493
- <th>๐Ÿ’ญ Sentiment</th>
494
- <th>โš ๏ธ Priority</th>
495
  <th>๐Ÿ”— Resources</th>
496
- <th>๐Ÿ“Š Risk</th>
497
- <th>๐Ÿ“ˆ Probability</th>
498
- <th>๐Ÿ‘ฅ Reach</th>
499
  <th>โฑ๏ธ Timeline</th>
500
  <th>๐Ÿ“‹ Action Plan</th>
501
  </tr>
@@ -504,8 +594,13 @@ def generate_predictions_table(predictions: List[Dict]) -> str:
504
  """
505
 
506
  for pred in predictions:
507
- sentiment_color = sentiment_colors.get(pred.get('sentiment', 'Neutral'), '#6b7280')
508
- priority_color = priority_colors.get(pred.get('priority_level', 'Medium'), '#f59e0b')
 
 
 
 
 
509
 
510
  # Format links
511
  links_html = ""
@@ -527,11 +622,9 @@ def generate_predictions_table(predictions: List[Dict]) -> str:
527
  <td><strong>{pred.get('theme', 'N/A')}</strong></td>
528
  <td><strong>{pred.get('subject', 'N/A')}</strong></td>
529
  <td>{pred.get('summary', 'N/A')}</td>
530
- <td><span class="sentiment-badge" style="background-color: {sentiment_color};">{pred.get('sentiment', 'Neutral')}</span></td>
531
- <td><span class="priority-badge" style="background-color: {priority_color};">{pred.get('priority_level', 'Medium')} Priority</span></td>
532
  <td><ul class="links-list">{links_html}</ul></td>
533
- <td><span class="metric-value">{pred.get('risk_score', 0)}</span></td>
534
- <td><span class="metric-value">{pred.get('probability', 0)}%</span></td>
535
  <td><span class="metric-value">{pred.get('predicted_reach', 0):,}</span></td>
536
  <td><span class="metric-value">{pred.get('timeline', 'N/A')}</span></td>
537
  <td>{action_plan_html}</td>
@@ -705,8 +798,8 @@ def generate_predictions_table(predictions: List[Dict]) -> str:
705
  <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 2px solid #e2e8f0; padding-bottom: 16px;">
706
  <h2 style="margin: 0; color: #1e40af; font-size: 1.5em;">${plan.title}</h2>
707
  <button onclick="closeActionPlanModal()" style="background: #ef4444; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer;">โœ• Close</button>
708
- </div>
709
-
710
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
711
  <div style="background: #fef3c7; padding: 12px; border-radius: 8px; border-left: 4px solid #f59e0b;">
712
  <strong style="color: #92400e;">Priority:</strong> ${plan.priority}
@@ -714,33 +807,33 @@ def generate_predictions_table(predictions: List[Dict]) -> str:
714
  <div style="background: #dbeafe; padding: 12px; border-radius: 8px; border-left: 4px solid #3b82f6;">
715
  <strong style="color: #1e40af;">Timeline:</strong> ${plan.timeline}
716
  </div>
717
- </div>
718
-
719
  <div style="background: #f0fdf4; padding: 16px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #10b981;">
720
  <h3 style="color: #065f46; margin: 0 0 8px 0;">Strategy: ${plan.strategy}</h3>
721
- </div>
722
-
723
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
724
  <div>
725
  <h4 style="color: #dc2626; margin-bottom: 12px;">๐Ÿšจ Immediate Actions</h4>
726
  <ul style="padding-left: 20px; margin: 0;">
727
  ${plan.immediate_actions.map(action => `<li style="margin-bottom: 8px; line-height: 1.4;">${action}</li>`).join('')}
728
- </ul>
729
- </div>
730
  <div>
731
  <h4 style="color: #059669; margin-bottom: 12px;">๐Ÿ“… Medium-term Actions</h4>
732
  <ul style="padding-left: 20px; margin: 0;">
733
  ${plan.medium_term_actions.map(action => `<li style="margin-bottom: 8px; line-height: 1.4;">${action}</li>`).join('')}
734
- </ul>
735
- </div>
736
  </div>
737
-
 
738
  <div style="margin-top: 20px; padding-top: 16px; border-top: 1px solid #e2e8f0;">
739
  <h4 style="color: #7c3aed; margin-bottom: 12px;">๐Ÿ‘ฅ Key Messengers</h4>
740
  <div style="display: flex; flex-wrap: wrap; gap: 8px;">
741
  ${plan.key_messengers.map(messenger => `<span style="background: #ede9fe; color: #5b21b6; padding: 4px 8px; border-radius: 6px; font-size: 12px;">${messenger}</span>`).join('')}
742
- </div>
743
- </div>
744
  </div>
745
  `;
746
 
@@ -873,8 +966,12 @@ theme = gr.themes.Base(
873
  )
874
 
875
  with gr.Blocks(theme=theme, title="ForesightSphere - Disinformation Prevention", css="""
 
 
 
876
  .gradio-container {
877
  background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%) !important;
 
878
  }
879
  .block {
880
  background: white !important;
@@ -890,6 +987,44 @@ with gr.Blocks(theme=theme, title="ForesightSphere - Disinformation Prevention",
890
  background: #2563eb !important;
891
  color: white !important;
892
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
893
  """) as demo:
894
  # Header
895
  gr.HTML(create_header)
@@ -942,28 +1077,46 @@ with gr.Blocks(theme=theme, title="ForesightSphere - Disinformation Prevention",
942
  with gr.Tabs():
943
  with gr.TabItem("๐Ÿ“‹ Enhanced Prediction Table"):
944
  gr.Markdown("""#### **Detailed Predictions with Action Plans**
945
- *Each row shows theme classification, summary, sentiment analysis, and action plans.*""")
946
  predictions_html = gr.HTML(
947
  label="๐Ÿ“Š Interactive Prediction Analysis",
948
  value="<p style='text-align: center; color: #666; padding: 20px;'>Click 'Generate Prediction Dashboard' to load data.</p>"
949
  )
950
-
951
  with gr.TabItem("๐ŸŒ Global Dashboard"):
952
  gr.Markdown("#### **Global Disinformation Landscape**")
 
 
 
 
 
 
 
 
 
 
953
  with gr.Row():
954
- with gr.Column(scale=3):
955
- gr.Markdown("**Global Threat Map**<br>*Average risk score by country.*")
956
- world_map_chart = gr.Plot(label="๐ŸŒ Global Threat Map")
957
- with gr.Column(scale=2):
958
- gr.Markdown("**Sentiment Distribution**<br>*Breakdown of narrative sentiment.*")
959
- sentiment_chart = gr.Plot(label="๐Ÿ˜Š Sentiment Analysis")
 
 
 
 
 
 
 
 
 
960
  with gr.Row():
961
- with gr.Column(scale=2):
962
- gr.Markdown("**Top 10 Countries by Threat Count**<br>*Countries with the most active threats.*")
963
- top_countries_chart = gr.Plot(label="๐Ÿ† Top Threat Countries")
964
- with gr.Column(scale=3):
965
- gr.Markdown("**Threats by Theme**<br>*Number of threats per disinformation theme.*")
966
- threats_by_theme_chart = gr.Plot(label="๐Ÿ“Š Threats by Theme")
967
 
968
  # JSON Export section
969
  with gr.Accordion("๐Ÿ“ Raw Data Export", open=False):
@@ -1002,8 +1155,8 @@ with gr.Blocks(theme=theme, title="ForesightSphere - Disinformation Prevention",
1002
 
1003
  # Enhanced prediction function wrapper
1004
  def enhanced_generate_predictions():
1005
- status, data, world_map, sentiment, top_countries, threats_by_theme = generate_predictions()
1006
- return status, data, world_map, sentiment, top_countries, threats_by_theme
1007
 
1008
  # JSON export function
1009
  def export_json_data():
@@ -1020,7 +1173,7 @@ with gr.Blocks(theme=theme, title="ForesightSphere - Disinformation Prevention",
1020
  prediction_status,
1021
  predictions_html,
1022
  world_map_chart,
1023
- sentiment_chart,
1024
  top_countries_chart,
1025
  threats_by_theme_chart
1026
  ]
 
235
  def generate_predictions():
236
  """Generate enhanced predictions for current narratives with visualization"""
237
  try:
238
+ print("๐Ÿ”„ Starting prediction generation...")
239
  predictions = demo_data.get_sample_predictions()
240
+ print(f"โœ… Got {len(predictions)} sample predictions")
241
+
242
  table_html = generate_predictions_table(predictions)
243
+ print("โœ… Generated predictions table")
244
 
245
  # Generate the entire dashboard
246
  dashboard_charts = create_prediction_dashboard(predictions)
247
+ print("โœ… Generated dashboard charts")
248
 
249
  return (
250
  "๐Ÿ”ฎ Enhanced Prediction Dashboard Generated Successfully!",
251
  table_html,
252
  dashboard_charts['world_threat_map'],
253
+ dashboard_charts['verification_distribution'],
254
  dashboard_charts['top_countries_by_threat'],
255
  dashboard_charts['threats_by_theme']
256
  )
257
  except Exception as e:
258
+ print(f"โŒ Prediction generation failed: {e}")
259
+ import traceback
260
+ traceback.print_exc()
261
+ # Create empty charts for error case with proper sizing
262
+ def create_empty_chart(title, height=500):
263
+ chart = go.Figure()
264
+ chart.update_layout(
265
+ title={'text': title, 'x': 0.5, 'font': {'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 16}},
266
+ font={'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 12},
267
+ template="plotly_white",
268
+ height=height,
269
+ autosize=True,
270
+ margin={"r":50,"t":60,"l":50,"b":50},
271
+ paper_bgcolor='rgba(255,255,255,1)',
272
+ plot_bgcolor='rgba(255,255,255,1)',
273
+ annotations=[
274
+ dict(
275
+ text="No Data Available<br>Click 'Generate Prediction Dashboard' to load data",
276
+ xref="paper", yref="paper",
277
+ x=0.5, y=0.5, xanchor='center', yanchor='middle',
278
+ showarrow=False,
279
+ font=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color="black", size=14)
280
+ )
281
+ ]
282
+ )
283
+ return chart
284
+
285
  charts = {
286
+ 'world_threat_map': create_empty_chart("๐ŸŒ Global Predicted Reach Map", 500),
287
+ 'verification_distribution': create_empty_chart("โœ… Verification Status Distribution", 400),
288
+ 'top_countries_by_threat': create_empty_chart("๐Ÿ† Top 10 Countries by Threat Count", 400),
289
+ 'threats_by_theme': create_empty_chart("๐Ÿ“Š Threats by Theme", 400)
290
  }
291
+ return "โŒ Prediction Generation Error", "Error generating table.", charts['world_threat_map'], charts['verification_distribution'], charts['top_countries_by_threat'], charts['threats_by_theme']
292
 
293
  def create_prediction_dashboard(predictions: List[Dict]):
294
  """Create a comprehensive and intuitive dashboard with multiple visualizations."""
295
  df = pd.DataFrame(predictions)
296
 
297
  # --- 1. Global Threat Map (Choropleth) ---
298
+ country_threats = df.groupby('country_iso')['predicted_reach'].sum().reset_index()
299
+ country_threats.columns = ['iso_code', 'total_predicted_reach']
300
 
301
  world_threat_map = go.Figure(data=go.Choropleth(
302
  locations=country_threats['iso_code'],
303
+ z=country_threats['total_predicted_reach'],
304
  text=country_threats['iso_code'],
305
  colorscale='Reds',
306
  autocolorscale=False,
307
  reversescale=False,
308
  marker_line_color='darkgray',
309
  marker_line_width=0.5,
310
+ colorbar_title='Total Predicted<br>Reach',
311
+ hovertemplate='<b>%{text}</b><br>Total Reach: %{z:,.0f}<extra></extra>'
312
  ))
313
  world_threat_map.update_layout(
314
+ title={
315
+ 'text': '๐ŸŒ Global Predicted Reach Map',
316
+ 'x': 0.5,
317
+ 'font': {'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 16}
318
+ },
319
+ font={'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 12},
320
  geo=dict(
321
  showframe=False,
322
+ showcoastlines=True,
323
+ coastlinecolor="gray",
324
  projection_type='equirectangular',
325
+ bgcolor='rgba(255,255,255,1)',
326
+ lakecolor='rgba(240,240,240,1)'
327
  ),
328
+ template="plotly_white",
329
  height=500,
330
+ autosize=True,
331
+ margin={"r":20,"t":50,"l":20,"b":20},
332
+ paper_bgcolor='rgba(255,255,255,1)',
333
+ plot_bgcolor='rgba(255,255,255,1)'
334
  )
335
 
336
+ # --- 2. Verification Status Distribution (Donut Chart) ---
337
+ verified_counts = df['verified'].value_counts()
338
+ verified_colors = {'Yes': '#10b981', 'No': '#ef4444'}
339
 
340
+ verification_distribution = go.Figure(data=[go.Pie(
341
+ labels=verified_counts.index,
342
+ values=verified_counts.values,
343
  hole=.4,
344
+ marker_colors=[verified_colors.get(s, '#6b7280') for s in verified_counts.index],
345
+ hovertemplate="<b>%{label}</b><br>Count: %{value}<br>(%{percent})<extra></extra>",
346
+ textinfo='label+percent',
347
+ textposition='auto'
348
  )])
349
+ verification_distribution.update_layout(
350
+ title={
351
+ 'text': 'โœ… Verification Status Distribution',
352
+ 'x': 0.5,
353
+ 'font': {'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 16}
354
+ },
355
+ font={'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 12},
356
+ template="plotly_white",
357
  height=400,
358
+ autosize=True,
359
  showlegend=True,
360
+ legend=dict(
361
+ orientation="h",
362
+ yanchor="bottom",
363
+ y=-0.2,
364
+ xanchor="center",
365
+ x=0.5,
366
+ font=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color="black", size=12)
367
+ ),
368
+ margin={"r":50,"t":50,"l":50,"b":80},
369
+ paper_bgcolor='rgba(255,255,255,1)',
370
+ plot_bgcolor='rgba(255,255,255,1)'
371
  )
372
 
373
  # --- 3. Top 10 Countries by Threat Count ---
 
377
  y=country_counts.index,
378
  x=country_counts.values,
379
  orientation='h',
380
+ marker_color='#3b82f6',
381
  text=country_counts.values,
382
+ textposition='outside',
383
+ textfont=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=12),
384
  hovertemplate='<b>%{y}</b><br>Threat Count: %{x}<extra></extra>'
385
  ))
386
  top_countries_by_threat.update_layout(
387
+ title={
388
+ 'text': '๐Ÿ† Top 10 Countries by Threat Count',
389
+ 'x': 0.5,
390
+ 'font': {'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 16}
391
+ },
392
+ font={'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 12},
393
  xaxis_title="Number of Active Threats",
394
  yaxis_title="Country",
395
+ xaxis=dict(
396
+ color='black',
397
+ gridcolor='rgba(0,0,0,0.2)',
398
+ title_font=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=14),
399
+ tickfont=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=12)
400
+ ),
401
+ yaxis=dict(
402
+ color='black',
403
+ gridcolor='rgba(0,0,0,0.2)',
404
+ title_font=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=14),
405
+ tickfont=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=12)
406
+ ),
407
+ template="plotly_white",
408
  height=400,
409
+ autosize=True,
410
+ margin=dict(l=120, r=50, t=50, b=50),
411
+ paper_bgcolor='rgba(255,255,255,1)',
412
+ plot_bgcolor='rgba(255,255,255,1)'
413
  )
414
 
415
  # --- 4. Threats by Theme (Bar Chart) ---
 
421
  marker_color='#8b5cf6',
422
  text=theme_counts.values,
423
  textposition='outside',
424
+ textfont=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=12),
425
  hovertemplate='<b>%{x}</b><br>Threat Count: %{y}<extra></extra>'
426
  ))
427
  threats_by_theme.update_layout(
428
+ title={
429
+ 'text': '๐Ÿ“Š Threats by Theme',
430
+ 'x': 0.5,
431
+ 'font': {'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 16}
432
+ },
433
+ font={'family': 'Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', 'color': 'black', 'size': 12},
434
  xaxis_title="Disinformation Theme",
435
  yaxis_title="Number of Threats",
436
+ xaxis=dict(
437
+ color='black',
438
+ gridcolor='rgba(0,0,0,0.2)',
439
+ tickangle=-45,
440
+ title_font=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=14),
441
+ tickfont=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=12)
442
+ ),
443
+ yaxis=dict(
444
+ color='black',
445
+ gridcolor='rgba(0,0,0,0.2)',
446
+ title_font=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=14),
447
+ tickfont=dict(family='Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif', color='black', size=12)
448
+ ),
449
+ template="plotly_white",
450
  height=400,
451
+ autosize=True,
452
+ margin=dict(l=60, r=50, t=50, b=100),
453
+ paper_bgcolor='rgba(255,255,255,1)',
454
+ plot_bgcolor='rgba(255,255,255,1)'
455
  )
456
+
457
  return {
458
  'world_threat_map': world_threat_map,
459
+ 'verification_distribution': verification_distribution,
460
  'top_countries_by_threat': top_countries_by_threat,
461
  'threats_by_theme': threats_by_theme
462
  }
 
464
  def generate_predictions_table(predictions: List[Dict]) -> str:
465
  """Generate HTML table for predictions with enhanced action plan integration"""
466
 
467
+ # Define colors for verified status and velocity spread
468
+ verified_colors = {
469
+ 'Yes': '#10b981',
470
+ 'No': '#ef4444'
 
471
  }
472
 
473
+ velocity_colors = {
474
+ 'Yes': '#ef4444', # Red for high velocity (concerning)
475
+ 'No': '#10b981' # Green for low velocity (good)
 
476
  }
477
 
478
  header = """
 
480
  .predictions-table {
481
  width: 100%;
482
  border-collapse: collapse;
483
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
484
  background: #f8fafc;
485
  border-radius: 12px;
486
  overflow: hidden;
 
513
  transform: scale(1.002);
514
  transition: all 0.2s ease;
515
  }
516
+ .verified-badge {
517
  padding: 4px 8px;
518
  border-radius: 6px;
519
  font-weight: 600;
 
521
  color: white;
522
  display: inline-block;
523
  }
524
+ .velocity-badge {
525
  padding: 4px 8px;
526
  border-radius: 6px;
527
  font-weight: 600;
 
582
  <th>๐ŸŽฏ Theme</th>
583
  <th>๐Ÿ“‹ Subject</th>
584
  <th>๐Ÿ“ Summary</th>
585
+ <th>โœ… Verified</th>
586
+ <th>โšก High Velocity Spread</th>
587
  <th>๐Ÿ”— Resources</th>
588
+ <th>๐Ÿ‘ฅ Predicted Reach</th>
 
 
589
  <th>โฑ๏ธ Timeline</th>
590
  <th>๐Ÿ“‹ Action Plan</th>
591
  </tr>
 
594
  """
595
 
596
  for pred in predictions:
597
+ # Format verified status
598
+ verified = pred.get('verified', 'No')
599
+ verified_color = '#10b981' if verified == 'Yes' else '#ef4444'
600
+
601
+ # Format high velocity spread
602
+ high_velocity = pred.get('high_velocity_spread', 'No')
603
+ velocity_color = '#ef4444' if high_velocity == 'Yes' else '#10b981'
604
 
605
  # Format links
606
  links_html = ""
 
622
  <td><strong>{pred.get('theme', 'N/A')}</strong></td>
623
  <td><strong>{pred.get('subject', 'N/A')}</strong></td>
624
  <td>{pred.get('summary', 'N/A')}</td>
625
+ <td><span class="verified-badge" style="background-color: {verified_color};">{verified}</span></td>
626
+ <td><span class="velocity-badge" style="background-color: {velocity_color};">{high_velocity}</span></td>
627
  <td><ul class="links-list">{links_html}</ul></td>
 
 
628
  <td><span class="metric-value">{pred.get('predicted_reach', 0):,}</span></td>
629
  <td><span class="metric-value">{pred.get('timeline', 'N/A')}</span></td>
630
  <td>{action_plan_html}</td>
 
798
  <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 2px solid #e2e8f0; padding-bottom: 16px;">
799
  <h2 style="margin: 0; color: #1e40af; font-size: 1.5em;">${plan.title}</h2>
800
  <button onclick="closeActionPlanModal()" style="background: #ef4444; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer;">โœ• Close</button>
801
+ </div>
802
+
803
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
804
  <div style="background: #fef3c7; padding: 12px; border-radius: 8px; border-left: 4px solid #f59e0b;">
805
  <strong style="color: #92400e;">Priority:</strong> ${plan.priority}
 
807
  <div style="background: #dbeafe; padding: 12px; border-radius: 8px; border-left: 4px solid #3b82f6;">
808
  <strong style="color: #1e40af;">Timeline:</strong> ${plan.timeline}
809
  </div>
810
+ </div>
811
+
812
  <div style="background: #f0fdf4; padding: 16px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #10b981;">
813
  <h3 style="color: #065f46; margin: 0 0 8px 0;">Strategy: ${plan.strategy}</h3>
814
+ </div>
815
+
816
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
817
  <div>
818
  <h4 style="color: #dc2626; margin-bottom: 12px;">๐Ÿšจ Immediate Actions</h4>
819
  <ul style="padding-left: 20px; margin: 0;">
820
  ${plan.immediate_actions.map(action => `<li style="margin-bottom: 8px; line-height: 1.4;">${action}</li>`).join('')}
821
+ </ul>
822
+ </div>
823
  <div>
824
  <h4 style="color: #059669; margin-bottom: 12px;">๐Ÿ“… Medium-term Actions</h4>
825
  <ul style="padding-left: 20px; margin: 0;">
826
  ${plan.medium_term_actions.map(action => `<li style="margin-bottom: 8px; line-height: 1.4;">${action}</li>`).join('')}
827
+ </ul>
 
828
  </div>
829
+ </div>
830
+
831
  <div style="margin-top: 20px; padding-top: 16px; border-top: 1px solid #e2e8f0;">
832
  <h4 style="color: #7c3aed; margin-bottom: 12px;">๐Ÿ‘ฅ Key Messengers</h4>
833
  <div style="display: flex; flex-wrap: wrap; gap: 8px;">
834
  ${plan.key_messengers.map(messenger => `<span style="background: #ede9fe; color: #5b21b6; padding: 4px 8px; border-radius: 6px; font-size: 12px;">${messenger}</span>`).join('')}
835
+ </div>
836
+ </div>
837
  </div>
838
  `;
839
 
 
966
  )
967
 
968
  with gr.Blocks(theme=theme, title="ForesightSphere - Disinformation Prevention", css="""
969
+ /* Import web-safe fonts */
970
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
971
+
972
  .gradio-container {
973
  background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%) !important;
974
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important;
975
  }
976
  .block {
977
  background: white !important;
 
987
  background: #2563eb !important;
988
  color: white !important;
989
  }
990
+ /* Font configuration for all elements */
991
+ * {
992
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important;
993
+ }
994
+ /* Plot container sizing */
995
+ .plot-container {
996
+ min-height: 400px !important;
997
+ width: 100% !important;
998
+ }
999
+ .plot-container > div {
1000
+ width: 100% !important;
1001
+ height: 100% !important;
1002
+ }
1003
+ /* Ensure plots are responsive */
1004
+ .js-plotly-plot {
1005
+ width: 100% !important;
1006
+ height: auto !important;
1007
+ min-height: 400px !important;
1008
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important;
1009
+ }
1010
+ /* Row-based chart layout */
1011
+ .chart-row {
1012
+ width: 100% !important;
1013
+ margin: 20px 0 !important;
1014
+ padding: 10px !important;
1015
+ background: white !important;
1016
+ border-radius: 8px !important;
1017
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
1018
+ }
1019
+ /* Better spacing between dashboard elements */
1020
+ .dashboard-container {
1021
+ padding: 20px !important;
1022
+ gap: 20px !important;
1023
+ }
1024
+ /* Plotly font override */
1025
+ .js-plotly-plot .plotly text {
1026
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important;
1027
+ }
1028
  """) as demo:
1029
  # Header
1030
  gr.HTML(create_header)
 
1077
  with gr.Tabs():
1078
  with gr.TabItem("๐Ÿ“‹ Enhanced Prediction Table"):
1079
  gr.Markdown("""#### **Detailed Predictions with Action Plans**
1080
+ *Each row shows theme classification, summary, verified status, and action plans.*""")
1081
  predictions_html = gr.HTML(
1082
  label="๐Ÿ“Š Interactive Prediction Analysis",
1083
  value="<p style='text-align: center; color: #666; padding: 20px;'>Click 'Generate Prediction Dashboard' to load data.</p>"
1084
  )
1085
+
1086
  with gr.TabItem("๐ŸŒ Global Dashboard"):
1087
  gr.Markdown("#### **Global Disinformation Landscape**")
1088
+
1089
+ # Row 1 - World Map
1090
+ with gr.Row():
1091
+ world_map_chart = gr.Plot(
1092
+ label="๐ŸŒ Global Predicted Reach Map - Total predicted reach by country",
1093
+ container=True,
1094
+ show_label=True
1095
+ )
1096
+
1097
+ # Row 2 - Verification Status Distribution
1098
  with gr.Row():
1099
+ verification_chart = gr.Plot(
1100
+ label="โœ… Verification Status Distribution - Breakdown of verified vs unverified narratives",
1101
+ container=True,
1102
+ show_label=True
1103
+ )
1104
+
1105
+ # Row 3 - Top Countries by Threat
1106
+ with gr.Row():
1107
+ top_countries_chart = gr.Plot(
1108
+ label="๐Ÿ† Top Threat Countries - Countries with the most active threats",
1109
+ container=True,
1110
+ show_label=True
1111
+ )
1112
+
1113
+ # Row 4 - Threats by Theme
1114
  with gr.Row():
1115
+ threats_by_theme_chart = gr.Plot(
1116
+ label="๐Ÿ“Š Threats by Theme - Number of threats per disinformation theme",
1117
+ container=True,
1118
+ show_label=True
1119
+ )
 
1120
 
1121
  # JSON Export section
1122
  with gr.Accordion("๐Ÿ“ Raw Data Export", open=False):
 
1155
 
1156
  # Enhanced prediction function wrapper
1157
  def enhanced_generate_predictions():
1158
+ status, data, world_map, verification, top_countries, threats_by_theme = generate_predictions()
1159
+ return status, data, world_map, verification, top_countries, threats_by_theme
1160
 
1161
  # JSON export function
1162
  def export_json_data():
 
1173
  prediction_status,
1174
  predictions_html,
1175
  world_map_chart,
1176
+ verification_chart,
1177
  top_countries_chart,
1178
  threats_by_theme_chart
1179
  ]