"""
๐ฌ NLP Insight Engine โ Text Analytics Dashboard
Sentiment ยท NER ยท Keywords ยท Summarisation ยท Language Detection ยท Text Comparison
Built with HuggingFace Transformers + Gradio
"""
import gradio as gr
import json
from utils.pipeline import NLPPipeline
from utils.helpers import EXAMPLE_TEXTS
nlp = NLPPipeline()
ENT_CSS = {
"PER": ("#dbeafe", "#1e40af"),
"ORG": ("#dcfce7", "#166534"),
"LOC": ("#fef3c7", "#92400e"),
"MISC": ("#f3e8ff", "#6b21a8"),
}
LANG_FLAGS = {
"English": "๐ฌ๐ง", "Spanish": "๐ช๐ธ", "French": "๐ซ๐ท", "German": "๐ฉ๐ช",
"Italian": "๐ฎ๐น", "Portuguese": "๐ต๐น", "Dutch": "๐ณ๐ฑ", "Turkish": "๐น๐ท",
"Arabic": "๐ธ๐ฆ", "Urdu": "๐ต๐ฐ", "Chinese": "๐จ๐ณ", "Japanese": "๐ฏ๐ต",
"Korean": "๐ฐ๐ท", "Cyrillic": "๐ท๐บ", "Devanagari": "๐ฎ๐ณ",
}
def make_warning_banner(lang):
"""Generate a warning banner for non-English text."""
return (
f'
'
f'โ ๏ธ'
f''
f'Non-English text detected ({lang}). '
f'Sentiment and NER results may be inaccurate as the models are trained on English text.'
f'
'
)
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# SINGLE TEXT ANALYSIS
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def analyse(text, sentiment_mode, do_sentiment, do_ner, do_keywords, do_summary):
if not text or len(text.strip()) < 20:
msg = "Please enter at least 20 characters of text.
"
return msg, msg, msg, msg, msg, msg, ""
text = text.strip()
word_count = len(text.split())
sent_count = max(1, text.count(".") + text.count("!") + text.count("?"))
mode = "financial" if "Financial" in sentiment_mode else "general"
sent_result = None
entities = None
kw = None
summary_text = None
# โโ Language Detection (always on) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
lang_result = nlp.detect_language(text)
lang = lang_result["language"]
lang_conf = lang_result["confidence"]
lang_script = lang_result["script"]
flag = LANG_FLAGS.get(lang, "๐")
is_non_english = lang != "English" and lang != "Unknown"
lang_html = (
f''
f'
{flag}
'
f'
{lang}
'
f'
Confidence: {lang_conf:.1%}
'
f'
Script: {lang_script}
'
f'
'
)
warning = make_warning_banner(lang) if is_non_english else ""
# โโ Sentiment โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if do_sentiment:
sent_result = nlp.analyse_sentiment(text, mode=mode)
label = sent_result["label"]
score = sent_result["score"]
emoji = {"POSITIVE": "๐", "NEGATIVE": "๐", "NEUTRAL": "๐"}.get(label, "๐ค")
if mode == "financial":
badge = ('๐ FinBERT โ trained on financial text')
else:
badge = ('๐ฌ DistilBERT โ trained on movie reviews')
bars_html = ""
if "details" in sent_result:
for d in sent_result["details"]:
lbl = d["label"].title()
s = d["score"]
color = {"Positive": "#22c55e", "Negative": "#ef4444", "Neutral": "#64748b"}.get(lbl, "#64748b")
width = max(4, int(s * 100))
bars_html += (
f''
)
sentiment_html = (
f'{warning}'
f''
f'
{emoji} {label.title()}
'
f'
Confidence: {score:.1%}
'
f'{badge}
{bars_html}'
)
else:
sentiment_html = "Sentiment analysis disabled.
"
# โโ NER โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if do_ner:
entities = nlp.extract_entities(text)
ner_warning = warning if is_non_english else ""
if entities:
tags = ""
for ent in entities:
bg, fg = ENT_CSS.get(ent["entity_group"], ("#f1f5f9", "#334155"))
tags += (f'{ent["word"]} ({ent["entity_group"]}) ')
rows = "".join(
f''
f'| {e["word"]} | '
f'{e["entity_group"]} | '
f'{e["score"]:.2%} |
'
for e in entities
)
table = (f''
f''
f'| Entity | '
f'Type | '
f'Confidence | '
f'
{rows}
')
ner_html = f'{ner_warning}{tags}
{table}'
else:
ner_html = f'{ner_warning}No named entities detected.
'
else:
ner_html = "NER disabled.
"
# โโ Keywords โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if do_keywords:
kw = nlp.extract_keywords(text)
if kw:
max_score = kw[0][1]
bars = ""
for word, score in kw[:12]:
width = max(6, int((score / max_score) * 100))
bars += (f''
f'{word}'
f''
f'{score:.4f}
')
keywords_html = bars
else:
keywords_html = "No keywords extracted.
"
else:
keywords_html = "Keyword extraction disabled.
"
# โโ Summary โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if do_summary:
summary_text = nlp.summarise(text)
summary_len = len(summary_text.split())
compression = (1 - summary_len / word_count) * 100 if word_count else 0
summary_html = (
f'{summary_text}
'
f''
f'{word_count} โ {summary_len} words ({compression:.0f}% reduction)
'
)
else:
summary_html = "Summarisation disabled.
"
# โโ Metrics โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
sent_label = sent_result["label"].title() if sent_result else "โ"
sent_emoji = {"Positive": "๐", "Negative": "๐", "Neutral": "๐"}.get(sent_label, "โ")
ent_count = len(entities) if entities else 0
ms = ("background:linear-gradient(145deg,#f8fafc,#f1f5f9);border:1px solid #e2e8f0;"
"border-radius:12px;padding:1rem;text-align:center;")
vs = "font-family:monospace;font-size:1.6rem;font-weight:700;color:#0f172a;"
ls = "font-size:0.78rem;color:#64748b;text-transform:uppercase;"
metrics_html = (
f''
f'
'
f'
'
f'
{sent_emoji} {sent_label}
Sentiment
'
f'
'
f'
'
f'
'
)
# โโ JSON export โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
export = {"word_count": word_count, "sentence_count": sent_count, "language": lang_result}
if sent_result:
export["sentiment"] = sent_result
if entities:
export["entities"] = entities
if kw:
export["keywords"] = kw
if summary_text:
export["summary"] = summary_text
json_str = json.dumps(export, indent=2, default=str)
return metrics_html, sentiment_html, ner_html, keywords_html, summary_html, lang_html, json_str
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# TEXT COMPARISON
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def compare(text_a, text_b, sentiment_mode):
if not text_a or len(text_a.strip()) < 20:
return "Text A needs at least 20 characters.
"
if not text_b or len(text_b.strip()) < 20:
return "Text B needs at least 20 characters.
"
mode = "financial" if "Financial" in sentiment_mode else "general"
result = nlp.compare_texts(text_a.strip(), text_b.strip(), mode=mode)
a = result["text_a"]
b = result["text_b"]
shared = result["shared_keywords"]
a_label = a["sentiment"]["label"].title()
b_label = b["sentiment"]["label"].title()
a_score = a["sentiment"]["score"]
b_score = b["sentiment"]["score"]
a_emoji = {"Positive": "๐", "Negative": "๐", "Neutral": "๐"}.get(a_label, "๐ค")
b_emoji = {"Positive": "๐", "Negative": "๐", "Neutral": "๐"}.get(b_label, "๐ค")
a_lang = a["language"]["language"]
b_lang = b["language"]["language"]
a_flag = LANG_FLAGS.get(a_lang, "๐")
b_flag = LANG_FLAGS.get(b_lang, "๐")
a_ents = len(a["entities"])
b_ents = len(b["entities"])
a_ent_words = {e["word"].lower() for e in a["entities"]}
b_ent_words = {e["word"].lower() for e in b["entities"]}
shared_ents = a_ent_words & b_ent_words
# Non-English warnings for comparison
comp_warnings = ""
non_eng = []
if a_lang != "English" and a_lang != "Unknown":
non_eng.append(f"Text A ({a_lang})")
if b_lang != "English" and b_lang != "Unknown":
non_eng.append(f"Text B ({b_lang})")
if non_eng:
comp_warnings = (
f''
f'โ ๏ธ'
f''
f'Non-English text detected in {" and ".join(non_eng)}. '
f'Sentiment and NER results may be inaccurate as the models are trained on English text.'
f'
'
)
cs = ("background:linear-gradient(145deg,#f8fafc,#f1f5f9);border:1px solid #e2e8f0;"
"border-radius:12px;padding:1rem;text-align:center;")
hdr = "font-size:0.72rem;color:#94a3b8;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:0.5rem;"
html = f"""
{comp_warnings}
Text A
{a_emoji} {a_label}
Confidence: {a_score:.1%}
Text B
{b_emoji} {b_label}
Confidence: {b_score:.1%}
๐ Shared Keywords ({len(shared)})
{"".join(f'{kw}' for kw in shared) if shared else 'No shared keywords found'}
๐ท๏ธ Shared Entities ({len(shared_ents)})
{"".join(f'{e}' for e in shared_ents) if shared_ents else 'No shared entities found'}
"""
return html
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# GRADIO UI
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
with gr.Blocks(title="NLP Insight Engine") as demo:
gr.Markdown("# ๐ฌ NLP Insight Engine")
gr.Markdown("*Sentiment ยท Entities ยท Keywords ยท Summarisation ยท Language Detection ยท Text Comparison*")
with gr.Tabs():
with gr.Tab("๐ Analyse Text"):
with gr.Row():
with gr.Column(scale=3):
text_input = gr.Textbox(
label="Enter text to analyse",
placeholder="Paste an article, review, report, or any text hereโฆ",
lines=8, max_lines=20,
)
examples = gr.Examples(
examples=[[v] for v in EXAMPLE_TEXTS.values()],
inputs=[text_input],
label="Try an example",
)
with gr.Column(scale=1):
gr.Markdown("### โ๏ธ Options")
sentiment_mode = gr.Radio(
choices=["๐ฌ General (movie reviews)", "๐ Financial (FinBERT)"],
value="๐ฌ General (movie reviews)",
label="Sentiment Model",
)
do_sentiment = gr.Checkbox(label="Sentiment Analysis", value=True)
do_ner = gr.Checkbox(label="Named Entity Recognition", value=True)
do_keywords = gr.Checkbox(label="Keyword Extraction", value=True)
do_summary = gr.Checkbox(label="Extractive Summary", value=True)
analyse_btn = gr.Button("๐ Analyse", variant="primary", size="lg")
metrics_output = gr.HTML(label="Quick Metrics")
with gr.Tabs():
with gr.Tab("๐ฌ Sentiment"):
sentiment_output = gr.HTML()
with gr.Tab("๐ท๏ธ Entities"):
ner_output = gr.HTML()
with gr.Tab("๐ Keywords"):
keywords_output = gr.HTML()
with gr.Tab("๐ Summary"):
summary_output = gr.HTML()
with gr.Tab("๐ Language"):
lang_output = gr.HTML()
with gr.Tab("๐ฅ Export JSON"):
json_output = gr.Textbox(label="Analysis Results (JSON)", lines=15)
analyse_btn.click(
fn=analyse,
inputs=[text_input, sentiment_mode, do_sentiment, do_ner, do_keywords, do_summary],
outputs=[metrics_output, sentiment_output, ner_output, keywords_output, summary_output, lang_output, json_output],
)
with gr.Tab("โ๏ธ Compare Texts"):
gr.Markdown("### Compare two texts side-by-side")
gr.Markdown("*Analyse sentiment, entities, keywords, and language across two texts simultaneously.*")
with gr.Row():
text_a = gr.Textbox(label="Text A", placeholder="Paste first text hereโฆ", lines=6)
text_b = gr.Textbox(label="Text B", placeholder="Paste second text hereโฆ", lines=6)
compare_mode = gr.Radio(
choices=["๐ฌ General (movie reviews)", "๐ Financial (FinBERT)"],
value="๐ฌ General (movie reviews)",
label="Sentiment Model",
)
compare_btn = gr.Button("โ๏ธ Compare", variant="primary", size="lg")
compare_output = gr.HTML(label="Comparison Results")
compare_btn.click(
fn=compare,
inputs=[text_a, text_b, compare_mode],
outputs=[compare_output],
)
gr.Markdown("**๐ก Try comparing:** A positive product review vs a negative one, "
"or a bullish financial article vs a bearish one.")
gr.Markdown(
""
"NLP Insight Engine ยท Built by Wasif ยท "
"Models: distilbert-sst2 ยท ProsusAI/finbert ยท dslim/bert-base-NER ยท "
"100% free, no API keys"
""
)
if __name__ == "__main__":
demo.launch(ssr_mode=False)