Topsis_Web_Service / src /streamlit_app.py
AishaniS's picture
Update src/streamlit_app.py
c752748 verified
Raw History Blame Contribute Delete
5.91 kB
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}")