rocky250 commited on
Commit
d43adf3
·
verified ·
1 Parent(s): 1e7da4b

Update charts.py

Browse files
Files changed (1) hide show
  1. charts.py +108 -186
charts.py CHANGED
@@ -4,35 +4,27 @@ import plotly.express as px
4
  import pandas as pd
5
  import numpy as np
6
 
7
- DARK_BG = "#FFFFE3"
8
- CARD_BG = "#FFFFFF"
9
- BORDER = "#BDDDFC"
10
- TEXT_MAIN = "#2d2d2d"
11
- TEXT_DIM = "#555555"
12
- PRIMARY = "#269ccc"
13
- CYAN = "#269ccc"
14
-
15
- POS_COLOR = "#88BDF2"
16
- NEG_COLOR = "#6A89A7"
17
- NEU_COLOR = "#CBCBCB"
18
-
19
- ALGAE = "#9ed2c5"
20
- GREEN = "#16a34a"
21
- RED = "#dc2626"
22
- AMBER = "#d97706"
23
- PURPLE = "#7c3aed"
24
- BLUE = "#2563eb"
25
-
26
- _FONT = dict(family="'DM Sans', 'Nunito', sans-serif", color=TEXT_MAIN, size=12)
27
- _TITLE_FONT = dict(family="'DM Sans', sans-serif", color=TEXT_MAIN, size=13)
28
- _TICK_FONT = dict(family="'DM Sans', sans-serif", color=TEXT_MAIN, size=11)
29
- _LEGEND_FONT= dict(family="'DM Sans', sans-serif", color=TEXT_MAIN, size=11)
30
 
31
  PLOTLY_LAYOUT = dict(
32
- paper_bgcolor="rgba(255,255,227,0)",
33
  plot_bgcolor="rgba(189,221,252,0.13)",
34
- font=_FONT,
35
- margin=dict(l=20, r=20, t=44, b=20),
36
  )
37
 
38
 
