mubashirhussaindev commited on
Commit
ed4c051
·
verified ·
1 Parent(s): d8cc5ca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -90
app.py CHANGED
@@ -14,57 +14,31 @@ from urllib.parse import urlencode
14
  logging.basicConfig(filename='appointment_bot.log', level=logging.INFO,
15
  format='%(asctime)s - %(levelname)s - %(message)s')
16
 
17
- # === Email Configuration ===
18
- SMTP_SERVER = "mail.mubashirdev.com"
19
- SMTP_PORT = 465
20
- SMTP_USERNAME = os.getenv("SMTP_USERNAME")
21
- SMTP_PASSWORD = os.getenv("SMTP_PASSWORD")
22
- if not SMTP_USERNAME or not SMTP_PASSWORD:
23
- raise Exception("SMTP_USERNAME and SMTP_PASSWORD environment variables are not set.")
24
-
25
- FROM_EMAIL = SMTP_USERNAME
26
- FROM_NAME = "Mubashir Hussain"
27
-
28
- # === Excel File ===
29
  EXCEL_FILE = "appointments.xlsx"
30
-
31
- # === Time Slots ===
32
  TIME_SLOTS = [f"{hour:02d}:00" for hour in range(9, 18)] # 9 AM to 5 PM
33
-
34
- # === Date Options (Next 15 Days for Speed) ===
35
  DATE_OPTIONS = [
36
  (datetime.now() + timedelta(days=i)).strftime("%Y-%m-%d (%A)")
37
  for i in range(0, 15)
38
  ]
39
-
40
- # === Common Timezones (minimized for speed) ===
41
  COMMON_TIMEZONES = [
42
  "UTC", "Asia/Karachi", "US/Pacific", "US/Eastern",
43
  "Europe/London", "Asia/Tokyo", "America/New_York", "Asia/Dubai"
44
  ]
45
 
46
- # === Google Calendar Link Generator ===
47
- def generate_google_calendar_link(name, email, date_str, time_str, timezone):
48
- try:
49
- date_clean = date_str.split(" ")[0] # Extract YYYY-MM-DD
50
- tz = pytz.timezone(timezone)
51
- start_time = datetime.strptime(f"{date_clean} {time_str}", "%Y-%m-%d %H:%M").replace(tzinfo=tz)
52
- end_time = start_time + timedelta(hours=1)
53
- start_time_utc = start_time.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
54
- end_time_utc = end_time.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
55
- event = {
56
- "action": "TEMPLATE",
57
- "text": f"Appointment with {FROM_NAME}",
58
- "details": f"Appointment for {name} ({email}). Contact: https://mubashirdev.com",
59
- "dates": f"{start_time_utc}/{end_time_utc}",
60
- "ctz": timezone
61
- }
62
- return f"https://www.google.com/calendar/render?{urlencode(event)}"
63
- except Exception as e:
64
- logging.error(f"Google Calendar link error: {e}")
65
- raise Exception("Failed to generate calendar link.")
66
 
67
- # === Email Utility ===
68
  def send_email(to_email, subject, body):
69
  try:
70
  msg = MIMEMultipart()
@@ -73,58 +47,88 @@ def send_email(to_email, subject, body):
73
  msg["Subject"] = subject
74
  msg.attach(MIMEText(body, "plain"))
75
  context = ssl.create_default_context()
76
- with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, context=context, timeout=5) as server:
77
- server.login(SMTP_USERNAME, SMTP_PASSWORD)
78
- server.send_message(msg)
 
 
 
 
 
 
 
 
 
 
 
79
  logging.info(f"Email sent to {to_email}")
80
  return True
81
  except Exception as e:
82
  logging.error(f"Email error: {e}")
83
  raise Exception(f"Failed to send email: {str(e)}")
84
 
85
- # === Save to Excel ===
86
- def save_to_excel(name, email, date_str, time_str, timezone, message):
87
  try:
88
- date_clean = date_str.split(" ")[0] # Extract YYYY-MM-DD
89
- appointment_data = {
90
- "Name": name,
91
- "Email": email,
92
- "Date": date_clean,
93
- "Time": time_str,
94
- "Timezone": timezone,
95
- "Message": message or "No message",
96
- "Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
97
- }
98
- df = pd.DataFrame([appointment_data])
99
  mode = 'a' if os.path.exists(EXCEL_FILE) else 'w'
100
  with pd.ExcelWriter(EXCEL_FILE, engine='openpyxl', mode=mode, if_sheet_exists='overlay' if mode == 'a' else None) as writer:
