EmailSentry / app.py
ISOM5240GP4's picture
Update app.py
7817e88 verified
raw
history blame
11.1 kB
import streamlit as st
from transformers import pipeline, AutoModelForSequenceClassification, AutoTokenizer
import torch
import numpy as np
# Function to analyze email for spam and sentiment
def analyze_email(email_body):
# Load pre-trained models: spam detection and sentiment analysis
spam_pipeline = pipeline("text-classification", model="cybersectony/phishing-email-detection-distilbert_v2.4.1")
sentiment_model = AutoModelForSequenceClassification.from_pretrained("ISOM5240GP4/email_sentiment", num_labels=2)
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
# Step 1: Check if the email is spam using the spam detection model
spam_result = spam_pipeline(email_body)
spam_label = spam_result[0]["label"] # LABEL_1 indicates spam
spam_confidence = spam_result[0]["score"]
if spam_label == "LABEL_1":
# If spam, return type "spam" and a message indicating no follow-up
return "spam", f"This is a spam email (Confidence: {spam_confidence:.2f}). No follow-up needed."
else:
# Step 2: For non-spam emails, analyze sentiment (positive/negative)
inputs = tokenizer(email_body, padding=True, truncation=True, return_tensors='pt') # Tokenize input
outputs = sentiment_model(**inputs) # Get model predictions
predictions = torch.nn.functional.softmax(outputs.logits, dim=-1) # Apply softmax for probabilities
predictions = predictions.cpu().detach().numpy() # Convert to numpy array
sentiment_index = np.argmax(predictions) # Get the predicted sentiment (0 = negative, 1 = positive)
sentiment_confidence = predictions[0][sentiment_index]
sentiment = "Positive" if sentiment_index == 1 else "Negative"
if sentiment == "Positive":
# If positive sentiment, no follow-up needed
return "positive", (f"This email is not spam (Confidence: {spam_confidence:.2f}).\n"
f"Sentiment: {sentiment} (Confidence: {sentiment_confidence:.2f}). No follow-up needed.")
else:
# If negative sentiment, mark as needing follow-up with bolded text
return "negative", (f"This email is not spam (Confidence: {spam_confidence:.2f}).\n"
f"Sentiment: {sentiment} (Confidence: {sentiment_confidence:.2f}).\n"
"<b>Need to Follow-Up</b>: This email is not spam and has negative sentiment.")
# Main application function
def main():
# Set the app title to the project name
st.title("EmailSentry")
# Display the project objective
st.write("Aims to perform analysis on incoming emails and to determine whether there is urgency or higher priority for the company to follow-up.")
# Initialize session state to store email input and analysis results
if "email_body" not in st.session_state:
st.session_state.email_body = "" # Holds the email text
if "result" not in st.session_state:
st.session_state.result = "" # Stores the analysis result text
if "result_type" not in st.session_state:
st.session_state.result_type = "" # Tracks result type (spam, positive, negative)
# Add collapsible instructions for user guidance
with st.expander("How to Use", expanded=False):
st.write("""
- Type or paste an email into the text box.
- Alternatively, click one of the sample buttons to load a predefined email.
- Press 'Analyze Email' to check if it’s spam and analyze its sentiment.
- Use 'Clear' to reset the input and result.
""")
# Text area where users input or view the email body
email_body = st.text_area("Email Body", value=st.session_state.email_body, height=200, key="email_input")
# Define sample emails and their snippets for example buttons
sample_spam = """
Subject: Urgent: Verify Your Account Now!
Dear Customer,
We have detected unusual activity on your account. To prevent suspension, please verify your login details immediately by clicking the link below:
[Click Here to Verify](http://totally-legit-site.com/verify)
Failure to verify within 24 hours will result in your account being locked. This is for your security.
Best regards,
The Security Team
"""
spam_snippet = "Subject: Urgent: Verify Your Account Now! Dear Customer, We have detected unusual activity..."
sample_not_spam_positive = """
Subject: Great Experience with HKTV mall
Dear Sir,
I just received my order and I’m really impressed with the speed of the delivery. Keep up the good work.
Best regards,
Emily
"""
positive_snippet = "Subject: Great Experience with HKTV mall Dear Sir, I just received my order and I’m really..."
sample_not_spam_negative = """
Subject: Issue with Recent Delivery
Dear Support,
I received my package today, but it was damaged, and two items were missing. This is really frustrating—please let me know how we can resolve this as soon as possible.
Thanks,
Sarah
"""
negative_snippet = "Subject: Issue with Recent Delivery Dear Support, I received my package today, but..."
# Custom CSS for styling buttons and result boxes with higher specificity
# Replace your existing st.markdown CSS block with this
st.markdown("""
<style>
/* Ensure high specificity for Analyze Email button (orange) */
div[data-testid="stButton"] > button[key="analyze"] {
background-color: #FF5733 !important; /* Orange */
color: white !important;
font-size: 18px !important;
padding: 12px 24px !important;
border: none !important;
border-radius: 5px !important;
width: 100% !important;
height: 50px !important;
box-sizing: border-box !important;
text-align: center !important;
margin: 0 !important;
}
div[data-testid="stButton"] > button[key="analyze"]:hover {
background-color: #E74C3C !important; /* Darker orange on hover */
}
/* Ensure high specificity for Clear button (blue) */
div[data-testid="stButton"] > button[key="clear"] {
background-color: #007BFF !important; /* Blue */
color: white !important;
font-size: 18px !important;
padding: 12px 24px !important;
border: none !important;
border-radius: 5px !important;
width: 100% !important;
height: 50px !important;
box-sizing: border-box !important;
text-align: center !important;
margin: 0 !important;
}
div[data-testid="stButton"] > button[key="clear"]:hover {
background-color: #0056b3 !important; /* Darker blue on hover */
}
/* Style for sample buttons (light gray) */
div[data-testid="stButton"] > button[kind="secondary"]:not([key="clear"]):not([key="analyze"]) {
font-size: 12px !important;
padding: 5px 10px !important;
background-color: #f0f0f0 !important;
color: #333333 !important;
border: 1px solid #cccccc !important;
border-radius: 3px !important;
}
/* Result boxes (unchanged) */
.spam-result {
background-color: #ff3333 !important; /* Red for no follow-up (spam) */
color: white !important;
padding: 10px !important;
border-radius: 5px !important;
border: 1px solid #cc0000 !important;
}
.positive-result {
background-color: #ff3333 !important; /* Red for no follow-up (positive) */
color: white !important;
padding: 10px !important;
border-radius: 5px !important;
border: 1px solid #cc0000 !important;
}
.negative-result {
background-color: #006633 !important; /* Dark green for follow-up needed */
color: white !important;
padding: 10px !important;
border-radius: 5px !important;
border: 1px solid #004d26 !important;
}
</style>
""", unsafe_allow_html=True)
# Subheading to label the sample email buttons
st.subheader("Examples")
# Layout for sample buttons in 3 columns
col1, col2, col3 = st.columns(3)
with col1:
# Button to load spam sample
if st.button(spam_snippet, key="spam_sample"):
st.session_state.email_body = sample_spam
st.session_state.result = ""
st.session_state.result_type = ""
st.rerun()
with col2:
# Button to load positive non-spam sample
if st.button(positive_snippet, key="positive_sample"):
st.session_state.email_body = sample_not_spam_positive
st.session_state.result = ""
st.session_state.result_type = ""
st.rerun()
with col3:
# Button to load negative non-spam sample
if st.button(negative_snippet, key="negative_sample"):
st.session_state.email_body = sample_not_spam_negative
st.session_state.result = ""
st.session_state.result_type = ""
st.rerun()
# Layout for action buttons (Analyze and Clear) in 2 columns
col_analyze, col_clear = st.columns(2)
with col_analyze:
# Button to trigger email analysis (no type="primary" to rely on CSS)
if st.button("Analyze Email", key="analyze"):
if email_body:
with st.spinner("Analyzing email..."): # Show spinner during processing
result_type, result = analyze_email(email_body)
st.session_state.result = result
st.session_state.result_type = result_type
else:
# Error message if no email is provided
st.session_state.result = "Please enter an email body or select a sample to analyze."
st.session_state.result_type = ""
with col_clear:
# Button to reset the app state
if st.button("Clear", key="clear"):
st.session_state.email_body = ""
st.session_state.result = ""
st.session_state.result_type = ""
st.rerun()
# Display the analysis result in styled boxes based on result type
if st.session_state.result:
if st.session_state.result_type == "spam":
st.markdown(f'<div class="spam-result">{st.session_state.result}</div>', unsafe_allow_html=True)
elif st.session_state.result_type == "positive":
st.markdown(f'<div class="positive-result">{st.session_state.result}</div>', unsafe_allow_html=True)
elif st.session_state.result_type == "negative":
st.markdown(f'<div class="negative-result">{st.session_state.result}</div>', unsafe_allow_html=True)
else:
st.write(st.session_state.result) # Display error messages without styling
# Run the app
if __name__ == "__main__":
main()