@@ -48,26 +40,26 @@ def misinfo_gauge(score: float, label: str) -> go.Figure:
48
  fig = go.Figure(go.Indicator(
49
  mode="gauge+number+delta",
50
  value=pct,
51
- number={"suffix": "%", "font": {"size": 32, "color": bar_color, "family": "'Nunito', sans-serif"}},
52
  delta={"reference": 50, "increasing": {"color": RED}, "decreasing": {"color": GREEN}},
53
- title={"text": label, "font": {"size": 13, "color": TEXT_MAIN}},
54
  gauge={
55
  "axis": {
56
  "range": [0, 100],
57
  "tickwidth": 1,
58
  "tickcolor": BORDER,
59
- "tickfont": {"color": TEXT_MAIN, "size": 10},
60
  },
61
  "bar": {"color": bar_color, "thickness": 0.3},
62
  "bgcolor": CARD_BG,
63
  "borderwidth": 0,
64
  "steps": [
65
- {"range": [0, 35], "color": "#0d1f18"},
66
- {"range": [35, 65], "color": "#1f1a0d"},
67
- {"range": [65, 100],"color": "#1f0d0d"},
68
  ],
69
  "threshold": {
70
- "line": {"color": TEXT_MAIN, "width": 2},
71
  "thickness": 0.75,
72
  "value": pct,
73
  },
@@ -78,35 +70,34 @@ def misinfo_gauge(score: float, label: str) -> go.Figure:
78
 
79
 
80
  def sentiment_donut(summary: Dict) -> go.Figure:
81
- labels = ["Positively Engagement", "Neutral", "Negatively Engagement"]
82
- values = [summary["Positively Engagement"], summary["Neutral"], summary["Negatively Engagement"]]
83
- colors = [POS_COLOR, NEU_COLOR, NEG_COLOR]
84
 
85
  fig = go.Figure(go.Pie(
86
  labels=labels,
87
  values=values,
88
  hole=0.62,
89
- marker=dict(colors=colors, line=dict(color=DARK_BG, width=3)),
90
  textinfo="label+percent",
91
- textfont=dict(family="'DM Sans', sans-serif", size=11, color=TEXT_MAIN),
92
  hovertemplate="<b>%{label}</b><br>%{value} comments (%{percent})<extra></extra>",
93
  rotation=90,
94
  ))
95
 
96
  avg = summary.get("avg_compound", 0)
97
- overall = "😊 Positively Engaged" if avg > 0.05 else ("😟 Negatively Engaged" if avg < -0.05 else "😐 Mixed")
98
  fig.add_annotation(
99
  text=f"<b>{overall}</b><br><span style='font-size:11px;color:{TEXT_DIM}'>{summary['total']} comments</span>",
100
  x=0.5, y=0.5,
101
  showarrow=False,
102
- font=dict(size=13, color=TEXT_MAIN, family="'DM Sans', sans-serif"),
103
  align="center",
104
  )
105
  fig.update_layout(
106
  **PLOTLY_LAYOUT,
107
  height=300,
108
- legend=dict(orientation="h", y=-0.08, font=_LEGEND_FONT),
109
- title=dict(text="Sentiment Breakdown", font=_TITLE_FONT, x=0),
110
  )
111
  return fig
112
 
@@ -114,7 +105,7 @@ def sentiment_donut(summary: Dict) -> go.Figure:
114
  def keyword_bar(
115
  keywords: List[Tuple[str, float]],
116
  title: str = "Top Keywords",
117
- color: str = CYAN,
118
  ) -> go.Figure:
119
  if not keywords:
120
  return _empty_fig(title)
@@ -129,33 +120,25 @@ def keyword_bar(
129
  orientation="h",
130
  marker=dict(
131
  color=norm,
132
- colorscale=[[0, f"{PRIMARY}33"], [1, PRIMARY]],
133
- line=dict(color=DARK_BG, width=1.5),
134
  ),
135
  text=[f"{w:.0f}" for w in weights],
136
  textposition="inside",
137
- textfont=dict(size=10, color="#FFFFFF", family="'DM Sans', sans-serif"),
138
  hovertemplate="<b>%{y}</b><br>Weight: %{text}<extra></extra>",
139
  ))
140
  fig.update_layout(
141
  **PLOTLY_LAYOUT,
142
- title=dict(text=title, font=_TITLE_FONT, x=0),
143
  height=380,
144
  yaxis=dict(
145
  autorange="reversed",
146
- tickfont=_TICK_FONT,
147
- title_font=_TITLE_FONT,
148
  gridcolor=BORDER,
149
- showgrid=True,
150
- gridwidth=1,
151
  ),
152
- xaxis=dict(
153
- showticklabels=False,
154
- gridcolor=BORDER,
155
- showgrid=False,
156
- ),
157
- bargap=0.3,
158
- plot_bgcolor="rgba(189,221,252,0.13)",
159
  )
160
  return fig
161
 
@@ -169,7 +152,7 @@ def stream_trust_bars(stream_details: Dict) -> go.Figure:
169
  x=values,
170
  y=[l.replace("_", " ").title() for l in labels],
171
  orientation="h",
172
- marker=dict(color=colors, line=dict(color=DARK_BG, width=1.5)),
173
  text=[f"{v}%" for v in values],
174
  textposition="outside",
175
  textfont=dict(size=11, color=TEXT_MAIN),
@@ -177,10 +160,10 @@ def stream_trust_bars(stream_details: Dict) -> go.Figure:
177
  ))
178
  fig.update_layout(
179
  **PLOTLY_LAYOUT,
180
- title=dict(text="Per-Stream Analysis", font=_TITLE_FONT, x=0),
181
  height=220,
182
- xaxis=dict(range=[0, 110], showticklabels=False, gridcolor=BORDER, tickfont=_TICK_FONT),
183
- yaxis=dict(tickfont=_TICK_FONT),
184
  bargap=0.4,
185
  )
186
  return fig
@@ -206,12 +189,12 @@ def modality_misinfo_distribution(modality_analysis: Dict) -> go.Figure:
206
  y=misinfo_pcts,
207
  marker=dict(
208
  color=[RED, RED, RED],
209
- opacity=0.88,
210
- line=dict(color=DARK_BG, width=1.5),
211
  ),
212
  text=[f"{v:.1f}%" for v in misinfo_pcts],
213
  textposition="outside",
214
- textfont=dict(size=11, color=RED),
215
  customdata=logit_tips,
