File size: 10,423 Bytes
f9903ea
9739fd6
 
 
f9903ea
95fe581
7c483c8
40b7a7a
c70917b
9739fd6
 
f9903ea
40b7a7a
7c483c8
40b7a7a
7c483c8
 
 
40b7a7a
48034cd
7c483c8
40b7a7a
 
 
 
 
 
7c483c8
 
 
 
40b7a7a
48034cd
 
7c483c8
40b7a7a
48034cd
 
7a0022c
7c483c8
95fe581
7c483c8
40b7a7a
7d63c56
40b7a7a
7d63c56
f9903ea
40b7a7a
bfc5779
40b7a7a
7c483c8
40b7a7a
48034cd
40b7a7a
48034cd
40b7a7a
48034cd
 
 
 
 
 
 
7c483c8
40b7a7a
345319f
7c483c8
40b7a7a
7c483c8
 
 
 
 
 
 
 
 
345319f
f9903ea
7c483c8
7f5130a
 
 
7d63c56
 
7c483c8
7f5130a
bfc5779
7c483c8
 
 
 
 
 
 
345319f
bfc5779
95fe581
6f7d2fb
 
40b7a7a
1138fa8
48034cd
 
 
 
6f7d2fb
 
 
40b7a7a
7a0022c
1138fa8
 
48034cd
 
6f7d2fb
 
7a0022c
40b7a7a
7a0022c
 
6f7d2fb
7a0022c
1138fa8
48034cd
7a0022c
 
1138fa8
 
95fe581
 
48034cd
 
7a0022c
40b7a7a
7a0022c
 
48034cd
7a0022c
1138fa8
48034cd
40b7a7a
48034cd
40b7a7a
 
48034cd
 
40b7a7a
48034cd
 
40b7a7a
 
48034cd
 
40b7a7a
48034cd
 
40b7a7a
 
48034cd
 
40b7a7a
6f7d2fb
 
 
 
40b7a7a
305441b
 
40b7a7a
bfc5779
 
40b7a7a
345319f
bfc5779
6f7d2fb
48034cd
b4fad3e
bfc5779
40b7a7a
345319f
bfc5779
345319f
48034cd
bfc5779
 
40b7a7a
345319f
bfc5779
345319f
48034cd
bfc5779
 
40b7a7a
305441b
48034cd
1138fa8
7a0022c
48034cd
40b7a7a
48034cd
 
 
 
40b7a7a
48034cd
fbd79d5
 
40b7a7a
fbd79d5
 
 
 
 
 
40b7a7a
fbd79d5
 
 
 
 
 
 
 
40b7a7a
fbd79d5
95fe581
fbd79d5
7a0022c
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
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
    st.markdown("""
        <style>
        /* Style for sample buttons (smaller, light gray) */
        div.stButton > button[kind="secondary"]:not([key="clear"]) {
            font-size: 12px;
            padding: 5px 10px;
            background-color: #f0f0f0;
            color: #333333;
            border: 1px solid #cccccc;
            border-radius: 3px;
        }
        /* Analyze Email button (orange, matches Clear button size) */
        div.stButton > button[key="analyze"] {
            background-color: #FF5733 !important; /* Force orange color */
            color: white !important;
            font-size: 18px;
            padding: 12px 24px;
            border: none;
            border-radius: 5px;
            width: 100%;
            height: 50px;
            box-sizing: border-box;
            text-align: center;
        }
        div.stButton > button[key="analyze"]:hover {
            background-color: #E74C3C !important; /* Darker orange on hover */
        }
        /* Clear button (gray, aligned with Analyze) */
        div.stButton > button[key="clear"] {
            background-color: #d3d3d3 !important; /* Force gray color */
            color: #333333 !important;
            font-size: 18px;
            padding: 12px 24px;
            border: none;
            border-radius: 5px;
            width: 100%;
            height: 50px;
            box-sizing: border-box;
            text-align: center;
        }
        div.stButton > button[key="clear"]:hover {
            background-color: #b0b0b0 !important; /* Darker gray on hover */
        }
        /* Result boxes with updated colors */
        .spam-result {
            background-color: #ff3333; /* Red for no follow-up (spam) */
            color: white;
            padding: 10px;
            border-radius: 5px;
            border: 1px solid #cc0000;
        }
        .positive-result {
            background-color: #ff3333; /* Red for no follow-up (positive) */
            color: white;
            padding: 10px;
            border-radius: 5px;
            border: 1px solid #cc0000;
        }
        .negative-result {
            background-color: #006633; /* Dark green for follow-up needed */
            color: white;
            padding: 10px;
            border-radius: 5px;
            border: 1px solid #004d26;
        }
        </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()