101
  df.to_excel(writer, index=False, sheet_name='Sheet1', header=not os.path.exists(EXCEL_FILE))
102
- logging.info(f"Appointment saved: {name}, {email}")
103
  return True
104
  except Exception as e:
105
  logging.error(f"Excel save error: {e}")
106
  raise Exception(f"Failed to save appointment: {str(e)}")
107
 
108
- # === Appointment Handler ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  def book_appointment(name, email, date, time, timezone, message):
110
  try:
 
111
  if not all([name, email, date, time, timezone]):
112
  raise Exception("Please fill out all required fields.")
113
 
114
  date_clean = date.split(" ")[0] # Extract YYYY-MM-DD
115
- datetime.strptime(date_clean, "%Y-%m-%d")
116
  tz = pytz.timezone(timezone)
117
  appointment_time = datetime.strptime(f"{date_clean} {time}", "%Y-%m-%d %H:%M").replace(tzinfo=tz)
118
  if appointment_time < datetime.now(tz):
119
  raise Exception("Appointments must be in the future.")
120
 
121
- if not save_to_excel(name, email, date_clean, time, timezone, message):
122
- raise Exception("Failed to save appointment.")
 
 
 
 
 
 
 
 
 
 
 
123
 
124
- calendar_link = generate_google_calendar_link(name, email, date_clean, time, timezone)
125
- if not calendar_link:
126
- raise Exception("Failed to generate calendar link.")
127
 
 
128
  admin_msg = f"New Appointment:\nName: {name}\nEmail: {email}\nDate: {date_clean}\nTime: {time}\nTimezone: {timezone}\nMessage: {message or 'No message'}"
129
  send_email("admin@mubashirdev.com", "📅 New Appointment", admin_msg)
130
 
@@ -139,18 +143,18 @@ def book_appointment(name, email, date, time, timezone, message):
139
  "datasets": [{
140
  "label": "Appointment",
141
  "data": [1],
142
- "backgroundColor": "#003087",
143
- "borderColor": "#001F5C",
144
  "borderWidth": 2
145
  }]
146
  },
147
  "options": {
148
  "scales": {
149
- "y": {"beginAtZero": True, "ticks": {"stepSize": 1, "color": "#333"}, "title": {"display": True, "text": "Confirmed", "color": "#333"}},
150
- "x": {"title": {"display": True, "text": "Date & Time", "color": "#333"}, "ticks": {"color": "#333"}}
151
  },
152
  "plugins": {
153
- "title": {"display": True, "text": f"Appointment for {name}", "color": "#003087", "font": {"size": 18}}
154
  }
155
  }
156
  }
@@ -164,14 +168,14 @@ def book_appointment(name, email, date, time, timezone, message):
164
  logging.error(f"Booking error: {str(e)}")
165
  return str(e), []
166
 
167
- # === JavaScript for Date Dropdown Enhancement ===
168
  JS_ENHANCEMENT = """
169
  <script>
170
  document.addEventListener('DOMContentLoaded', function() {
171
  const dateDropdown = document.querySelector('select[id*="date"]');
172
  if (dateDropdown) {
173
- dateDropdown.style.background = '#E6F0FA';
174
- dateDropdown.style.border = '2px solid #003087';
175
  dateDropdown.style.borderRadius = '8px';
176
  dateDropdown.style.padding = '12px';
177
  dateDropdown.style.fontSize = '16px';
@@ -179,7 +183,7 @@ document.addEventListener('DOMContentLoaded', function() {
179
  Array.from(dateDropdown.options).forEach(option => {
180
  if (option.value.includes(today)) {
181
  option.style.fontWeight = 'bold';
182
- option.style.color = '#003087';
183
  }
184
  });
185
  }
@@ -192,33 +196,34 @@ with gr.Blocks(theme=gr.themes.Soft(), css="""
192
  .gradio-container {
193
  max-width: 700px;
194
  margin: 20px auto;
195
- background: brown;
196
  padding: 24px;
197
  border-radius: 16px;
198
- box-shadow: 0 4px 16px rgba(0,0,0,0.1);
199
  }
200
  .welcome {
201
  text-align: center;
202
  font-size: 28px;
203
- color: #003087;
204
  margin: 0 0 20px;
205
  font-weight: 700;
206
  }
207
  .gr-textbox, .gr-dropdown {
208
  border-radius: 8px;
209
- border: 2px solid #003087;
210
  padding: 12px;
211
  font-size: 16px;
212
- background: white;
 
213
  transition: border-color 0.2s;
214
  }
