mubashirhussaindev commited on
Commit
591bbb5
·
verified ·
1 Parent(s): fc06ab9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -87
app.py CHANGED
@@ -28,27 +28,31 @@ EXCEL_FILE = "appointments.xlsx"
28
  # === Time Slots ===
29
  TIME_SLOTS = [f"{hour:02d}:00" for hour in range(9, 18)] # 9 AM to 5 PM
30
 
31
- # === Common Timezones (for performance) ===
 
 
 
 
 
 
32
  COMMON_TIMEZONES = [
33
- "UTC", "US/Pacific", "US/Eastern", "US/Central", "US/Mountain",
34
- "Europe/London", "Europe/Paris", "Europe/Berlin", "Asia/Karachi",
35
- "Asia/Tokyo", "Asia/Shanghai", "Asia/Dubai", "Australia/Sydney",
36
- "America/New_York", "America/Los_Angeles", "America/Chicago",
37
- "America/Toronto", "America/Sao_Paulo", "Asia/Kolkata", "Asia/Singapore"
38
- ] # Curated list for faster dropdown rendering
39
 
40
- # === Google Calendar Event Link Generator ===
41
  def generate_google_calendar_link(name, email, date_str, time_str, timezone):
42
  try:
 
43
  tz = pytz.timezone(timezone)
44
- start_time = datetime.strptime(f"{date_str} {time_str}", "%Y-%m-%d %H:%M").replace(tzinfo=tz)
45
  end_time = start_time + timedelta(hours=1)
46
  start_time_utc = start_time.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
47
  end_time_utc = end_time.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
48
  event = {
49
  "action": "TEMPLATE",
50
  "text": f"Appointment with {FROM_NAME}",
51
- "details": f"Appointment with {FROM_NAME} for {name} ({email}).\nContact: https://mubashirdev.com",
52
  "dates": f"{start_time_utc}/{end_time_utc}",
53
  "ctz": timezone
54
  }
@@ -66,10 +70,10 @@ def send_email(to_email, subject, body):
66
  msg["Subject"] = subject
67
  msg.attach(MIMEText(body, "plain"))
68
  context = ssl.create_default_context()
69
- with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, context=context) as server:
70
  server.login(SMTP_USERNAME, SMTP_PASSWORD)
71
  server.send_message(msg)
72
- logging.info(f"Email sent to {to_email}: {subject}")
73
  return True
74
  except Exception as e:
75
  logging.error(f"Email error: {e}")
@@ -78,22 +82,21 @@ def send_email(to_email, subject, body):
78
  # === Save to Excel ===
79
  def save_to_excel(name, email, date_str, time_str, timezone, message):
80
  try:
 
81
  appointment_data = {
82
  "Name": name,
83
  "Email": email,
84
- "Date": date_str,
85
  "Time": time_str,
86
  "Timezone": timezone,
87
- "Message": message if message else "No message",
88
  "Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
89
  }
90
- try:
91
- df = pd.read_excel(EXCEL_FILE, engine='openpyxl')
92
- except FileNotFoundError:
93
- df = pd.DataFrame()
94
- df = pd.concat([df, pd.DataFrame([appointment_data])], ignore_index=True)
95
- df.to_excel(EXCEL_FILE, engine='openpyxl', index=False)
96
- logging.info(f"Appointment saved: {name}, {email}, {date_str}, {time_str}, {timezone}, {message}")
97
  return True
98
  except Exception as e:
99
  logging.error(f"Excel save error: {e}")
@@ -105,134 +108,130 @@ def book_appointment(name, email, date, time, timezone, message):
105
  return "Please fill out all required fields.", []
106
 
107
  try:
108
- # Validate date format
109
- datetime.strptime(date, "%Y-%m-%d")
110
  tz = pytz.timezone(timezone)
111
- appointment_time = datetime.strptime(f"{date} {time}", "%Y-%m-%d %H:%M").replace(tzinfo=tz)
112
  if appointment_time < datetime.now(tz):
113
  return "Appointments must be in the future.", []
114
 
115
- if not save_to_excel(name, email, date, time, timezone, message):
116
  return "Failed to save appointment.", []
117
 
118
- calendar_link = generate_google_calendar_link(name, email, date, time, timezone)
119
  if not calendar_link:
120
  return "Failed to generate calendar link, but appointment saved.", []
121
 
