File size: 5,912 Bytes
7c50e6c
9d2a066
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c752748
 
 
9d2a066
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7c50e6c
9d2a066
 
 
 
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
import streamlit as st
import pandas as pd
import numpy as np
import smtplib
import re
import os
from dotenv import load_dotenv  # <--- Import this
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

# --- Configuration ---
load_dotenv()  # <--- This loads the variables from .env

# Now we read the secrets using os.getenv
SENDER_EMAIL = os.getenv("SENDER_EMAIL")
SENDER_PASSWORD = os.getenv("SENDER_PASSWORD")

if not SENDER_EMAIL or not SENDER_PASSWORD:
    st.error("Error: Email credentials not found. Please check your .env file.")
    st.stop()

# --- Helper Functions ---

def validate_email(email):
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email)

def send_email(receiver_email, result_df, filename="result.csv"):
    try:
        msg = MIMEMultipart()
        msg['From'] = SENDER_EMAIL
        msg['To'] = receiver_email
        msg['Subject'] = "Your TOPSIS Result is Ready"

        body = "Hello,\n\nPlease find the attached result file for your TOPSIS calculation.\n\nBest,\nTOPSIS Web Service"
        msg.attach(MIMEText(body, 'plain'))

        # Convert DataFrame to CSV string
        csv_data = result_df.to_csv(index=False)
        
        # Attachment
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(csv_data)
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', f"attachment; filename= {filename}")
        msg.attach(part)

       # SMTP Server Setup (For Gmail) - USING SSL PORT 465
        # Note: SMTP_SSL does not need server.starttls()
        server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
        server.login(SENDER_EMAIL, SENDER_PASSWORD)
        text = msg.as_string()
        server.sendmail(SENDER_EMAIL, receiver_email, text)
        server.quit()
        return True, "Email sent successfully!"
    except Exception as e:
        return False, str(e)

def calculate_topsis(df, weights, impacts):
    # This logic is adapted from your CLI package
    try:
        # Preprocessing: Drop first column (Object Names) and convert to float
        data = df.iloc[:, 1:].values.astype(float)
        
        # Normalization
        rss = np.sqrt(np.sum(data**2, axis=0))
        normalized_data = data / rss

        # Weighting
        weighted_data = normalized_data * weights

        # Ideal Best and Worst
        ideal_best = []
        ideal_worst = []

        for i in range(len(impacts)):
            if impacts[i] == '+':
                ideal_best.append(np.max(weighted_data[:, i]))
                ideal_worst.append(np.min(weighted_data[:, i]))
            else:
                ideal_best.append(np.min(weighted_data[:, i]))
                ideal_worst.append(np.max(weighted_data[:, i]))

        # Euclidean Distance
        s_best = np.sqrt(np.sum((weighted_data - ideal_best)**2, axis=1))
        s_worst = np.sqrt(np.sum((weighted_data - ideal_worst)**2, axis=1))

        # Topsis Score
        topsis_score = s_worst / (s_best + s_worst)
        
        # Add to DF
        df['Topsis Score'] = topsis_score
        df['Rank'] = df['Topsis Score'].rank(ascending=False).astype(int)
        
        return df

    except Exception as e:
        st.error(f"Error in calculation: {e}")
        return None

# --- Main App UI ---

st.title("Topsis Web Service")
st.write("Upload your data, define weights/impacts, and get results via email.")

# 1. Inputs
email_id = st.text_input("Email ID (to receive results)", placeholder="name@example.com")
uploaded_file = st.file_uploader("Upload Input File (CSV)", type=["csv"])
weights_input = st.text_input("Weights (comma-separated)", placeholder="1,1,1,2")
impacts_input = st.text_input("Impacts (comma-separated, + or -)", placeholder="+,+,-,+")

# 2. Submit Button
if st.button("Submit"):
    # --- Validations ---
    if not uploaded_file:
        st.error("Please upload a CSV file.")
    elif not email_id or not validate_email(email_id):
        st.error("Please enter a valid email address.")
    elif not weights_input:
        st.error("Please enter weights.")
    elif not impacts_input:
        st.error("Please enter impacts.")
    else:
        # Process Inputs
        try:
            df = pd.read_csv(uploaded_file)
            
            # Parse Weights
            weights = [float(w) for w in weights_input.split(',')]
            
            # Parse Impacts
            impacts = impacts_input.split(',')
            
            # Validation: Column Count
            num_cols = df.shape[1] - 1  # Exclude first column
            if len(weights) != num_cols or len(impacts) != num_cols:
                st.error(f"Count Mismatch! Input file has {num_cols} numerical columns, but you provided {len(weights)} weights and {len(impacts)} impacts.")
            elif not all(i in ['+', '-'] for i in impacts):
                st.error("Impacts must be either '+' or '-'.")
            else:
                # --- Run Logic ---
                st.info("Calculating TOPSIS Score...")
                result_df = calculate_topsis(df, weights, impacts)
                
                if result_df is not None:
                    # --- Send Email ---
                    st.info(f"Sending result to {email_id}...")
                    success, message = send_email(email_id, result_df)
                    
                    if success:
                        st.success("Success! Result file has been sent to your email.")
                        st.dataframe(result_df) # Show preview
                    else:
                        st.error(f"Failed to send email: {message}")

        except ValueError:
            st.error("Weights must be numeric values separated by commas.")
        except Exception as e:
            st.error(f"An unexpected error occurred: {e}")