215
  .gr-textbox:focus, .gr-dropdown:focus {
216
- border-color: #001F5C;
217
  outline: none;
218
  }
219
  .gr-button {
220
- background: #003087;
221
- color: white;
222
  font-size: 16px;
223
  font-weight: 600;
224
  padding: 12px 32px;
@@ -229,26 +234,26 @@ with gr.Blocks(theme=gr.themes.Soft(), css="""
229
  transition: background 0.2s;
230
  }
231
  .gr-button:hover {
232
- background: #001F5C;
233
  }
234
  .gr-textbox[label="Result"] {
235
- background: #E8F5E9;
236
- border-color: #2E7D32;
237
- color: #1B5E20;
238
  font-weight: 500;
239
  padding: 16px;
240
  border-radius: 8px;
241
  }
242
  .gr-column {
243
- background: white;
244
  padding: 20px;
245
  border-radius: 12px;
246
- box-shadow: 0 2px 8px rgba(0,0,0,0.05);
247
  }
248
  .footer {
249
  text-align: center;
250
  font-size: 14px;
251
- color: #003087;
252
  margin-top: 20px;
253
  }
254
  .logo {
@@ -258,7 +263,18 @@ with gr.Blocks(theme=gr.themes.Soft(), css="""
258
  }
259
  .required::after {
260
  content: " *";
261
- color: #D32F2F;
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
  @media (max-width: 600px) {
264
  .gradio-container {
@@ -277,7 +293,7 @@ with gr.Blocks(theme=gr.themes.Soft(), css="""
277
  f"""
278
  <img src="https://ik.imagekit.io/kqeykkpy5/WhatsApp_Image_2024-12-20_at_02.57.39-removebg-preview.png?updatedAt=1747857266922" class="logo" alt="Mubashir Hussain Logo">
279
  <div class="welcome">Schedule Your Appointment</div>
280
- <p style="text-align: center; color: #333;">Select a date and time below to book with Mubashir Hussain. You'll receive a confirmation email with a Google Calendar link.</p>
281
  {JS_ENHANCEMENT}
282
  """
283
  )
@@ -294,7 +310,7 @@ with gr.Blocks(theme=gr.themes.Soft(), css="""
294
 
295
  gr.Markdown(
296
  """
297
- <div class="footer">Powered by <a href="https://mubashirdev.com" style="color: #003087; text-decoration: none;">mubashirdev.com</a></div>
298
  """
299
  )
300
 
 
14
  logging.basicConfig(filename='appointment_bot.log', level=logging.INFO,
15
  format='%(asctime)s - %(levelname)s - %(message)s')
16
 
17
+ # === Constants ===
 
 
 
 
 
 
 
 
 
 
 
18
  EXCEL_FILE = "appointments.xlsx"
 
 
19
  TIME_SLOTS = [f"{hour:02d}:00" for hour in range(9, 18)] # 9 AM to 5 PM
 
 
20
  DATE_OPTIONS = [
21
  (datetime.now() + timedelta(days=i)).strftime("%Y-%m-%d (%A)")
22
  for i in range(0, 15)
23
  ]
 
 
24
  COMMON_TIMEZONES = [
25
  "UTC", "Asia/Karachi", "US/Pacific", "US/Eastern",
26
  "Europe/London", "Asia/Tokyo", "America/New_York", "Asia/Dubai"
27
  ]
28
 
29
+ # === Email Configuration ===
30
+ SMTP_SERVER = "mail.mubashirdev.com"
31
+ SMTP_PORT = 465
32
+ SMTP_USERNAME = os.getenv("SMTP_USERNAME")
33
+ SMTP_PASSWORD = os.getenv("SMTP_PASSWORD")
34
+ FROM_NAME = "Mubashir Hussain"
35
+
36
+ # Validate environment variables
37
+ if not SMTP_USERNAME or not SMTP_PASSWORD:
38
+ raise Exception("SMTP_USERNAME and SMTP_PASSWORD environment variables are not set.")
39
+ FROM_EMAIL = SMTP_USERNAME
 
 
 
 
 
 
 
 
 
40
 
41
+ # === Utility Functions ===
42
  def send_email(to_email, subject, body):
43
  try:
44
  msg = MIMEMultipart()
 
47
  msg["Subject"] = subject
48
  msg.attach(MIMEText(body, "plain"))
49
  context = ssl.create_default_context()
50
+
51
+ # Try custom SMTP server first
52
+ try:
53
+ with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, context=context, timeout=5) as server:
54
+ server.login(SMTP_USERNAME, SMTP_PASSWORD)
55
+ server.send_message(msg)
56
+ except Exception as e:
57
+ logging.warning(f"Custom SMTP failed: {e}. Falling back to Gmail.")
58
+ # Fallback to Gmail
59
+ with smtplib.SMTP("smtp.gmail.com", 587) as server:
60
+ server.starttls(context=context)
61
+ server.login(SMTP_USERNAME, SMTP_PASSWORD)
62
+ server.send_message(msg)
63
+
64
  logging.info(f"Email sent to {to_email}")
65
  return True
66
  except Exception as e:
67
  logging.error(f"Email error: {e}")
68
  raise Exception(f"Failed to send email: {str(e)}")
69
 
70
+ def save_to_excel(data):
 
71
  try:
72
+ df = pd.DataFrame([data])
 
 
 
 
 
 
 
 
 
 
73
  mode = 'a' if os.path.exists(EXCEL_FILE) else 'w'
74
  with pd.ExcelWriter(EXCEL_FILE, engine='openpyxl', mode=mode, if_sheet_exists='overlay' if mode == 'a' else None) as writer:
75
  df.to_excel(writer, index=False, sheet_name='Sheet1', header=not os.path.exists(EXCEL_FILE))
76
+ logging.info(f"Appointment saved: {data['Name']}, {data['Email']}")
77
  return True
78
  except Exception as e:
79
  logging.error(f"Excel save error: {e}")
80
  raise Exception(f"Failed to save appointment: {str(e)}")
81
 
82
+ def create_calendar_link(name, email, date, time, timezone):
83
+ try:
84
+ tz = pytz.timezone(timezone)
85
+ start_time = datetime.strptime(f"{date} {time}", "%Y-%m-%d %H:%M").replace(tzinfo=tz)
86
+ end_time = start_time + timedelta(hours=1)
87
+ start_time_utc = start_time.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
88
+ end_time_utc = end_time.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
89
+ event = {
90
+ "action": "TEMPLATE",
91
+ "text": f"Appointment with {FROM_NAME}",
92
+ "details": f"Appointment for {name} ({email}). Contact: https://mubashirdev.com",
93
+ "dates": f"{start_time_utc}/{end_time_utc}",
94
+ "ctz": timezone
95
+ }
96
+ return f"https://www.google.com/calendar/render?{urlencode(event)}"
97
+ except Exception as e:
98
+ logging.error(f"Calendar link error: {e}")
99
+ raise Exception("Failed to generate calendar link.")
100
+
101
+ # === Core Logic ===
102
  def book_appointment(name, email, date, time, timezone, message):
103
  try:
104
+ # Input validation
105
  if not all([name, email, date, time, timezone]):
106
  raise Exception("Please fill out all required fields.")
107
 
108
  date_clean = date.split(" ")[0] # Extract YYYY-MM-DD
 
109
  tz = pytz.timezone(timezone)
110
  appointment_time = datetime.strptime(f"{date_clean} {time}", "%Y-%m-%d %H:%M").replace(tzinfo=tz)
111
  if appointment_time < datetime.now(tz):
112
  raise Exception("Appointments must be in the future.")
113
 
114
+ # Prepare data
115
+ data = {
116
+ "Name": name,
117
+ "Email": email,
118
+ "Date": date_clean,
119
+ "Time": time,
120
+ "Timezone": timezone,
121
+ "Message": message or "No message",
122
+ "Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
123
+ }
124
+
125
+ # Save to Excel
126
+ save_to_excel(data)
127
 
128
+ # Generate calendar link
129
+ calendar_link = create_calendar_link(name, email, date_clean, time, timezone)
 
130
 
131
+ # Send emails
132
  admin_msg = f"New Appointment:\nName: {name}\nEmail: {email}\nDate: {date_clean}\nTime: {time}\nTimezone: {timezone}\nMessage: {message or 'No message'}"
133
  send_email("admin@mubashirdev.com", "📅 New Appointment", admin_msg)
134
 
 
143
  "datasets": [{
144
  "label": "Appointment",
145
  "data": [1],
146
+ "backgroundColor": "#4E342E",
147
+ "borderColor": "#3E2723",
148
  "borderWidth": 2
149
  }]
150
  },
151
  "options": {
152
  "scales": {
153
+ "y": {"beginAtZero": True, "ticks": {"stepSize": 1, "color": "#FFFFFF"}, "title": {"display": True, "text": "Confirmed", "color": "#FFFFFF"}},
154
+ "x": {"title": {"display": True, "text": "Date & Time", "color": "#FFFFFF"}, "ticks": {"color": "#FFFFFF"}}
155
  },
156
  "plugins": {
157
+ "title": {"display": True, "text": f"Appointment for {name}", "color": "#FFFFFF", "font": {"size": 18}}
158
  }
159
  }
160
  }
 
168
  logging.error(f"Booking error: {str(e)}")
169
  return str(e), []
170
 
171
+ # === UI Enhancements ===
172
  JS_ENHANCEMENT = """
173
  <script>
174
  document.addEventListener('DOMContentLoaded', function() {
175
  const dateDropdown = document.querySelector('select[id*="date"]');
176
  if (dateDropdown) {
177
+ dateDropdown.style.background = '#FFFFFF';
178
+ dateDropdown.style.border = '2px solid #8D6E63';
179
  dateDropdown.style.borderRadius = '8px';
180
  dateDropdown.style.padding = '12px';
181
  dateDropdown.style.fontSize = '16px';
 
183
  Array.from(dateDropdown.options).forEach(option => {
184
  if (option.value.includes(today)) {
185
  option.style.fontWeight = 'bold';
186
+ option.style.color = '#4E342E';
187
  }
188
  });
189
  }
 
196
  .gradio-container {
197
  max-width: 700px;
198
  margin: 20px auto;
199
+ background: #5D4037; /* Brown background */
200
  padding: 24px;
201
  border-radius: 16px;
202
+ box-shadow: 0 4px 16px rgba(0,0,0,0.3);
203
  }
204
  .welcome {
205
  text-align: center;
206
  font-size: 28px;
207
+ color: #FFFFFF; /* White text */
208
  margin: 0 0 20px;
209
  font-weight: 700;
210
  }
211
  .gr-textbox, .gr-dropdown {
212
  border-radius: 8px;
213
+ border: 2px solid #8D6E63; /* Light brown border */
214
  padding: 12px;
215
  font-size: 16px;
216
+ background: #FFFFFF; /* White background */
217
+ color: #4E342E; /* Dark brown text */
218
  transition: border-color 0.2s;
219
  }
220
  .gr-textbox:focus, .gr-dropdown:focus {
221
+ border-color: #4E342E; /* Darker brown on focus */
222
  outline: none;
223
  }
224
  .gr-button {
225
+ background: #4E342E; /* Dark brown button */
226
+ color: #FFFFFF; /* White text */
227
  font-size: 16px;
228
  font-weight: 600;
229
  padding: 12px 32px;
 
234
  transition: background 0.2s;
235
  }
236
  .gr-button:hover {
237
+ background: #6D4C41; /* Lighter brown on hover */
238
  }
239
  .gr-textbox[label="Result"] {
240
+ background: #EFEBE9; /* Soft beige for success */
241
+ border-color: #8D6E63; /* Light brown border */
242
+ color: #4E342E; /* Dark brown text */
243
  font-weight: 500;
244
  padding: 16px;
245
  border-radius: 8px;
246
  }
247
  .gr-column {
248
+ background: #FFFFFF; /* White background for form container */
249
  padding: 20px;
250
  border-radius: 12px;
251
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
252
  }
253
  .footer {
254
  text-align: center;
255
  font-size: 14px;
256
+ color: #FFFFFF; /* White text */
257
  margin-top: 20px;
258
  }
259
  .logo {
 
263
  }
264
  .required::after {
265
  content: " *";
266
+ color: #D32F2F; /* Red for required fields */
267
+ }
268
+ p {
269
+ text-align: center;
270
+ color: #FFFFFF !important; /* White text for instructions */
271
+ }
272
+ a {
273
+ color: #EFEBE9 !important; /* Soft beige for links */
274
+ text-decoration: none;
275
+ }
276
+ a:hover {
277
+ text-decoration: underline;
278
  }
279
  @media (max-width: 600px) {
280
  .gradio-container {
 
293
  f"""
294
  <img src="https://ik.imagekit.io/kqeykkpy5/WhatsApp_Image_2024-12-20_at_02.57.39-removebg-preview.png?updatedAt=1747857266922" class="logo" alt="Mubashir Hussain Logo">
295
  <div class="welcome">Schedule Your Appointment</div>
296
+ <p>Select a date and time below to book with Mubashir Hussain. You'll receive a confirmation email with a Google Calendar link.</p>
297
  {JS_ENHANCEMENT}
298
  """
299
  )
 
310
 
311
  gr.Markdown(
312
  """
313
+ <div class="footer">Powered by <a href="https://mubashirdev.com">mubashirdev.com</a></div>
314
  """
315
  )
316