File size: 5,475 Bytes
6e15cca 306b7e2 6e15cca 7d06466 6e15cca 15a6e11 cc0e613 39721f8 15a6e11 cc0e613 39721f8 15a6e11 cc0e613 15a6e11 39721f8 15a6e11 39721f8 15a6e11 cc0e613 15a6e11 39721f8 cc0e613 15a6e11 7d06466 6e15cca 7d06466 6e15cca 9277452 7d06466 6e15cca 9277452 6e15cca 306b7e2 9277452 7d06466 9277452 6e15cca 9277452 240a85e 6e15cca 7d06466 9277452 6e15cca 7d06466 9277452 6f43b48 9277452 6f43b48 6e15cca 7d06466 9277452 6f43b48 6e15cca 6f43b48 240a85e 6f43b48 6e15cca 39721f8 306b7e2 6e15cca 39721f8 6e15cca 7d06466 9277452 39721f8 7d06466 9277452 cc0e613 6e15cca 9277452 39721f8 306b7e2 cc0e613 306b7e2 39721f8 306b7e2 cc0e613 306b7e2 7d06466 39721f8 7d06466 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
import json
from typing import Any, Dict, List
import gradio as gr
from analysis_core import extract_chats, get_chat_name, analyze_chat
THEME = gr.themes.Soft(
primary_hue="fuchsia",
secondary_hue="pink",
neutral_hue="slate",
)
CSS = """
/* full width */
.gradio-container {
max-width: 100% !important;
padding-left: 24px !important;
padding-right: 24px !important;
}
/* buttons */
.btn-load button {
background: linear-gradient(90deg, #d946ef, #ec4899) !important;
border-radius: 16px !important;
font-weight: 700 !important;
}
.btn-analyze button {
background: linear-gradient(90deg, #22c55e, #16a34a) !important;
border-radius: 16px !important;
font-weight: 700 !important;
}
/* status */
.status-box textarea {
border-radius: 14px !important;
font-weight: 500;
}
/* plot */
.plot-container { min-height: 520px; }
/* -------- Dataframe styling (match magenta theme) -------- */
.lex-table .wrap {
border-radius: 16px !important;
border: 1px solid rgba(236,72,153,0.28) !important;
overflow: hidden !important;
}
.lex-table table {
border-collapse: separate !important;
border-spacing: 0 !important;
}
/* header */
.lex-table thead th {
background: linear-gradient(90deg, rgba(217,70,239,0.35), rgba(236,72,153,0.25)) !important;
color: rgba(255,255,255,0.92) !important;
font-weight: 800 !important;
border-bottom: 1px solid rgba(236,72,153,0.28) !important;
}
/* body cells */
.lex-table tbody td {
background: rgba(255,255,255,0.02) !important;
border-bottom: 1px solid rgba(236,72,153,0.10) !important;
}
/* zebra rows */
.lex-table tbody tr:nth-child(even) td {
background: rgba(217,70,239,0.06) !important;
}
/* hover */
.lex-table tbody tr:hover td {
background: rgba(236,72,153,0.14) !important;
}
/* align */
.lex-table td, .lex-table th { padding: 10px 12px !important; }
/* make numbers a bit clearer */
.lex-table td:last-child {
font-variant-numeric: tabular-nums;
}
"""
def _path(file_obj) -> str:
if file_obj is None:
raise gr.Error("upload Telegram result.json first.")
if isinstance(file_obj, str):
return file_obj
if isinstance(file_obj, dict) and "path" in file_obj:
return file_obj["path"]
if hasattr(file_obj, "name") and isinstance(file_obj.name, str):
return file_obj.name
raise gr.Error("could not read uploaded file path.")
def lex_list_to_rows(lst: List[Dict[str, Any]]):
if not lst:
return []
return [[d["word"], round(float(d["score"]), 6)] for d in lst]
def load_chats(file_obj):
p = _path(file_obj)
with open(p, "r", encoding="utf-8") as f:
data = json.load(f)
chats = extract_chats(data)
labels = [f"{i} | {get_chat_name(c, f'Chat {i}')}" for i, c in enumerate(chats)]
if not labels:
raise gr.Error("no chats found. make sure you uploaded Telegram export result.json")
state = {"data": data}
return gr.update(choices=labels, value=labels[0]), state, f"loaded {len(labels)} chats."
def run_analysis(choice: str, state: Dict[str, Any], max_bert_persian: int):
if not state or "data" not in state:
raise gr.Error("Upload result.json and load chats first.")
if not choice:
raise gr.Error("Choose a chat first.")
chats = extract_chats(state["data"])
idx = int(choice.split("|", 1)[0].strip())
if idx < 0 or idx >= len(chats):
raise gr.Error("Invalid chat selection.")
result, fig, pos_top, neg_top = analyze_chat(
chats[idx],
max_bert_persian=int(max_bert_persian),
)
pos_rows = lex_list_to_rows(pos_top) if pos_top else []
neg_rows = lex_list_to_rows(neg_top) if neg_top else []
return result, fig, pos_rows, neg_rows
with gr.Blocks(
title="Telegram Sentiment Analysis",
theme=THEME,
css=CSS,
) as demo:
gr.Markdown(
"upload Telegram result.json → load chats → choose chat → analyze. "
"weekly plot includes peak/low word annotations. tables show top lex words."
)
state = gr.State({})
status = gr.Textbox(
label="Status",
interactive=False,
elem_classes=["status-box"],
)
file_in = gr.File(
label="Upload Telegram result.json",
file_types=[".json"],
)
chat_dd = gr.Dropdown(
label="Choose chat",
choices=[],
value=None,
)
max_bert = gr.Slider(
minimum=0,
maximum=2000,
value=300,
step=50,
label="Max Persian messages to run BERT on (speed control)",
)
load_btn = gr.Button("Load chats", elem_classes=["btn-load"])
analyze_btn = gr.Button("Analyze selected chat", elem_classes=["btn-analyze"])
out_json = gr.JSON(label="Results (JSON)")
out_plot = gr.Plot(
label="Weekly emotion trajectory (with peak/low word annotations)",
elem_classes=["plot-container"],
)
out_pos = gr.Dataframe(
label="Top 5 positive lex words (word, score)",
headers=["word", "score"],
elem_classes=["lex-table"],
)
out_neg = gr.Dataframe(
label="Top 5 negative lex words (word, score)",
headers=["word", "score"],
elem_classes=["lex-table"],
)
load_btn.click(load_chats, inputs=[file_in], outputs=[chat_dd, state, status])
analyze_btn.click(
run_analysis,
inputs=[chat_dd, state, max_bert],
outputs=[out_json, out_plot, out_pos, out_neg],
)
demo.launch() |