AishaniS commited on
Commit
9d2a066
·
verified ·
1 Parent(s): ce1e633

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +159 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,161 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import smtplib
5
+ import re
6
+ import os
7
+ from dotenv import load_dotenv # <--- Import this
8
+ from email.mime.multipart import MIMEMultipart
9
+ from email.mime.text import MIMEText
10
+ from email.mime.base import MIMEBase
11
+ from email import encoders
12
+
13
+ # --- Configuration ---
14
+ load_dotenv() # <--- This loads the variables from .env
15
+
16
+ # Now we read the secrets using os.getenv
17
+ SENDER_EMAIL = os.getenv("SENDER_EMAIL")
18
+ SENDER_PASSWORD = os.getenv("SENDER_PASSWORD")
19
+
20
+ if not SENDER_EMAIL or not SENDER_PASSWORD:
21
+ st.error("Error: Email credentials not found. Please check your .env file.")
22
+ st.stop()
23
+
24
+ # --- Helper Functions ---
25
+
26
+ def validate_email(email):
27
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
28
+ return re.match(pattern, email)
29
+
30
+ def send_email(receiver_email, result_df, filename="result.csv"):
31
+ try:
32
+ msg = MIMEMultipart()
33
+ msg['From'] = SENDER_EMAIL
34
+ msg['To'] = receiver_email
35
+ msg['Subject'] = "Your TOPSIS Result is Ready"
36
+
37
+ body = "Hello,\n\nPlease find the attached result file for your TOPSIS calculation.\n\nBest,\nTOPSIS Web Service"
38
+ msg.attach(MIMEText(body, 'plain'))
39
+
40
+ # Convert DataFrame to CSV string
41
+ csv_data = result_df.to_csv(index=False)
42
+
43
+ # Attachment
44
+ part = MIMEBase('application', 'octet-stream')
45
+ part.set_payload(csv_data)
46
+ encoders.encode_base64(part)
47
+ part.add_header('Content-Disposition', f"attachment; filename= {filename}")
48
+ msg.attach(part)
49
+
50
+ # SMTP Server Setup (For Gmail)
51
+ server = smtplib.SMTP('smtp.gmail.com', 587)
52
+ server.starttls()
53
+ server.login(SENDER_EMAIL, SENDER_PASSWORD)
54
+ text = msg.as_string()
55
+ server.sendmail(SENDER_EMAIL, receiver_email, text)
56
+ server.quit()
57
+ return True, "Email sent successfully!"
58
+ except Exception as e:
59
+ return False, str(e)
60
+
61
+ def calculate_topsis(df, weights, impacts):
62
+ # This logic is adapted from your CLI package
63
+ try:
64
+ # Preprocessing: Drop first column (Object Names) and convert to float
65
+ data = df.iloc[:, 1:].values.astype(float)
66
+
67
+ # Normalization
68
+ rss = np.sqrt(np.sum(data**2, axis=0))
69
+ normalized_data = data / rss
70
+
71
+ # Weighting
72
+ weighted_data = normalized_data * weights
73
+
74
+ # Ideal Best and Worst
75
+ ideal_best = []
76
+ ideal_worst = []
77
+
78
+ for i in range(len(impacts)):
79
+ if impacts[i] == '+':
80
+ ideal_best.append(np.max(weighted_data[:, i]))
81
+ ideal_worst.append(np.min(weighted_data[:, i]))
82
+ else:
83
+ ideal_best.append(np.min(weighted_data[:, i]))
84
+ ideal_worst.append(np.max(weighted_data[:, i]))
85
+
86
+ # Euclidean Distance
87
+ s_best = np.sqrt(np.sum((weighted_data - ideal_best)**2, axis=1))
88
+ s_worst = np.sqrt(np.sum((weighted_data - ideal_worst)**2, axis=1))
89
+
90
+ # Topsis Score
91
+ topsis_score = s_worst / (s_best + s_worst)
92
+
93
+ # Add to DF
94
+ df['Topsis Score'] = topsis_score
95
+ df['Rank'] = df['Topsis Score'].rank(ascending=False).astype(int)
96
+
97
+ return df
98
+
99
+ except Exception as e:
100
+ st.error(f"Error in calculation: {e}")
101
+ return None
102
+
103
+ # --- Main App UI ---
104
+
105
+ st.title("Topsis Web Service")
106
+ st.write("Upload your data, define weights/impacts, and get results via email.")
107
+
108
+ # 1. Inputs
109
+ email_id = st.text_input("Email ID (to receive results)", placeholder="name@example.com")
110
+ uploaded_file = st.file_uploader("Upload Input File (CSV)", type=["csv"])
111
+ weights_input = st.text_input("Weights (comma-separated)", placeholder="1,1,1,2")
112
+ impacts_input = st.text_input("Impacts (comma-separated, + or -)", placeholder="+,+,-,+")
113
+
114
+ # 2. Submit Button
115
+ if st.button("Submit"):
116
+ # --- Validations ---
117
+ if not uploaded_file:
118
+ st.error("Please upload a CSV file.")
119
+ elif not email_id or not validate_email(email_id):
120
+ st.error("Please enter a valid email address.")
121
+ elif not weights_input:
122
+ st.error("Please enter weights.")
123
+ elif not impacts_input:
124
+ st.error("Please enter impacts.")
125
+ else:
126
+ # Process Inputs
127
+ try:
128
+ df = pd.read_csv(uploaded_file)
129
+
130
+ # Parse Weights
131
+ weights = [float(w) for w in weights_input.split(',')]
132
+
133
+ # Parse Impacts
134
+ impacts = impacts_input.split(',')
135
+
136
+ # Validation: Column Count
137
+ num_cols = df.shape[1] - 1 # Exclude first column
138
+ if len(weights) != num_cols or len(impacts) != num_cols:
139
+ st.error(f"Count Mismatch! Input file has {num_cols} numerical columns, but you provided {len(weights)} weights and {len(impacts)} impacts.")
140
+ elif not all(i in ['+', '-'] for i in impacts):
141
+ st.error("Impacts must be either '+' or '-'.")
142
+ else:
143
+ # --- Run Logic ---
144
+ st.info("Calculating TOPSIS Score...")
145
+ result_df = calculate_topsis(df, weights, impacts)
146
+
147
+ if result_df is not None:
148
+ # --- Send Email ---
149
+ st.info(f"Sending result to {email_id}...")
150
+ success, message = send_email(email_id, result_df)
151
+
152
+ if success:
153
+ st.success("Success! Result file has been sent to your email.")
154
+ st.dataframe(result_df) # Show preview
155
+ else:
156
+ st.error(f"Failed to send email: {message}")
157
 
158
+ except ValueError:
159
+ st.error("Weights must be numeric values separated by commas.")
160
+ except Exception as e:
161
+ st.error(f"An unexpected error occurred: {e}")