Sajjadistic's picture
Update app.py
cc0e613 verified
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()