122
- admin_msg = (
123
- f"New Appointment:\n"
124
- f"Name: {name}\n"
125
- f"Email: {email}\n"
126
- f"Date: {date}\n"
127
- f"Time: {time}\n"
128
- f"Timezone: {timezone}\n"
129
- f"Message: {message if message else 'No message'}"
130
- )
131
  send_email("admin@mubashirdev.com", "📅 New Appointment", admin_msg)
132
 
133
- user_msg = (
134
- f"Hi {name},\n\n"
135
- f"Your appointment is confirmed for {date} at {time} ({timezone}).\n"
136
- f"Add to Google Calendar: {calendar_link}\n\n"
137
- f"Your message: {message if message else 'No message'}\n\n"
138
- f"Thank you for choosing my services!\n"
139
- f"Mubashir Hussain\nhttps://mubashirdev.com"
140
- )
141
  send_email(email, "✅ Appointment Confirmed", user_msg)
142
 
143
- # Generate styled chart
144
  chart_data = {
145
  "type": "bar",
146
  "data": {
147
- "labels": [f"{date} {time}"],
148
  "datasets": [{
149
  "label": "Appointment",
150
  "data": [1],
151
- "backgroundColor": "#1976D2",
152
- "borderColor": "#0D47A1",
153
  "borderWidth": 2
154
  }]
155
  },
156
  "options": {
157
  "scales": {
158
- "y": {
159
- "beginAtZero": True,
160
- "ticks": {"stepSize": 1, "color": "#333", "font": {"family": "Roboto", "size": 14}},
161
- "title": {"display": True, "text": "Confirmed", "color": "#333", "font": {"family": "Roboto", "size": 16}}
162
- },
163
- "x": {
164
- "title": {"display": True, "text": "Date & Time", "color": "#333", "font": {"family": "Roboto", "size": 16}},
165
- "ticks": {"color": "#333", "font": {"family": "Roboto", "size": 14}}
166
- }
167
  },
168
  "plugins": {
169
- "title": {"display": True, "text": f"Appointment for {name}", "color": "#1976D2", "font": {"family": "Roboto", "size": 18}}
170
  }
171
  }
172
  }
173
 
174
  return (
175
- f"✅ Appointment confirmed for {date} at {time} ({timezone}). Email sent to {email}.",
176
  [chart_data]
177
  )
178
 
179
  except pytz.exceptions.UnknownTimeZoneError:
180
  return "Invalid timezone.", []
181
  except ValueError:
182
- return "Invalid date format. Use YYYY-MM-DD (e.g., 2025-06-01).", []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
  # === Gradio UI ===
185
  with gr.Blocks(theme=gr.themes.Soft(), css="""
186
- @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap');
187
  .gradio-container {
188
  max-width: 700px;
189
  margin: 20px auto;
190
- font-family: 'Roboto', sans-serif;
191
- background: #F5F6F5;
192
  padding: 24px;
193
  border-radius: 16px;
194
  box-shadow: 0 4px 16px rgba(0,0,0,0.1);
195
  }
196
  .welcome {
197
  text-align: center;
198
- font-size: 32px;
199
- color: #1976D2;
200
- margin: 0 0 24px;
201
  font-weight: 700;
202
  }
203
  .gr-textbox, .gr-dropdown {
204
  border-radius: 8px;
205
- border: 2px solid #B0BEC5;
206
  padding: 12px;
207
  font-size: 16px;
 
208
  transition: border-color 0.2s;
209
  }
210
  .gr-textbox:focus, .gr-dropdown:focus {
211
- border-color: #1976D2;
212
  outline: none;
213
  }
214
- .gr-textbox[label="Date"]::before {
215
- content: "📅 ";
216
- }
217
  .gr-button {
218
- background: #1976D2;
219
  color: white;
220
  font-size: 16px;
221
  font-weight: 600;
222
  padding: 12px 32px;
223
  border-radius: 8px;
224
  border: none;
225
- transition: background 0.2s, transform 0.1s;
226
  margin: 24px auto;
227
  display: block;
 
228
  }
229
  .gr-button:hover {
230
- background: #0D47A1;
231
- transform: translateY(-2px);
232
  }
233
  .gr-textbox[label="Result"] {
234
- background: #C8E6C9;
235
- border-color: #388E3C;
236
  color: #1B5E20;
237
  font-weight: 500;
238
  padding: 16px;
@@ -240,15 +239,15 @@ with gr.Blocks(theme=gr.themes.Soft(), css="""
240
  }