216
  hovertemplate=(
217
  "<b>%{x} — Misinformation</b><br>"
@@ -226,12 +209,12 @@ def modality_misinfo_distribution(modality_analysis: Dict) -> go.Figure:
226
  y=credible_pcts,
227
  marker=dict(
228
  color=[GREEN, GREEN, GREEN],
229
- opacity=0.88,
230
- line=dict(color=DARK_BG, width=1.5),
231
  ),
232
  text=[f"{v:.1f}%" for v in credible_pcts],
233
  textposition="outside",
234
- textfont=dict(size=11, color=GREEN),
235
  customdata=logit_tips,
236
  hovertemplate=(
237
  "<b>%{x} — Credible</b><br>"
@@ -244,34 +227,31 @@ def modality_misinfo_distribution(modality_analysis: Dict) -> go.Figure:
244
  **PLOTLY_LAYOUT,
245
  title=dict(
246
  text="Modality Misinformation Distribution",
247
- font=_TITLE_FONT,
248
  x=0,
249
  ),
250
  barmode="group",
251
- height=280,
252
  xaxis=dict(
253
- title="Modality",
254
- title_font=_TITLE_FONT,
255
- tickfont=_TICK_FONT,
256
  gridcolor=BORDER,
257
  ),
258
  yaxis=dict(
259
- title="Softmax Score (%)",
260
- title_font=_TITLE_FONT,
261
- tickfont=_TICK_FONT,
262
- range=[0, 115],
263
  gridcolor=BORDER,
264
  ticksuffix="%",
 
265
  ),
266
  legend=dict(
267
  orientation="h",
268
- y=1.12,
269
- font=_LEGEND_FONT,
270
- bgcolor="rgba(255,255,227,0)",
271
  ),
272
  bargap=0.22,
273
  bargroupgap=0.06,
274
- plot_bgcolor="rgba(189,221,252,0.13)",
275
  )
276
  return fig
277
 
@@ -291,12 +271,12 @@ def trust_score_by_modality(modality_analysis: Dict) -> go.Figure:
291
  y=trust_vals,
292
  marker=dict(
293
  color=bar_colors,
294
- opacity=0.88,
295
- line=dict(color=DARK_BG, width=1.5),
296
  ),
297
  text=[f"{v:.1f}%" for v in trust_vals],
298
  textposition="outside",
299
- textfont=dict(size=11, color=TEXT_MAIN),
300
  hovertemplate=(
301
  "<b>%{x}</b><br>"
302
  "Trust Level: %{y:.2f}%<br>"
@@ -308,7 +288,7 @@ def trust_score_by_modality(modality_analysis: Dict) -> go.Figure:
308
  for level, label, color in [(80, "High Trust", GREEN), (50, "Threshold", AMBER)]:
309
  fig.add_hline(
310
  y=level,
311
- line=dict(color=color, width=1, dash="dot"),
312
  annotation_text=label,
313
  annotation_position="right",
314
  annotation_font=dict(size=9, color=color),
@@ -318,26 +298,23 @@ def trust_score_by_modality(modality_analysis: Dict) -> go.Figure:
318
  **PLOTLY_LAYOUT,
319
  title=dict(
320
  text="Trust Score by Modality",
321
- font=_TITLE_FONT,
322
  x=0,
323
  ),
324
- height=280,
325
  xaxis=dict(
326
- title="Modality",
327
- title_font=_TITLE_FONT,
328
- tickfont=_TICK_FONT,
329
  gridcolor=BORDER,
330
  ),
331
  yaxis=dict(
332
- title="Trust Level (%)",
333
- title_font=_TITLE_FONT,
334
- tickfont=_TICK_FONT,
335
- range=[0, 115],
336
  gridcolor=BORDER,
337
  ticksuffix="%",
 
338
  ),
339
  bargap=0.38,
340
- plot_bgcolor="rgba(189,221,252,0.13)",
341
  )
342
  return fig
343
 
@@ -359,12 +336,12 @@ def uncertainty_analysis(modality_analysis: Dict) -> go.Figure:
359
  y=uncertainty_vals,
360
  marker=dict(
361
  color=bar_colors,
362
- opacity=0.88,
363
- line=dict(color=DARK_BG, width=1.5),
364
  ),
365
  text=[f"{v:.1f}%" for v in uncertainty_vals],
366
  textposition="outside",
367
- textfont=dict(size=11, color=TEXT_MAIN),
368
  customdata=[[f"p_misinfo={m:.1f}%"] for m in misinfo_pcts],
369
  hovertemplate=(
370
  "<b>%{x}</b><br>"
@@ -377,14 +354,14 @@ def uncertainty_analysis(modality_analysis: Dict) -> go.Figure:
377
 
378
  fig.add_hline(
379
  y=100,
380
- line=dict(color=RED, width=1, dash="dot"),
381
  annotation_text="Max Entropy (no signal)",
382
  annotation_position="right",
383
  annotation_font=dict(size=9, color=RED),
384
  )
385
  fig.add_hline(
386
  y=50,
387
- line=dict(color=AMBER, width=1, dash="dot"),
388
  annotation_text="Mid Uncertainty",
389
  annotation_position="right",
390
  annotation_font=dict(size=9, color=AMBER),
@@ -394,26 +371,23 @@ def uncertainty_analysis(modality_analysis: Dict) -> go.Figure:
394
  **PLOTLY_LAYOUT,
395
  title=dict(
396
  text="Uncertainty Analysis (Shannon Entropy)",
397
- font=_TITLE_FONT,
398
  x=0,
399
  ),
400
- height=280,
401
  xaxis=dict(
402
- title="Modality",
403
- title_font=_TITLE_FONT,
404
- tickfont=_TICK_FONT,
405
  gridcolor=BORDER,
406
  ),
407
  yaxis=dict(
408
- title="Uncertainty (%)",
409
- title_font=_TITLE_FONT,
410
- tickfont=_TICK_FONT,
411
- range=[0, 120],
412
  gridcolor=BORDER,
413
  ticksuffix="%",
 
414
  ),
415
  bargap=0.38,
416
- plot_bgcolor="rgba(189,221,252,0.13)",
417
  )
418
  return fig
419
 
@@ -423,20 +397,16 @@ def sentiment_timeline(comments_df: pd.DataFrame, sentiments: List[Dict]) -> go.
423
  return _empty_fig("Comment Sentiment Distribution")
424
 
425
  df = comments_df.copy()
426
- df["compound"] = [s.get("compound", 0) for s in sentiments]
427
- df["label"] = [s.get("label", "Neutral") for s in sentiments]
428
- df["color"] = df["label"].map({
429
- "Positively Engagement": POS_COLOR,
430
- "Negatively Engagement": NEG_COLOR,
431
- "Neutral": NEU_COLOR,
432
- })
433
  df["text_short"] = df["text"].str[:80] + "…"
434
 
435
  fig = go.Figure()
436
- for lbl, clr in [
437
- ("Positively Engagement", POS_COLOR),
438
- ("Negatively Engagement", NEG_COLOR),
439
- ("Neutral", NEU_COLOR),
440
  ]:
441
  sub = df[df["label"] == lbl]
442
  if sub.empty:
@@ -445,11 +415,11 @@ def sentiment_timeline(comments_df: pd.DataFrame, sentiments: List[Dict]) -> go.
445
  x=sub.index,
446
  y=sub["compound"],
447
  mode="markers",
448
- name=lbl,
449
  marker=dict(
450
  size=np.clip(np.log1p(sub["likes"].fillna(0)) * 4 + 4, 4, 20),
451
  color=clr,
452
- opacity=0.75,
453
  line=dict(width=0),
454
  ),
455
  text=sub["text_short"],
@@ -459,33 +429,11 @@ def sentiment_timeline(comments_df: pd.DataFrame, sentiments: List[Dict]) -> go.
459
  fig.add_hline(y=0, line=dict(color=BORDER, width=1, dash="dot"))
460
  fig.update_layout(
461
  **PLOTLY_LAYOUT,
462
- title=dict(text="Comment Sentiment (size = likes)", font=_TITLE_FONT, x=0),
463
  height=320,
464
- xaxis=dict(
465
- title="Comment index",
466
- title_font=_TITLE_FONT,
467
- tickfont=_TICK_FONT,
468
- gridcolor=BORDER,
469
- showgrid=False,
470
- ),
471
- yaxis=dict(
472
- title="Compound score",
473
- title_font=_TITLE_FONT,
474
- tickfont=_TICK_FONT,
475
- gridcolor=BORDER,
476
- range=[-1.1, 1.1],
477
- showgrid=True,
478
- gridwidth=1,
479
- ),
480
- legend=dict(
481
- orientation="h",
482
- y=1.12,
483
- font=_LEGEND_FONT,
484
- bgcolor="rgba(255,255,255,0.85)",
485
- bordercolor=BORDER,
486
- borderwidth=1,
487
- ),
488
- plot_bgcolor="rgba(189,221,252,0.13)",
489
  )
490
  return fig
491
 
@@ -509,15 +457,9 @@ def keyword_comparison(
509
  fig.add_trace(go.Bar(
510
  name="Positively Engagement",
511
  y=list(pw),
512
- x=[v/max_p*100 for v in pv],
513
  orientation="h",
514
- marker=dict(
515
- color=POS_COLOR,
516
- line=dict(color=DARK_BG, width=1.5),
517
- ),
518
- text=[f"{v/max_p*100:.0f}" for v in pv],
519
- textposition="outside",
520
- textfont=dict(size=10, color=TEXT_MAIN),
521
  hovertemplate="<b>%{y}</b><br>Score: %{x:.1f}<extra></extra>",
522
  ))
523
 
@@ -527,42 +469,27 @@ def keyword_comparison(
527
  fig.add_trace(go.Bar(
528
  name="Negatively Engagement",
529
  y=list(nw),
530
- x=[-v/max_n*100 for v in nv],
531
  orientation="h",
532
- marker=dict(
533
- color=NEG_COLOR,
534
- line=dict(color=DARK_BG, width=1.5),
535
- ),
536
- text=[f"{v/max_n*100:.0f}" for v in nv],
537
- textposition="outside",
538
- textfont=dict(size=10, color=TEXT_MAIN),
539
  hovertemplate="<b>%{y}</b><br>Score: %{x:.1f}<extra></extra>",
540
  ))
541
 
542
  fig.update_layout(
543
  **PLOTLY_LAYOUT,
544
- title=dict(text="Sentiment-Weighted Keywords", font=_TITLE_FONT, x=0),
545
  height=360,
546
  barmode="overlay",
547
  xaxis=dict(
548
  title="← Negatively Engagement | Positively Engagement →",
549
- title_font=_TITLE_FONT,
550
- tickfont=_TICK_FONT,
551
  gridcolor=BORDER,
552
  zeroline=True,
553
- zerolinecolor=BORDER,
554
  zerolinewidth=2,
 
555
  ),
556
- yaxis=dict(tickfont=_TICK_FONT),
557
- legend=dict(
558
- orientation="h",
559
- y=1.1,
560
- font=_LEGEND_FONT,
561
- bgcolor="rgba(255,255,255,0.85)",
562
- bordercolor=BORDER,
563
- borderwidth=1,
564
- ),
565
- plot_bgcolor="rgba(189,221,252,0.13)",
566
  )
567
  return fig
568
 
@@ -573,12 +500,7 @@ def _empty_fig(title: str) -> go.Figure:
573
  text="No data available",
574
  x=0.5, y=0.5,
575
  showarrow=False,
576
- font=dict(size=14, color=TEXT_MAIN),
577
- )
578
- fig.update_layout(
579
- **PLOTLY_LAYOUT,
580
- title=dict(text=title, font=_TITLE_FONT, x=0),
581
- height=250,
582
- plot_bgcolor="rgba(189,221,252,0.13)",
583
  )
 
584
  return fig
 
4
  import pandas as pd
5
  import numpy as np
6
 
7
+ CREAM = "#FFFFE3"
8
+ CARD_BG = "#FFFFFF"
9
+ BORDER = "#BDDDFC"
10
+ TEXT_MAIN = "#4A4A4A"
11
+ TEXT_DIM = "#7b7b7b"
12
+ INK_DARK = "#384959"
13
+ PRIMARY = "#269ccc"
14
+ STORMY_SKY = "#88BDF2"
15
+ STORMY_SLATE = "#6A89A7"
16
+ INK_GREY = "#CBCBCB"
17
+ GREEN = "#2e9e6b"
18
+ RED = "#c0392b"
19
+ AMBER = "#d4841a"
20
+ PURPLE = "#7C6DB5"
21
+ BLUE = "#269ccc"
 
 
 
 
 
 
 
 
22
 
23
  PLOTLY_LAYOUT = dict(
24
+ paper_bgcolor="rgba(0,0,0,0)",
25
  plot_bgcolor="rgba(189,221,252,0.13)",
26
+ font=dict(family="'DM Mono', monospace", color=TEXT_MAIN, size=12),
27
+ margin=dict(l=20, r=20, t=40, b=20),
28
  )
29
 
30
 
 
40
  fig = go.Figure(go.Indicator(
41
  mode="gauge+number+delta",
42
  value=pct,
43
+ number={"suffix": "%", "font": {"size": 32, "color": bar_color, "family": "'DM Mono', monospace"}},
44
  delta={"reference": 50, "increasing": {"color": RED}, "decreasing": {"color": GREEN}},
45
+ title={"text": label, "font": {"size": 13, "color": TEXT_DIM}},
46
  gauge={
47
  "axis": {
48
  "range": [0, 100],
49
  "tickwidth": 1,
50
  "tickcolor": BORDER,
51
+ "tickfont": {"color": TEXT_DIM, "size": 10},
52
  },
53
  "bar": {"color": bar_color, "thickness": 0.3},
54
  "bgcolor": CARD_BG,
55
  "borderwidth": 0,
56
  "steps": [
57
+ {"range": [0, 35], "color": "#e8f7ef"},
58
+ {"range": [35, 65], "color": "#fdf3e3"},
59
+ {"range": [65, 100], "color": "#fdecea"},
60
  ],
61
  "threshold": {
62
+ "line": {"color": INK_DARK, "width": 2},
63
  "thickness": 0.75,
64
  "value": pct,
65
  },
 
70
 
71
 
72
  def sentiment_donut(summary: Dict) -> go.Figure:
73
+ labels = ["Positively Engagement", "Neutral", "Negatively Engagement"]
74
+ values = [summary["POSITIVE"], summary["NEUTRAL"], summary["NEGATIVE"]]
75
+ colors = [STORMY_SKY, INK_GREY, STORMY_SLATE]
76
 
77
  fig = go.Figure(go.Pie(
78
  labels=labels,
79
  values=values,
80
  hole=0.62,
81
+ marker=dict(colors=colors, line=dict(color=CARD_BG, width=3)),
82
  textinfo="label+percent",
83
+ textfont=dict(family="'DM Mono', monospace", size=11, color=TEXT_MAIN),
84
  hovertemplate="<b>%{label}</b><br>%{value} comments (%{percent})<extra></extra>",
85
  rotation=90,
86
  ))
87
 
88
  avg = summary.get("avg_compound", 0)
89
+ overall = "😊 Positive" if avg > 0.05 else ("😟 Negative" if avg < -0.05 else "😐 Mixed")
90
  fig.add_annotation(
91
  text=f"<b>{overall}</b><br><span style='font-size:11px;color:{TEXT_DIM}'>{summary['total']} comments</span>",
92
  x=0.5, y=0.5,
93
  showarrow=False,
94
+ font=dict(size=13, color=TEXT_MAIN, family="'DM Mono', monospace"),
95
  align="center",
96
  )
97
  fig.update_layout(
98
  **PLOTLY_LAYOUT,
99
  height=300,
100
+ legend=dict(orientation="h", y=-0.08, font=dict(size=11, color=TEXT_MAIN)),
 
101
  )
102
  return fig
103
 
 
105
  def keyword_bar(
106
  keywords: List[Tuple[str, float]],
107
  title: str = "Top Keywords",
108
+ color: str = PRIMARY,
109
  ) -> go.Figure:
110
  if not keywords:
111
  return _empty_fig(title)
 
120
  orientation="h",
121
  marker=dict(
122
  color=norm,
123
+ colorscale=[[0, f"{color}44"], [1, color]],
124
+ line=dict(width=0),
125
  ),
126
  text=[f"{w:.0f}" for w in weights],
127
  textposition="inside",
128
+ textfont=dict(size=10, color=CARD_BG),
129
  hovertemplate="<b>%{y}</b><br>Weight: %{text}<extra></extra>",
130
  ))
131
  fig.update_layout(
132
  **PLOTLY_LAYOUT,
133
+ title=dict(text=title, font=dict(size=13, color=INK_DARK), x=0),
134
  height=380,
135
  yaxis=dict(
136
  autorange="reversed",
137
+ tickfont=dict(size=11, color=TEXT_MAIN),
 
138
  gridcolor=BORDER,
 
 
139
  ),
140
+ xaxis=dict(showticklabels=False, gridcolor=BORDER),
141
+ bargap=0.35,
 
 
 
 
 
142
  )
143
  return fig
144
 
 
152
  x=values,
153
  y=[l.replace("_", " ").title() for l in labels],
154
  orientation="h",
155
+ marker=dict(color=colors, line=dict(color=CARD_BG, width=1)),
156
  text=[f"{v}%" for v in values],
157
  textposition="outside",
158
  textfont=dict(size=11, color=TEXT_MAIN),
 
160
  ))
161
  fig.update_layout(
162
  **PLOTLY_LAYOUT,
163
+ title=dict(text="Per-Stream Analysis", font=dict(size=13, color=INK_DARK), x=0),
164
  height=220,
165
+ xaxis=dict(range=[0, 120], showticklabels=False, gridcolor=BORDER),
166
+ yaxis=dict(tickfont=dict(size=11, color=TEXT_MAIN)),
167
  bargap=0.4,
168
  )
169
  return fig
 
189
  y=misinfo_pcts,
190
  marker=dict(
191
  color=[RED, RED, RED],
192
+ opacity=0.90,
193
+ line=dict(color=CARD_BG, width=2),
194
  ),
195
  text=[f"{v:.1f}%" for v in misinfo_pcts],
196
  textposition="outside",
197
+ textfont=dict(size=11, color=RED, family="'DM Mono', monospace"),
198
  customdata=logit_tips,
199
  hovertemplate=(
200
  "<b>%{x} — Misinformation</b><br>"
 
209
  y=credible_pcts,
210
  marker=dict(
211
  color=[GREEN, GREEN, GREEN],
212
+ opacity=0.90,
213
+ line=dict(color=CARD_BG, width=2),
214
  ),
215
  text=[f"{v:.1f}%" for v in credible_pcts],
216
  textposition="outside",
217
+ textfont=dict(size=11, color=GREEN, family="'DM Mono', monospace"),
218
  customdata=logit_tips,
219
  hovertemplate=(
220
  "<b>%{x} — Credible</b><br>"
 
227
  **PLOTLY_LAYOUT,
228
  title=dict(
229
  text="Modality Misinformation Distribution",
230
+ font=dict(size=13, color=INK_DARK),
231
  x=0,
232
  ),
233
  barmode="group",
234
+ height=300,
235
  xaxis=dict(
236
+ title=dict(text="Modality", font=dict(color=TEXT_MAIN)),
237
+ tickfont=dict(size=12, color=TEXT_MAIN),
 
238
  gridcolor=BORDER,
239
  ),
240
  yaxis=dict(
241
+ title=dict(text="Softmax Score (%)", font=dict(color=TEXT_MAIN)),
242
+ range=[0, 125],
 
 
243
  gridcolor=BORDER,
244
  ticksuffix="%",
245
+ tickfont=dict(color=TEXT_MAIN),
246
  ),
247
  legend=dict(
248
  orientation="h",
249
+ y=1.14,
250
+ font=dict(size=11, color=TEXT_MAIN),
251
+ bgcolor="rgba(0,0,0,0)",
252
  ),
253
  bargap=0.22,
254
  bargroupgap=0.06,
 
255
  )
256
  return fig
257
 
 
271
  y=trust_vals,
272
  marker=dict(
273
  color=bar_colors,
274
+ opacity=0.90,
275
+ line=dict(color=CARD_BG, width=2),
276
  ),
277
  text=[f"{v:.1f}%" for v in trust_vals],
278
  textposition="outside",
279
+ textfont=dict(size=11, color=TEXT_MAIN, family="'DM Mono', monospace"),
280
  hovertemplate=(
281
  "<b>%{x}</b><br>"
282
  "Trust Level: %{y:.2f}%<br>"
 
288
  for level, label, color in [(80, "High Trust", GREEN), (50, "Threshold", AMBER)]:
289
  fig.add_hline(
290
  y=level,
291
+ line=dict(color=color, width=1.5, dash="dot"),
292
  annotation_text=label,
293
  annotation_position="right",
294
  annotation_font=dict(size=9, color=color),
 
298
  **PLOTLY_LAYOUT,
299
  title=dict(
300
  text="Trust Score by Modality",
301
+ font=dict(size=13, color=INK_DARK),
302
  x=0,
303
  ),
304
+ height=300,
305
  xaxis=dict(
306
+ title=dict(text="Modality", font=dict(color=TEXT_MAIN)),
307
+ tickfont=dict(size=12, color=TEXT_MAIN),
 
308
  gridcolor=BORDER,
309
  ),
310
  yaxis=dict(
311
+ title=dict(text="Trust Level (%)", font=dict(color=TEXT_MAIN)),
312
+ range=[0, 120],
 
 
313
  gridcolor=BORDER,
314
  ticksuffix="%",
315
+ tickfont=dict(color=TEXT_MAIN),
316
  ),
317
  bargap=0.38,
 
318
  )
319
  return fig
320
 
 
336
  y=uncertainty_vals,
337
  marker=dict(
338
  color=bar_colors,
339
+ opacity=0.90,
340
+ line=dict(color=CARD_BG, width=2),
341
  ),
342
  text=[f"{v:.1f}%" for v in uncertainty_vals],
343
  textposition="outside",
344
+ textfont=dict(size=11, color=TEXT_MAIN, family="'DM Mono', monospace"),
345
  customdata=[[f"p_misinfo={m:.1f}%"] for m in misinfo_pcts],
346
  hovertemplate=(
347
  "<b>%{x}</b><br>"
 
354
 
355
  fig.add_hline(
356
  y=100,
357
+ line=dict(color=RED, width=1.5, dash="dot"),
358
  annotation_text="Max Entropy (no signal)",
359
  annotation_position="right",
360
  annotation_font=dict(size=9, color=RED),
361
  )
362
  fig.add_hline(
363
  y=50,
364
+ line=dict(color=AMBER, width=1.5, dash="dot"),
365
  annotation_text="Mid Uncertainty",
366
  annotation_position="right",
367
  annotation_font=dict(size=9, color=AMBER),
 
371
  **PLOTLY_LAYOUT,
372
  title=dict(
373
  text="Uncertainty Analysis (Shannon Entropy)",
374
+ font=dict(size=13, color=INK_DARK),
375
  x=0,
376
  ),
377
+ height=300,
378
  xaxis=dict(
379
+ title=dict(text="Modality", font=dict(color=TEXT_MAIN)),
380
+ tickfont=dict(size=12, color=TEXT_MAIN),
 
381
  gridcolor=BORDER,
382
  ),
383
  yaxis=dict(
384
+ title=dict(text="Uncertainty (%)", font=dict(color=TEXT_MAIN)),
385
+ range=[0, 130],
 
 
386
  gridcolor=BORDER,
387
  ticksuffix="%",
388
+ tickfont=dict(color=TEXT_MAIN),
389
  ),
390
  bargap=0.38,
 
391
  )
392
  return fig
393
 
 
397
  return _empty_fig("Comment Sentiment Distribution")
398
 
399
  df = comments_df.copy()
400
+ df["compound"] = [s.get("compound", 0) for s in sentiments]
401
+ df["label"] = [s.get("label", "NEUTRAL") for s in sentiments]
402
+ df["color"] = df["label"].map({"POSITIVE": STORMY_SKY, "NEGATIVE": STORMY_SLATE, "NEUTRAL": INK_GREY})
 
 
 
 
403
  df["text_short"] = df["text"].str[:80] + "…"
404
 
405
  fig = go.Figure()
406
+ for lbl, clr, disp in [
407
+ ("POSITIVE", STORMY_SKY, "Positively Engagement"),
408
+ ("NEGATIVE", STORMY_SLATE, "Negatively Engagement"),
409
+ ("NEUTRAL", INK_GREY, "Neutral"),
410
  ]:
411
  sub = df[df["label"] == lbl]
412
  if sub.empty:
 
415
  x=sub.index,
416
  y=sub["compound"],
417
  mode="markers",
418
+ name=disp,
419
  marker=dict(
420
  size=np.clip(np.log1p(sub["likes"].fillna(0)) * 4 + 4, 4, 20),
421
  color=clr,
422
+ opacity=0.80,
423
  line=dict(width=0),
424
  ),
425
  text=sub["text_short"],
 
429
  fig.add_hline(y=0, line=dict(color=BORDER, width=1, dash="dot"))
430
  fig.update_layout(
431
  **PLOTLY_LAYOUT,
432
+ title=dict(text="Comment Sentiment (size = likes)", font=dict(size=13, color=INK_DARK), x=0),
433
  height=320,
434
+ xaxis=dict(title="Comment index", gridcolor=BORDER, showgrid=False, tickfont=dict(color=TEXT_MAIN)),
435
+ yaxis=dict(title="Compound score", gridcolor=BORDER, range=[-1.1, 1.1], tickfont=dict(color=TEXT_MAIN)),
436
+ legend=dict(orientation="h", y=1.14, font=dict(size=11, color=TEXT_MAIN)),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  )
438
  return fig
439
 
 
457
  fig.add_trace(go.Bar(
458
  name="Positively Engagement",
459
  y=list(pw),
460
+ x=[v / max_p * 100 for v in pv],
461
  orientation="h",
462
+ marker_color=STORMY_SKY,
 
 
 
 
 
 
463
  hovertemplate="<b>%{y}</b><br>Score: %{x:.1f}<extra></extra>",
464
  ))
465
 
 
469
  fig.add_trace(go.Bar(
470
  name="Negatively Engagement",
471
  y=list(nw),
472
+ x=[-v / max_n * 100 for v in nv],
473
  orientation="h",
474
+ marker_color=STORMY_SLATE,
 
 
 
 
 
 
475
  hovertemplate="<b>%{y}</b><br>Score: %{x:.1f}<extra></extra>",
476
  ))
477
 
478
  fig.update_layout(
479
  **PLOTLY_LAYOUT,
480
+ title=dict(text="Sentiment-Weighted Keywords", font=dict(size=13, color=INK_DARK), x=0),
481
  height=360,
482
  barmode="overlay",
483
  xaxis=dict(
484
  title="← Negatively Engagement | Positively Engagement →",
 
 
485
  gridcolor=BORDER,
486
  zeroline=True,
487
+ zerolinecolor=INK_DARK,
488
  zerolinewidth=2,
489
+ tickfont=dict(color=TEXT_MAIN),
490
  ),
491
+ yaxis=dict(tickfont=dict(size=10, color=TEXT_MAIN)),
492
+ legend=dict(orientation="h", y=1.1, font=dict(size=11, color=TEXT_MAIN)),
 
 
 
 
 
 
 
 
493
  )
494
  return fig
495
 
 
500
  text="No data available",
501
  x=0.5, y=0.5,
502
  showarrow=False,
503
+ font=dict(size=14, color=TEXT_DIM),
 
 
 
 
 
 
504
  )
505
+ fig.update_layout(**PLOTLY_LAYOUT, title=dict(text=title, x=0, font=dict(color=INK_DARK)), height=250)
506
  return fig