Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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['
|
| 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 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
charts = {
|
| 262 |
-
'world_threat_map':
|
| 263 |
-
'
|
| 264 |
-
'top_countries_by_threat':
|
| 265 |
-
'threats_by_theme':
|
| 266 |
}
|
| 267 |
-
return "โ Prediction Generation Error", "Error generating table.", charts['world_threat_map'], charts['
|
| 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')['
|
| 275 |
-
country_threats.columns = ['iso_code', '
|
| 276 |
|
| 277 |
world_threat_map = go.Figure(data=go.Choropleth(
|
| 278 |
locations=country_threats['iso_code'],
|
| 279 |
-
z=country_threats['
|
| 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='
|
| 287 |
-
hovertemplate='<b>%{text}</b><br>
|
| 288 |
))
|
| 289 |
world_threat_map.update_layout(
|
| 290 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
geo=dict(
|
| 292 |
showframe=False,
|
| 293 |
-
showcoastlines=
|
|
|
|
| 294 |
projection_type='equirectangular',
|
| 295 |
-
bgcolor='rgba(
|
| 296 |
-
lakecolor='rgba(
|
| 297 |
),
|
| 298 |
-
template="
|
| 299 |
height=500,
|
| 300 |
-
|
|
|
|
|
|
|
|
|
|
| 301 |
)
|
| 302 |
|
| 303 |
-
# --- 2.
|
| 304 |
-
|
| 305 |
-
|
| 306 |
|
| 307 |
-
|
| 308 |
-
labels=
|
| 309 |
-
values=
|
| 310 |
hole=.4,
|
| 311 |
-
marker_colors=[
|
| 312 |
-
hovertemplate="<b>%{label}</b><br>Count: %{value}<br>(%{percent})<extra></extra>"
|
|
|
|
|
|
|
| 313 |
)])
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
height=400,
|
|
|
|
| 318 |
showlegend=True,
|
| 319 |
-
legend=dict(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 330 |
text=country_counts.values,
|
| 331 |
-
|
|
|
|
| 332 |
hovertemplate='<b>%{y}</b><br>Threat Count: %{x}<extra></extra>'
|
| 333 |
))
|
| 334 |
top_countries_by_threat.update_layout(
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
xaxis_title="Number of Active Threats",
|
| 337 |
yaxis_title="Country",
|
| 338 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
height=400,
|
| 340 |
-
|
|
|
|
|
|
|
|
|
|
| 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={
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
xaxis_title="Disinformation Theme",
|
| 357 |
yaxis_title="Number of Threats",
|
| 358 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
height=400,
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
| 361 |
)
|
| 362 |
-
|
| 363 |
return {
|
| 364 |
'world_threat_map': world_threat_map,
|
| 365 |
-
'
|
| 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
|
| 374 |
-
|
| 375 |
-
'
|
| 376 |
-
'
|
| 377 |
-
'Neutral': '#6b7280'
|
| 378 |
}
|
| 379 |
|
| 380 |
-
|
| 381 |
-
'
|
| 382 |
-
'
|
| 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 |
-
.
|
| 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 |
-
.
|
| 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>
|
| 494 |
-
<th>
|
| 495 |
<th>๐ Resources</th>
|
| 496 |
-
<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 |
-
|
| 508 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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="
|
| 531 |
-
<td><span class="
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 729 |
-
|
| 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 |
-
|
| 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 |
-
|
| 743 |
-
|
| 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,
|
| 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 |
-
|
| 955 |
-
|
| 956 |
-
|
| 957 |
-
|
| 958 |
-
|
| 959 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 960 |
with gr.Row():
|
| 961 |
-
|
| 962 |
-
|
| 963 |
-
|
| 964 |
-
|
| 965 |
-
|
| 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,
|
| 1006 |
-
return status, data, world_map,
|
| 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 |
-
|
| 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 |
]
|