241
  .gr-column {
242
  background: white;
243
- padding: 24px;
244
  border-radius: 12px;
245
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
246
  }
247
  .footer {
248
  text-align: center;
249
  font-size: 14px;
250
- color: #546E7A;
251
- margin-top: 24px;
252
  }
253
  .logo {
254
  display: block;
@@ -272,27 +271,28 @@ with gr.Blocks(theme=gr.themes.Soft(), css="""
272
  }
273
  }
274
  """) as demo:
275
- gr.Markdown(
276
- """
277
  <img src="/static/logo.png" class="logo" alt="Mubashir Hussain Logo">
278
- <div class="welcome">Book Your Appointment</div>
279
- Complete the form below to schedule an appointment with Mubashir Hussain. You'll receive a confirmation email with a Google Calendar link.
 
280
  """
281
  )
282
  with gr.Column():
283
  name = gr.Textbox(label="Your Name", placeholder="Enter your name", elem_classes="required")
284
  email = gr.Textbox(label="Your Email", placeholder="Enter your email", elem_classes="required")
285
- date = gr.Textbox(label="Date", placeholder="YYYY-MM-DD (e.g., 2025-06-01)", type="text", elem_classes="required")
286
  time = gr.Dropdown(label="Time", choices=TIME_SLOTS, value="09:00", elem_classes="required")
287
  timezone = gr.Dropdown(label="Timezone", choices=COMMON_TIMEZONES, value="Asia/Karachi", elem_classes="required")
288
- message = gr.Textbox(label="Message (Optional)", placeholder="Add any details or notes", lines=4)
289
  submit_btn = gr.Button("Book Appointment")
290
  output = gr.Textbox(label="Result", interactive=False)
291
  chart_output = gr.Plot(label="Confirmation")
292
 
293
  gr.Markdown(
294
  """
295
- <div class="footer">Powered by <a href="https://mubashirdev.com" style="color: #1976D2; text-decoration: none;">mubashirdev.com</a></div>
296
  """
297
  )
298
 
@@ -302,5 +302,5 @@ with gr.Blocks(theme=gr.themes.Soft(), css="""
302
  outputs=[output, chart_output]
303
  )
304
 
305
- # Launch the app (Hugging Face Spaces handles server_name and server_port)
306
  demo.launch()
 
28
  # === Time Slots ===
29
  TIME_SLOTS = [f"{hour:02d}:00" for hour in range(9, 18)] # 9 AM to 5 PM
30
 
31
+ # === Date Options (Next 15 Days for Speed) ===
32
+ DATE_OPTIONS = [
33
+ (datetime.now() + timedelta(days=i)).strftime("%Y-%m-%d (%A)")
34
+ for i in range(0, 15)
35
+ ]
36
+
37
+ # === Common Timezones (minimized for speed) ===
38
  COMMON_TIMEZONES = [
39
+ "UTC", "Asia/Karachi", "US/Pacific", "US/Eastern",
40
+ "Europe/London", "Asia/Tokyo", "America/New_York", "Asia/Dubai"
41
+ ]
 
 
 
42
 
43
+ # === Google Calendar Link Generator ===
44
  def generate_google_calendar_link(name, email, date_str, time_str, timezone):
45
  try:
46
+ date_clean = date_str.split(" ")[0] # Extract YYYY-MM-DD
47
  tz = pytz.timezone(timezone)
48
+ start_time = datetime.strptime(f"{date_clean} {time_str}", "%Y-%m-%d %H:%M").replace(tzinfo=tz)
49
  end_time = start_time + timedelta(hours=1)
50
  start_time_utc = start_time.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
51
  end_time_utc = end_time.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
52
  event = {
53
  "action": "TEMPLATE",
54
  "text": f"Appointment with {FROM_NAME}",
55
+ "details": f"Appointment for {name} ({email}). Contact: https://mubashirdev.com",
56
  "dates": f"{start_time_utc}/{end_time_utc}",
57
  "ctz": timezone
58
  }
 
70
  msg["Subject"] = subject
71
  msg.attach(MIMEText(body, "plain"))
72
  context = ssl.create_default_context()
73
+ with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, context=context, timeout=5) as server:
74
  server.login(SMTP_USERNAME, SMTP_PASSWORD)
75
  server.send_message(msg)
76
+ logging.info(f"Email sent to {to_email}")
77
  return True
78
  except Exception as e:
79
  logging.error(f"Email error: {e}")
 
82
  # === Save to Excel ===
83
  def save_to_excel(name, email, date_str, time_str, timezone, message):
84
  try:
85
+ date_clean = date_str.split(" ")[0] # Extract YYYY-MM-DD
86
  appointment_data = {
87
  "Name": name,
88
  "Email": email,
89
+ "Date": date_clean,
90
  "Time": time_str,
91
  "Timezone": timezone,
92
+ "Message": message or "No message",
93
  "Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
94
  }
95
+ df = pd.DataFrame([appointment_data])
96
+ mode = 'a' if os.path.exists(EXCEL_FILE) else 'w'
97
+ with pd.ExcelWriter(EXCEL_FILE, engine='openpyxl', mode=mode, if_sheet_exists='overlay' if mode == 'a' else None) as writer:
98
+ df.to_excel(writer, index=False, sheet_name='Sheet1', header=not os.path.exists(EXCEL_FILE))
99
+ logging.info(f"Appointment saved: {name}, {email}")
 
 
100
  return True
101
  except Exception as e:
102
  logging.error(f"Excel save error: {e}")
 
108
  return "Please fill out all required fields.", []
109
 
110
  try:
111
+ date_clean = date.split(" ")[0] # Extract YYYY-MM-DD
112
+ datetime.strptime(date_clean, "%Y-%m-%d")
113
  tz = pytz.timezone(timezone)
114
+ appointment_time = datetime.strptime(f"{date_clean} {time}", "%Y-%m-%d %H:%M").replace(tzinfo=tz)
115
  if appointment_time < datetime.now(tz):
116
  return "Appointments must be in the future.", []
117
 
118
+ if not save_to_excel(name, email, date_clean, time, timezone, message):
119
  return "Failed to save appointment.", []
120
 
121
+ calendar_link = generate_google_calendar_link(name, email, date_clean, time, timezone)
122
  if not calendar_link:
123
  return "Failed to generate calendar link, but appointment saved.", []
124
 
125
+ admin_msg = f"New Appointment:\nName: {name}\nEmail: {email}\nDate: {date_clean}\nTime: {time}\nTimezone: {timezone}\nMessage: {message or 'No message'}"
 
 
 
 
 
 
 
 
126
  send_email("admin@mubashirdev.com", "📅 New Appointment", admin_msg)
127
 
128
+ user_msg = f"Hi {name},\n\nYour appointment is confirmed for {date_clean} at {time} ({timezone}).\nAdd to Google Calendar: {calendar_link}\n\nYour message: {message or 'No message'}\n\nThank you!\nMubashir Hussain\nhttps://mubashirdev.com"
 
 
 
 
 
 
 
129
  send_email(email, "✅ Appointment Confirmed", user_msg)
130
 
131
+ # Generate chart
132
  chart_data = {
133
  "type": "bar",
134
  "data": {
135
+ "labels": [f"{date_clean} {time}"],
136
  "datasets": [{
137
  "label": "Appointment",
138
  "data": [1],
139
+ "backgroundColor": "#003087",
140
+ "borderColor": "#001F5C",
141
  "borderWidth": 2
142
  }]
143
  },
144
  "options": {
145
  "scales": {
146
+ "y": {"beginAtZero": True, "ticks": {"stepSize": 1, "color": "#333"}, "title": {"display": True, "text": "Confirmed", "color": "#333"}},
147
+ "x": {"title": {"display": True, "text": "Date & Time", "color": "#333"}, "ticks": {"color": "#333"}}
 
 
 
 
 
 
 
148
  },
149
  "plugins": {
150
+ "title": {"display": True, "text": f"Appointment for {name}", "color": "#003087", "font": {"size": 18}}
151
  }
152
  }
153
  }
154
 
155
  return (
156
+ f"✅ Appointment confirmed for {date_clean} at {time} ({timezone}). Email sent to {email}.",
157
  [chart_data]
158
  )
159
 
160
  except pytz.exceptions.UnknownTimeZoneError:
161
  return "Invalid timezone.", []
162
  except ValueError:
163
+ return "Invalid date format.", []
164
+
165
+ # === JavaScript for Date Dropdown Enhancement ===
166
+ JS_ENHANCEMENT = """
167
+ <script>
168
+ document.addEventListener('DOMContentLoaded', function() {
169
+ const dateDropdown = document.querySelector('select[id*="date"]');
170
+ if (dateDropdown) {
171
+ dateDropdown.style.background = '#E6F0FA';
172
+ dateDropdown.style.border = '2px solid #003087';
173
+ dateDropdown.style.borderRadius = '8px';
174
+ dateDropdown.style.padding = '12px';
175
+ dateDropdown.style.fontSize = '16px';
176
+ const today = new Date().toISOString().split('T')[0];
177
+ Array.from(dateDropdown.options).forEach(option => {
178
+ if (option.value.includes(today)) {
179
+ option.style.fontWeight = 'bold';
180
+ option.style.color = '#003087';
181
+ }
182
+ });
183
+ }
184
+ });
185
+ </script>
186
+ """
187
 
188
  # === Gradio UI ===
189
  with gr.Blocks(theme=gr.themes.Soft(), css="""
 
190
  .gradio-container {
191
  max-width: 700px;
192
  margin: 20px auto;
193
+ background: #E6F0FA;
 
194
  padding: 24px;
195
  border-radius: 16px;
196
  box-shadow: 0 4px 16px rgba(0,0,0,0.1);
197
  }
198
  .welcome {
199
  text-align: center;
200
+ font-size: 28px;
201
+ color: #003087;
202
+ margin: 0 0 20px;
203
  font-weight: 700;
204
  }
205
  .gr-textbox, .gr-dropdown {
206
  border-radius: 8px;
207
+ border: 2px solid #003087;
208
  padding: 12px;
209
  font-size: 16px;
210
+ background: white;
211
  transition: border-color 0.2s;
212
  }
213
  .gr-textbox:focus, .gr-dropdown:focus {
214
+ border-color: #001F5C;
215
  outline: none;
216
  }
 
 
 
217
  .gr-button {
218
+ background: #003087;
219
  color: white;
220
  font-size: 16px;
221
  font-weight: 600;
222
  padding: 12px 32px;
223
  border-radius: 8px;
224
  border: none;
 
225
  margin: 24px auto;
226
  display: block;
227
+ transition: background 0.2s;
228
  }
229
  .gr-button:hover {
230
+ background: #001F5C;
 
231
  }
232
  .gr-textbox[label="Result"] {
233
+ background: #E8F5E9;
234
+ border-color: #2E7D32;
235
  color: #1B5E20;
236
  font-weight: 500;
237
  padding: 16px;
 
239
  }
240
  .gr-column {
241
  background: white;
242
+ padding: 20px;
243
  border-radius: 12px;
244
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
245
  }
246
  .footer {
247
  text-align: center;
248
  font-size: 14px;
249
+ color: #003087;
250
+ margin-top: 20px;
251
  }
252
  .logo {
253
  display: block;
 
271
  }
272
  }
273
  """) as demo:
274
+ gr.HTML(
275
+ f"""
276
  <img src="/static/logo.png" class="logo" alt="Mubashir Hussain Logo">
277
+ <div class="welcome">Schedule Your Appointment</div>
278
+ <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>
279
+ {JS_ENHANCEMENT}
280
  """
281
  )
282
  with gr.Column():
283
  name = gr.Textbox(label="Your Name", placeholder="Enter your name", elem_classes="required")
284
  email = gr.Textbox(label="Your Email", placeholder="Enter your email", elem_classes="required")
285
+ date = gr.Dropdown(label="Date", choices=DATE_OPTIONS, value=DATE_OPTIONS[0], elem_classes="required")
286
  time = gr.Dropdown(label="Time", choices=TIME_SLOTS, value="09:00", elem_classes="required")
287
  timezone = gr.Dropdown(label="Timezone", choices=COMMON_TIMEZONES, value="Asia/Karachi", elem_classes="required")
288
+ message = gr.Textbox(label="Message (Optional)", placeholder="Add any details or notes", lines=3)
289
  submit_btn = gr.Button("Book Appointment")
290
  output = gr.Textbox(label="Result", interactive=False)
291
  chart_output = gr.Plot(label="Confirmation")
292
 
293
  gr.Markdown(
294
  """
295
+ <div class="footer">Powered by <a href="https://mubashirdev.com" style="color: #003087; text-decoration: none;">mubashirdev.com</a></div>
296
  """
297
  )
298
 
 
302
  outputs=[output, chart_output]
303
  )
304
 
305
+ # Launch the app (no port specification for Hugging Face Spaces)
306
  demo.launch()