Topsis_Web_Service / src /streamlit_app.py
AishaniS's picture
Update src/streamlit_app.py
c752748 verified
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}")