|
import streamlit as st |
|
import torch |
|
from transformers import BertForSequenceClassification, BertTokenizerFast |
|
from transformers import AutoModelForSequenceClassification, AutoTokenizer |
|
import time |
|
import pandas as pd |
|
import base64 |
|
from PIL import Image, ImageDraw, ImageFont |
|
import io |
|
import streamlit.components.v1 as components |
|
|
|
|
|
st.set_page_config( |
|
page_title="SMS Spam Guard", |
|
page_icon="🛡️", |
|
layout="wide", |
|
initial_sidebar_state="expanded" |
|
) |
|
|
|
|
|
def create_spam_guard_logo(): |
|
width, height = 200, 200 |
|
img = Image.new('RGBA', (width, height), (0,0,0,0)) |
|
draw = ImageDraw.Draw(img) |
|
|
|
|
|
primary_blue = (20, 120, 220) |
|
accent_green = (0, 200, 150) |
|
light_accent_blue = (100, 180, 240) |
|
white_color = (255, 255, 255) |
|
dark_gray_text = (50, 50, 50) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
path = [ |
|
(width * 0.15, height * 0.2), (width * 0.85, height * 0.2), |
|
(width * 0.75, height * 0.8), (width * 0.25, height * 0.8) |
|
] |
|
draw.polygon(path, fill=primary_blue) |
|
|
|
|
|
draw.line([ |
|
(width * 0.3, height * 0.35), |
|
(width * 0.7, height * 0.35), |
|
(width * 0.7, height * 0.5), |
|
(width * 0.3, height * 0.5), |
|
(width * 0.3, height * 0.65), |
|
(width * 0.7, height * 0.65) |
|
], fill=accent_green, width=18, joint="miter") |
|
|
|
|
|
draw.polygon([ |
|
(width * 0.18, height * 0.22), (width * 0.82, height * 0.22), |
|
(width * 0.72, height * 0.78), (width * 0.28, height * 0.78) |
|
], outline=light_accent_blue, width=4) |
|
|
|
|
|
try: |
|
|
|
|
|
|
|
font = ImageFont.truetype("arialbd.ttf", 70) |
|
except IOError: |
|
font = ImageFont.load_default() |
|
|
|
text = "SG" |
|
text_bbox = draw.textbbox((0,0), text, font=font) |
|
text_width = text_bbox[2] - text_bbox[0] |
|
text_height = text_bbox[3] - text_bbox[1] |
|
|
|
text_x = (width - text_width) / 2 |
|
|
|
|
|
text_y = (height - text_height) / 2 + 5 |
|
|
|
|
|
draw.text((text_x, text_y), text, font=font, fill=white_color) |
|
|
|
buffered = io.BytesIO() |
|
img.save(buffered, format="PNG") |
|
img_str = base64.b64encode(buffered.getvalue()).decode() |
|
return f"data:image/png;base64,{img_str}" |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
:root { |
|
--cm-blue: #0072d0; /* 调深主蓝色 */ |
|
--cm-light-blue: #4aa3df; |
|
--cm-green: #00a651; /* 调深绿色 */ |
|
--cm-bright-green: #70c050; /* 调整亮绿色 */ |
|
--cm-dark-blue: #004080; /* 更深的蓝色 */ |
|
--cm-gray: #f5f7fa; |
|
--text-color: #222222; /* 更深的文本颜色 */ |
|
--card-bg: rgba(255, 255, 255, 0.92); /* 增加不透明度 */ |
|
--card-border: rgba(180, 180, 180, 0.5); /* 更明显的边框 */ |
|
} |
|
|
|
body { |
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; |
|
line-height: 1.6; |
|
color: var(--text-color); |
|
-webkit-font-smoothing: antialiased; /* 改善文字渲染 */ |
|
-moz-osx-font-smoothing: grayscale; /* 改善文字渲染 */ |
|
} |
|
|
|
.stApp { |
|
background: linear-gradient(135deg, #c4e3ff 0%, #e8f4ff 60%, #ddf5dd 100%); /* 更亮的背景以增加对比度 */ |
|
} |
|
|
|
/* 标题强化样式 - 提高对比度 */ |
|
.main-header { |
|
font-size: 3.2rem !important; |
|
font-weight: 800 !important; |
|
margin-bottom: 0.3rem; |
|
color: var(--cm-dark-blue); /* 纯色替代渐变 */ |
|
text-shadow: none; /* 移除阴影以增加锐利度 */ |
|
-webkit-text-fill-color: var(--cm-dark-blue); /* 覆盖之前的透明设置 */ |
|
-moz-text-fill-color: var(--cm-dark-blue); |
|
text-fill-color: var(--cm-dark-blue); |
|
letter-spacing: -0.5px; |
|
} |
|
|
|
.sub-header { |
|
font-size: 1.25rem; |
|
color: #333333; /* 更深的颜色 */ |
|
margin-bottom: 2rem; |
|
font-weight: 500; |
|
} |
|
|
|
/* 各组件样式强化 - 更高对比度 */ |
|
.highlight { |
|
background-color: var(--card-bg); |
|
padding: 1.8rem; |
|
border-radius: 16px; |
|
margin-bottom: 1.5rem; |
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); /* 更深的阴影 */ |
|
transition: all 0.3s ease-out; |
|
border: 1px solid var(--card-border); |
|
} |
|
.highlight:hover { |
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.18); |
|
transform: translateY(-3px); |
|
} |
|
.result-card, .card-gradient, .card-shadow { |
|
padding: 1.8rem; |
|
border-radius: 16px; |
|
margin-bottom: 1.2rem; |
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); |
|
transition: transform 0.3s ease-out, box-shadow 0.3s ease-out; |
|
border: 1px solid var(--card-border); |
|
background-color: white; /* 纯白色背景 */ |
|
} |
|
.result-card:hover, .card-gradient:hover, .card-shadow:hover { |
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.18); |
|
transform: translateY(-4px) scale(1.01); |
|
} |
|
.spam-alert { |
|
background: #fff0f0; /* 移除渐变,使用纯色 */ |
|
border-left: 8px solid #EF4444; |
|
color: #aa0000; /* 更深的红色 */ |
|
} |
|
.ham-alert { |
|
background: #f0fff0; /* 移除渐变,使用纯色 */ |
|
border-left: 8px solid var(--cm-green); |
|
color: #005500; /* 更深的绿色 */ |
|
} |
|
|
|
/* 页脚加强 */ |
|
.footer { |
|
text-align: center; |
|
margin-top: 4rem; |
|
padding-top: 2rem; |
|
border-top: 1px solid var(--card-border); |
|
font-size: 0.9rem; |
|
color: #444444; /* 更深的灰色 */ |
|
} |
|
|
|
/* 标签样式加强 */ |
|
.language-tag { |
|
display: inline-block; |
|
padding: 0.4rem 1rem; |
|
background-color: var(--cm-dark-blue); /* 更深的蓝色 */ |
|
color: white; |
|
border-radius: 20px; |
|
font-size: 0.95rem; |
|
font-weight: 600; |
|
margin-right: 0.6rem; |
|
box-shadow: 0 3px 6px rgba(0, 64, 128, 0.3); /* 更深的阴影 */ |
|
} |
|
|
|
/* 按钮样式加强 */ |
|
.stButton > button { |
|
border-radius: 12px; |
|
padding: 0.7rem 1.4rem; |
|
font-weight: 600; |
|
transition: all 0.25s ease; |
|
box-shadow: 0 4px 8px rgba(0,0,0,0.12); |
|
font-size: 1rem; |
|
border: 1px solid rgba(0,0,0,0.1); /* 添加边框 */ |
|
} |
|
.stButton > button:hover { |
|
transform: translateY(-3px); |
|
box-shadow: 0 6px 12px rgba(0,0,0,0.15); |
|
} |
|
|
|
/* 分析按钮更加突出 */ |
|
div.stButton[key="analyze_btn"] > button, |
|
button[kind="primary"] |
|
{ |
|
background: var(--cm-blue); /* 纯色替代渐变 */ |
|
color: white !important; |
|
font-weight: 700; |
|
padding: 0.8rem 1.5rem; |
|
font-size: 1.1rem; |
|
letter-spacing: 0.5px; |
|
text-transform: uppercase; |
|
border: none; |
|
} |
|
div.stButton[key="analyze_btn"] > button:hover { |
|
background: var(--cm-dark-blue); |
|
box-shadow: 0 8px 15px rgba(0, 64, 128, 0.3); |
|
} |
|
|
|
/* 侧边栏按钮样式 */ |
|
.sidebar .stButton > button { |
|
background-color: var(--cm-green); |
|
text-transform: none; |
|
} |
|
.sidebar .stButton > button:hover { |
|
background-color: var(--cm-bright-green); |
|
} |
|
|
|
/* 文本框样式加强 */ |
|
div.stTextArea > div > div > textarea { |
|
border-color: #5599dd; /* 更鲜明的边框颜色 */ |
|
border-width: 2px; |
|
border-radius: 12px; |
|
background-color: white; /* 纯白色背景 */ |
|
font-size: 1.05rem; |
|
padding: 12px; |
|
color: #222; /* 确保文本为深色 */ |
|
} |
|
div.stTextArea > div > div > textarea:focus { |
|
border-color: var(--cm-blue); |
|
box-shadow: 0 0 0 4px rgba(0, 114, 208, 0.25); |
|
background-color: white; |
|
} |
|
|
|
/* 玻璃效果加强 - 减少模糊,增加清晰度 */ |
|
.glass-effect { |
|
background: rgba(255, 255, 255, 0.92); /* 增加不透明度 */ |
|
backdrop-filter: blur(6px); /* 减少模糊程度 */ |
|
border-radius: 20px; |
|
border: 1px solid rgba(255, 255, 255, 0.7); /* 更明显的边框 */ |
|
transition: all 0.3s ease-in-out; |
|
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15); |
|
} |
|
.glass-effect:hover { |
|
box-shadow: 0 15px 30px rgba(31, 38, 135, 0.25); |
|
transform: translateY(-5px); |
|
} |
|
|
|
/* 加载动画加强 */ |
|
.loading-animation-container svg { |
|
animation: spin 1.2s linear infinite, pulse-opacity 1.2s ease-in-out infinite alternate; |
|
transform-origin: center center; |
|
width: 70px; |
|
height: 70px; |
|
filter: drop-shadow(0 0 2px rgba(0,0,0,0.2)); /* 添加轻微阴影 */ |
|
} |
|
|
|
/* 图表样式加强 */ |
|
.stChart > div > div > div > svg g rect { |
|
fill: var(--cm-blue) !important; |
|
transition: fill 0.3s ease; |
|
} |
|
.stChart > div > div > div > svg g rect:hover { |
|
fill: var(--cm-dark-blue) !important; |
|
} |
|
|
|
/* 结果文本加强 - 提高对比度 */ |
|
.result-content { |
|
font-size: 1.1rem; |
|
color: #222; /* 确保深色文本 */ |
|
} |
|
.result-value { |
|
font-size: 1.2rem; |
|
font-weight: 700; |
|
color: var(--cm-dark-blue); |
|
} |
|
.result-title { |
|
font-size: 1.4rem; |
|
font-weight: 700; |
|
color: var(--cm-dark-blue); |
|
margin-bottom: 15px; |
|
letter-spacing: 0.5px; |
|
} |
|
|
|
/* 强调主要文字内容 - 提高对比度 */ |
|
h3 { |
|
font-size: 1.8rem !important; |
|
font-weight: 700 !important; |
|
color: var(--cm-dark-blue) !important; |
|
letter-spacing: -0.5px; |
|
margin-bottom: 20px !important; |
|
} |
|
h4 { |
|
font-size: 1.4rem !important; |
|
font-weight: 700 !important; |
|
letter-spacing: -0.3px; |
|
color: #222 !important; |
|
} |
|
h5 { |
|
font-size: 1.2rem !important; |
|
font-weight: 600 !important; |
|
color: #333 !important; |
|
} |
|
p, li { |
|
font-size: 1.05rem !important; |
|
color: #333 !important; |
|
} |
|
strong { |
|
font-weight: 700; |
|
color: var(--cm-dark-blue); |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
@st.cache_resource |
|
def load_language_model(): |
|
"""Load the language detection model""" |
|
model_name = "papluca/xlm-roberta-base-language-detection" |
|
tokenizer = AutoTokenizer.from_pretrained(model_name) |
|
model = AutoModelForSequenceClassification.from_pretrained(model_name) |
|
return tokenizer, model |
|
|
|
@st.cache_resource |
|
def load_spam_model(): |
|
"""Load the fine-tuned BERT spam detection model""" |
|
model_path = "chjivan/final" |
|
tokenizer = BertTokenizerFast.from_pretrained(model_path) |
|
model = BertForSequenceClassification.from_pretrained(model_path) |
|
return tokenizer, model |
|
|
|
def detect_language(text, tokenizer, model): |
|
"""Detect the language of the input text""" |
|
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True) |
|
with torch.no_grad(): |
|
outputs = model(**inputs) |
|
|
|
logits = outputs.logits |
|
probabilities = torch.softmax(logits, dim=1)[0] |
|
|
|
predicted_class_id = torch.argmax(probabilities).item() |
|
predicted_language = model.config.id2label[predicted_class_id] |
|
confidence = probabilities[predicted_class_id].item() |
|
|
|
top_3_indices = torch.topk(probabilities, 3).indices.tolist() |
|
top_3_probs = torch.topk(probabilities, 3).values.tolist() |
|
top_3_langs = [(model.config.id2label[idx], prob) for idx, prob in zip(top_3_indices, top_3_probs)] |
|
|
|
return predicted_language, confidence, top_3_langs |
|
|
|
def classify_spam(text, tokenizer, model): |
|
"""Classify the input text as spam or ham""" |
|
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=128) |
|
with torch.no_grad(): |
|
outputs = model(**inputs) |
|
|
|
logits = outputs.logits |
|
probabilities = torch.softmax(logits, dim=1)[0] |
|
|
|
predicted_class_id = torch.argmax(probabilities).item() |
|
confidence = probabilities[predicted_class_id].item() |
|
|
|
is_spam = predicted_class_id == 1 |
|
return is_spam, confidence |
|
|
|
|
|
logo_data = create_spam_guard_logo() |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
@keyframes fadeIn { |
|
from { opacity: 0; transform: translateY(20px); } |
|
to { opacity: 1; transform: translateY(0); } |
|
} |
|
|
|
@keyframes pulse { |
|
0% { transform: scale(1); } |
|
50% { transform: scale(1.03); } |
|
100% { transform: scale(1); } |
|
} |
|
|
|
@keyframes slideInLeft { |
|
from { opacity: 0; transform: translateX(-30px); } |
|
to { opacity: 1; transform: translateX(0); } |
|
} |
|
|
|
@keyframes slideInRight { |
|
from { opacity: 0; transform: translateX(30px); } |
|
to { opacity: 1; transform: translateX(0); } |
|
} |
|
|
|
.animated-fadeIn { |
|
animation: fadeIn 0.8s ease-out forwards; |
|
} |
|
|
|
.animated-pulse-subtle { |
|
animation: pulse 2.5s infinite ease-in-out; |
|
} |
|
|
|
.animated-slideInLeft { |
|
animation: slideInLeft 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; |
|
} |
|
|
|
.animated-slideInRight { |
|
animation: slideInRight 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; |
|
} |
|
|
|
.neon-text { |
|
text-shadow: 0 0 3px rgba(0, 134, 209, 0.2), 0 0 6px rgba(0, 134, 209, 0.15); |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
with st.spinner("Loading models... This may take a moment."): |
|
lang_tokenizer, lang_model = load_language_model() |
|
spam_tokenizer, spam_model = load_spam_model() |
|
|
|
|
|
st.markdown(f""" |
|
<div style="display: flex; align-items: center; margin-bottom: 2.5rem; padding: 1.5rem; background: rgba(255,255,255,0.92); border-radius: 20px; box-shadow: 0 8px 24px rgba(0,0,0,0.12);" class="animated-fadeIn"> |
|
<img src="{logo_data}" style="height: 100px; margin-right: 30px; border-radius: 16px; box-shadow: 0 5px 15px rgba(0,0,0,0.15);" class="animated-pulse-subtle"> |
|
<div> |
|
<h1 class="main-header">SMS Spam Guard</h1> |
|
<p class="sub-header">Intelligent SMS Filtering Assistant by China Mobile Communications Group Co.,Ltd</p> |
|
</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
col1, col2 = st.columns([1, 2]) |
|
|
|
|
|
with col1: |
|
st.markdown(f""" |
|
<div class="glass-effect animated-slideInLeft" style="padding: 25px; border-radius: 12px; margin-bottom: 25px; animation-delay: 0.1s;"> |
|
<img src="{logo_data}" style="width: 60%; margin: 0 auto 20px auto; display: block; border-radius: 8px;"> |
|
<h3 style="color: var(--cm-blue); text-align: center; margin-bottom:15px;">About Us</h3> |
|
<p style="font-size:0.9rem; color: #444;">China Mobile Communications Group Co.,Ltd provides intelligent communication security solutions to protect users from spam and fraudulent messages.</p> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
<div class="glass-effect animated-slideInLeft" style="padding: 25px; border-radius: 12px; margin-bottom: 25px; animation-delay: 0.2s;"> |
|
<h4 style="color: var(--cm-blue); margin-bottom:15px;">Our Technology</h4> |
|
<ul style="padding-left: 20px; font-size:0.9rem; color: #444;"> |
|
<li style="margin-bottom:8px;">✅ Advanced AI-powered spam detection</li> |
|
<li style="margin-bottom:8px;">🌐 Multi-language support</li> |
|
<li style="margin-bottom:8px;">🔒 Secure and private processing</li> |
|
<li>⚡ Real-time analysis</li> |
|
</ul> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
st.markdown("<h4 style='color: var(--cm-blue); margin-left: 5px; margin-bottom:10px;' class='animated-slideInLeft' data-animation-delay='0.3s'>Sample Messages</h4>", unsafe_allow_html=True) |
|
|
|
|
|
button_style = "margin-bottom: 8px; width: 100%;" |
|
if st.button("Sample Spam (English)", key="spam_btn_en", help="Load a sample English spam message", type="secondary"): |
|
st.session_state.sms_input = "URGENT: You have won a $1,000 Walmart gift card. Go to http://bit.ly/claim-prize to claim now before it expires!" |
|
|
|
if st.button("Sample Legitimate (English)", key="ham_btn_en", help="Load a sample English legitimate message", type="secondary"): |
|
st.session_state.sms_input = "Your Amazon package will be delivered today. Thanks for ordering from Amazon!" |
|
|
|
if st.button("Sample Message (French)", key="french_btn_fr", help="Load a sample French message", type="secondary"): |
|
st.session_state.sms_input = "Bonjour! Votre réservation pour le restaurant est confirmée pour ce soir à 20h. À bientôt!" |
|
|
|
if st.button("Sample Message (Spanish)", key="spanish_btn_es", help="Load a sample Spanish message", type="secondary"): |
|
st.session_state.sms_input = "Hola, tu cita médica está programada para mañana a las 10:00. Por favor llega 15 minutos antes." |
|
|
|
|
|
with col2: |
|
st.markdown(""" |
|
<div class="glass-effect animated-fadeIn" style="padding: 30px; border-radius: 16px; margin-bottom: 30px; animation-delay: 0.1s;"> |
|
<h3 style="color: var(--cm-blue); margin-bottom: 20px; text-align:center;">Analyze Your Message</h3> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
sms_input = st.text_area( |
|
"", |
|
value=st.session_state.get("sms_input", ""), |
|
height=120, |
|
key="sms_input", |
|
placeholder="Enter the SMS message you want to analyze here...", |
|
help="Paste or type the SMS message to check if it's spam or legitimate." |
|
) |
|
|
|
analyze_button = st.button("📱 Analyze Message", use_container_width=True, key="analyze_btn", type="primary") |
|
|
|
if analyze_button and sms_input: |
|
with st.spinner(""): |
|
st.markdown(""" |
|
<div class="loading-animation-container animated-fadeIn" style="text-align: center; color: var(--cm-blue); margin: 25px 0;"> |
|
<svg width="60" height="60" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|
<path d="M12 2C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2V2Z" stroke="var(--cm-blue)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /> |
|
<path d="M12 2C6.47715 2 2 6.47715 2 12H2Z" stroke="var(--cm-light-blue)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /> |
|
</svg> |
|
<p style="font-size: 1.1rem; margin-top: 10px;">Analyzing your message...</p> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
time.sleep(0.5) |
|
|
|
lang_start_time = time.time() |
|
lang_code, lang_confidence, top_langs = detect_language(sms_input, lang_tokenizer, lang_model) |
|
lang_time = time.time() - lang_start_time |
|
|
|
lang_names = { |
|
"ar": "Arabic", "bg": "Bulgarian", "de": "German", "el": "Greek", "en": "English", |
|
"es": "Spanish", "fr": "French", "hi": "Hindi", "it": "Italian", "ja": "Japanese", |
|
"nl": "Dutch", "pl": "Polish", "pt": "Portuguese", "ru": "Russian", "sw": "Swahili", |
|
"th": "Thai", "tr": "Turkish", "ur": "Urdu", "vi": "Vietnamese", "zh": "Chinese" |
|
} |
|
lang_name = lang_names.get(lang_code, lang_code.capitalize()) |
|
|
|
spam_start_time = time.time() |
|
is_spam, spam_confidence = classify_spam(sms_input, spam_tokenizer, spam_model) |
|
spam_time = time.time() - spam_start_time |
|
|
|
st.markdown("<h3 style='color: var(--cm-blue); margin-top: 30px; text-align:center;' class='animated-fadeIn'>Analysis Results</h3>", unsafe_allow_html=True) |
|
|
|
res_col1, res_col2 = st.columns(2) |
|
|
|
with res_col1: |
|
st.markdown(f""" |
|
<div class="result-card animated-slideInLeft" style="animation-delay: 0.2s; background: linear-gradient(135deg, #e6f7ff 0%, #f0faff 100%);"> |
|
<h4 class="result-title">📊 Language Detection</h4> |
|
<div class="result-content"> |
|
<div style="display: flex; align-items: center; margin-bottom: 15px;"> |
|
<span class="language-tag">{lang_name}</span> |
|
<span>Detected with <span class="result-value">{lang_confidence:.1%}</span> confidence</span> |
|
</div> |
|
<h5 style="color: var(--cm-dark-blue); margin-bottom: 10px; font-size:0.95rem;">Top language probabilities:</h5> |
|
<ul style="font-size:0.9rem; padding-left:18px;"> |
|
""", unsafe_allow_html=True) |
|
for l_code, l_prob in top_langs: |
|
st.markdown(f"<li>{lang_names.get(l_code, l_code.capitalize())}: {l_prob:.1%}</li>", unsafe_allow_html=True) |
|
st.markdown(f""" |
|
</ul> |
|
<p style="margin-top: 15px; font-size:0.85rem; color:#555;">⏱️ Processing time: {lang_time:.3f} seconds</p> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
with res_col2: |
|
result_confidence = spam_confidence if is_spam else (1 - spam_confidence) |
|
if is_spam: |
|
st.markdown(f""" |
|
<div class="result-card spam-alert animated-slideInRight" style="animation-delay: 0.3s;"> |
|
<h4 class="result-title">🔍 Spam Detection</h4> |
|
<div class="result-content"> |
|
<div style="font-size: 1.1rem; font-weight: bold; color: #b91c1c; margin-bottom: 10px;">⚠️ SPAM DETECTED</div> |
|
<p style="margin-bottom:10px;">Confidence: <span class="result-value">{result_confidence:.1%}</span></p> |
|
<p style="font-size:0.9rem;">This message shows strong indicators of being spam.</p> |
|
<p style="margin-top: 15px; font-size:0.85rem; color:#555;">⏱️ Processing time: {spam_time:.3f} seconds</p> |
|
</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
else: |
|
st.markdown(f""" |
|
<div class="result-card ham-alert animated-slideInRight" style="animation-delay: 0.3s;"> |
|
<h4 class="result-title">🔍 Spam Detection</h4> |
|
<div class="result-content"> |
|
<div style="font-size: 1.1rem; font-weight: bold; color: #047857; margin-bottom: 10px;">✅ LEGITIMATE MESSAGE</div> |
|
<p style="margin-bottom:10px;">Confidence: <span class="result-value">{result_confidence:.1%}</span></p> |
|
<p style="font-size:0.9rem;">This message appears to be legitimate.</p> |
|
<p style="margin-top: 15px; font-size:0.85rem; color:#555;">⏱️ Processing time: {spam_time:.3f} seconds</p> |
|
</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
<div class="glass-effect animated-fadeIn" style="padding: 25px; border-radius: 12px; margin: 30px 0; animation-delay: 0.4s;"> |
|
<h3 style="color: var(--cm-blue); margin-bottom: 15px; text-align:center;">📋 Summary & Recommendations</h3> |
|
""", unsafe_allow_html=True) |
|
if is_spam: |
|
st.warning("📵 **Recommended Action**: This message should be treated with caution, blocked, or moved to the spam folder.") |
|
st.markdown(""" |
|
**Potential reasons for spam classification:** |
|
<ul style="font-size:0.9rem;"> |
|
<li>Contains suspicious language patterns or urgency.</li> |
|
<li>May include unsolicited offers or links to untrusted sites.</li> |
|
<li>Resembles known spam message structures.</li> |
|
</ul> |
|
""", unsafe_allow_html=True) |
|
else: |
|
st.success("✅ **Recommended Action**: This message seems safe and can be delivered to the inbox.") |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
<div class="glass-effect animated-fadeIn" style="padding: 25px; border-radius: 12px; margin: 30px 0; animation-delay: 0.5s;"> |
|
<h3 style="color: var(--cm-blue); margin-bottom: 20px; text-align:center;">📈 Confidence Visualization</h3> |
|
""", unsafe_allow_html=True) |
|
chart_data = pd.DataFrame({ |
|
'Task': ['Language Detection Confidence', 'Spam Classification Certainty'], |
|
'Confidence': [lang_confidence, result_confidence] |
|
}) |
|
st.bar_chart(chart_data.set_index('Task'), height=300, use_container_width=True) |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(f""" |
|
<div class="footer animated-fadeIn" style="animation-delay: 0.8s;"> |
|
<div style="display: flex; justify-content: center; align-items: center; margin-bottom: 10px;"> |
|
<img src="{logo_data}" style="height: 25px; margin-right: 12px; border-radius:4px;"> |
|
<span>© {time.strftime('%Y')} China Mobile Communications Group Co.,Ltd | <a href="http://www.chinamobile.com" target="_blank" style="color: var(--cm-blue); text-decoration:none;">www.chinamobile.com</a></span> |
|
</div> |
|
<p style="font-size:0.8rem; color:#888;">SMS Spam Guard: Your intelligent shield against unwanted communications.</p> |
|
</div> |
|
""", unsafe_allow_html=True) |