sonuprasad23 commited on
Commit
f8af8c1
·
1 Parent(s): a2bc3de
Files changed (1) hide show
  1. server.py +394 -43
server.py CHANGED
@@ -1,4 +1,4 @@
1
- # server.py - Using Gmail API (Same Service Account)
2
  import eventlet
3
  eventlet.monkey_patch()
4
 
@@ -24,7 +24,6 @@ load_dotenv()
24
  app = Flask(__name__)
25
  app.config['SECRET_KEY'] = 'secret-key-for-hillside-automation'
26
 
27
- # Configure CORS for both local development and your Netlify frontend
28
  FRONTEND_ORIGIN = os.getenv('FRONTEND_URL', 'https://quantbot.netlify.app')
29
  CORS(app, resources={r"/*": {"origins": [FRONTEND_ORIGIN, "http://localhost:3000", "http://127.0.0.1:5500"]}})
30
  socketio = SocketIO(app, cors_allowed_origins=[FRONTEND_ORIGIN, "http://localhost:3000", "http://127.0.0.1:5500"], async_mode='eventlet')
@@ -41,57 +40,407 @@ class GmailApiService:
41
  from google.oauth2 import service_account
42
  from googleapiclient.discovery import build
43
 
44
- # Use the SAME credentials as your Google Drive setup
45
- base64_creds = os.getenv('GDRIVE_SA_KEY_BASE64') # Same as Google Drive!
46
  if not base64_creds:
47
  print("[Gmail API] WARNING: GDRIVE_SA_KEY_BASE64 not found.")
48
  return
49
 
50
- # Decode credentials
51
  creds_json = base64.b64decode(base64_creds).decode('utf-8')
52
  creds_dict = json.loads(creds_json)
53
 
54
- # Create credentials with Gmail scope
55
  credentials = service_account.Credentials.from_service_account_info(
56
  creds_dict,
57
  scopes=['https://www.googleapis.com/auth/gmail.send']
58
  )
59
 
60
- # If you need to send as a specific user (domain-wide delegation)
61
  if self.sender_email:
62
  credentials = credentials.with_subject(self.sender_email)
63
 
64
- # Build Gmail service
65
  self.service = build('gmail', 'v1', credentials=credentials)
66
- print("[Gmail API] Service initialized successfully using existing credentials!")
67
 
68
  except Exception as e:
69
- print(f"[Gmail API] Error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  def send_report(self, recipients, subject, body, attachments=None):
72
  if not self.service:
73
- print("[Gmail API] Service not initialized")
74
  return False
75
 
76
  if not recipients:
77
- print("[Gmail API] No recipients provided")
78
  return False
79
 
80
  try:
81
  from googleapiclient.errors import HttpError
82
 
83
- print(f"[Gmail API] 📧 Sending to: {recipients}")
84
 
85
- # Create MIME message
86
  message = MIMEMultipart()
87
  message['From'] = self.sender_email
88
  message['To'] = ', '.join(recipients)
89
  message['Subject'] = subject
90
 
91
- # Add HTML body
92
  message.attach(MIMEText(body, 'html'))
93
 
94
- # Add attachments
95
  if attachments:
96
  for filename, content in attachments.items():
97
  part = MIMEBase('application', 'octet-stream')
@@ -100,24 +449,22 @@ class GmailApiService:
100
  part.add_header('Content-Disposition', f'attachment; filename="{filename}"')
101
  message.attach(part)
102
 
103
- # Encode for Gmail API
104
  raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode('utf-8')
105
 
106
- # Send via Gmail API
107
  gmail_message = {'raw': raw_message}
108
  sent_message = self.service.users().messages().send(
109
  userId='me',
110
  body=gmail_message
111
  ).execute()
112
 
113
- print(f"[Gmail API] Email sent! Message ID: {sent_message['id']}")
114
  return True
115
 
116
  except HttpError as error:
117
- print(f"[Gmail API] HTTP Error: {error}")
118
  return False
119
  except Exception as e:
120
- print(f"[Gmail API] Error: {e}")
121
  return False
122
 
123
  class GoogleDriveService:
@@ -153,7 +500,6 @@ class GoogleDriveService:
153
  print(f"[G-Drive] ERROR: File upload failed: {e}")
154
  return False
155
 
156
- # Initialize services
157
  email_service = GmailApiService()
158
  drive_service = GoogleDriveService()
159
 
@@ -196,49 +542,54 @@ def generate_and_send_reports(session_id, results, is_crash_report=False, is_ter
196
  if not results:
197
  socketio.emit('process_complete', {'message': 'No patients were processed.'})
198
  return
 
199
  data = session_data.get(session_id, {})
200
  original_df = pd.read_csv(io.StringIO(data.get('csv_content')))
201
  if 'Status' not in original_df.columns:
202
  original_df.insert(1, 'Status', '')
 
203
  result_df = pd.DataFrame(results).set_index('Name')
204
  original_df.set_index('Name', inplace=True)
205
  original_df.update(result_df)
206
  full_df = original_df.reset_index()
207
  bad_df = full_df[full_df['Status'] == 'Bad'][['Name', 'Status']]
 
208
  timestamp = datetime.now().strftime("%d_%b_%Y")
209
  custom_name = data.get('filename') or timestamp
210
  full_report_name = f"{custom_name}_Full.csv"
211
  bad_report_name = f"{custom_name}_Bad.csv"
212
  full_report_content = full_df.to_csv(index=False)
 
213
  drive_service.upload_file(full_report_name, full_report_content)
 
214
  attachments = {full_report_name: full_report_content, bad_report_name: bad_df.to_csv(index=False)}
215
  status_text = "terminated by user" if is_terminated else "crashed due to an error" if is_crash_report else "completed successfully"
216
- subject = f"📧 Automation Report [{status_text.upper()}]: {custom_name}"
217
- body = f"""<html><body>
218
- <h2>🤗 Hillside's Quantum Automation Report</h2>
219
- <p><b>Status:</b> Process {status_text}</p>
220
- <p><b>Total Patients in File:</b> {len(full_df)}</p>
221
- <p><b>Processed in This Run:</b> {len(results)}</p>
222
- <p><b>Successful ('Done'):</b> {len([r for r in results if r['Status'] == 'Done'])}</p>
223
- <p><b>Bad State ('Bad'):</b> {len([r for r in results if r['Status'] == 'Bad'])}</p>
224
- <p><b>Email Method:</b> Gmail API (SMTP-Free) ✨</p>
225
- <p>The full report and 'Bad' status patients are attached to this email.</p>
226
- <p><i>Sent from HuggingFace Spaces via Gmail API</i></p>
227
- </body></html>"""
228
 
229
- email_success = email_service.send_report(data.get('emails'), subject, body, attachments)
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
  if email_success:
232
- socketio.emit('process_complete', {'message': f'Process {status_text}. Report sent via Gmail API!'})
233
  else:
234
- socketio.emit('process_complete', {'message': f'⚠️ Process {status_text}. Email failed!'})
235
 
236
  @app.route('/')
237
  def status_page():
238
- APP_STATUS_HTML = """<!DOCTYPE html><html lang="en"><head><title>🤗 Hillside Automation - Gmail API</title><style>body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);color:white;}.status-box{text-align:center;padding:40px 60px;background:rgba(255,255,255,0.1);border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,0.3);backdrop-filter:blur(10px);}h1{font-size:28px;margin-bottom:10px;} .indicator{font-size:18px;font-weight:600;padding:12px 20px;border-radius:25px;background:rgba(40,167,69,0.2);border:2px solid #28a745;color:#28a745;margin:15px 0;}.info{margin-top:20px;opacity:0.9;}</style></head><body><div class="status-box"><h1>🤗 Hillside Automation</h1><div class="indicator">📧 Gmail API Mode - SMTP Bypassed</div><div class="info">✅ Chrome: Ready<br>📧 Email: Gmail API (HTTP)<br>☁️ Drive: Integrated<br>🚀 Platform: HuggingFace<br><br><a href="https://quantbot.netlify.app" style="color:#60a5fa;">quantbot.netlify.app</a></div></div></body></html>"""
239
  return Response(APP_STATUS_HTML)
240
 
241
- # Keep all your existing socket handlers unchanged
242
  @socketio.on('connect')
243
  def handle_connect():
244
  print(f'Frontend connected from: {FRONTEND_ORIGIN}')
@@ -289,9 +640,9 @@ def handle_terminate():
289
 
290
  if __name__ == '__main__':
291
  print("====================================================================")
292
- print(" 🤗 Hillside Automation - Gmail API Edition")
293
- print(" 📧 Email: Gmail API (Same Service Account as Drive)")
294
- print(" 🚫 SMTP: Completely bypassed")
295
- print(f" 🔗 Frontend: {FRONTEND_ORIGIN}")
296
  print("====================================================================")
297
  socketio.run(app, host='0.0.0.0', port=int(os.getenv('PORT', 7860)))
 
1
+ # server.py - Gmail API with Professional Email Reports
2
  import eventlet
3
  eventlet.monkey_patch()
4
 
 
24
  app = Flask(__name__)
25
  app.config['SECRET_KEY'] = 'secret-key-for-hillside-automation'
26
 
 
27
  FRONTEND_ORIGIN = os.getenv('FRONTEND_URL', 'https://quantbot.netlify.app')
28
  CORS(app, resources={r"/*": {"origins": [FRONTEND_ORIGIN, "http://localhost:3000", "http://127.0.0.1:5500"]}})
29
  socketio = SocketIO(app, cors_allowed_origins=[FRONTEND_ORIGIN, "http://localhost:3000", "http://127.0.0.1:5500"], async_mode='eventlet')
 
40
  from google.oauth2 import service_account
41
  from googleapiclient.discovery import build
42
 
43
+ base64_creds = os.getenv('GDRIVE_SA_KEY_BASE64')
 
44
  if not base64_creds:
45
  print("[Gmail API] WARNING: GDRIVE_SA_KEY_BASE64 not found.")
46
  return
47
 
 
48
  creds_json = base64.b64decode(base64_creds).decode('utf-8')
49
  creds_dict = json.loads(creds_json)
50
 
 
51
  credentials = service_account.Credentials.from_service_account_info(
52
  creds_dict,
53
  scopes=['https://www.googleapis.com/auth/gmail.send']
54
  )
55
 
 
56
  if self.sender_email:
57
  credentials = credentials.with_subject(self.sender_email)
58
 
 
59
  self.service = build('gmail', 'v1', credentials=credentials)
60
+ print("[Gmail API] Service initialized successfully using existing credentials")
61
 
62
  except Exception as e:
63
+ print(f"[Gmail API] Error: {e}")
64
+
65
+ def create_professional_email_template(self, subject, status_text, stats, custom_name):
66
+ """Create a professional HTML email template"""
67
+
68
+ status_color = "#28a745" if "successfully" in status_text else "#dc3545" if "error" in status_text else "#ffc107"
69
+ current_date = datetime.now().strftime("%B %d, %Y at %I:%M %p")
70
+
71
+ html_template = f"""
72
+ <!DOCTYPE html>
73
+ <html lang="en">
74
+ <head>
75
+ <meta charset="UTF-8">
76
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
77
+ <title>{subject}</title>
78
+ <style>
79
+ * {{
80
+ margin: 0;
81
+ padding: 0;
82
+ box-sizing: border-box;
83
+ }}
84
+
85
+ body {{
86
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
87
+ line-height: 1.6;
88
+ color: #333333;
89
+ background-color: #f8f9fa;
90
+ }}
91
+
92
+ .email-container {{
93
+ max-width: 700px;
94
+ margin: 0 auto;
95
+ background-color: #ffffff;
96
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
97
+ }}
98
+
99
+ .header {{
100
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
101
+ color: white;
102
+ padding: 40px 30px;
103
+ text-align: center;
104
+ }}
105
+
106
+ .header h1 {{
107
+ font-size: 28px;
108
+ font-weight: 600;
109
+ margin-bottom: 8px;
110
+ }}
111
+
112
+ .header p {{
113
+ font-size: 16px;
114
+ opacity: 0.9;
115
+ font-weight: 300;
116
+ }}
117
+
118
+ .status-banner {{
119
+ background-color: {status_color};
120
+ color: white;
121
+ text-align: center;
122
+ padding: 15px;
123
+ font-size: 16px;
124
+ font-weight: 500;
125
+ text-transform: uppercase;
126
+ letter-spacing: 0.5px;
127
+ }}
128
+
129
+ .content {{
130
+ padding: 30px;
131
+ }}
132
+
133
+ .report-info {{
134
+ background-color: #f8f9fa;
135
+ border-left: 4px solid #667eea;
136
+ padding: 20px;
137
+ margin-bottom: 30px;
138
+ border-radius: 0 6px 6px 0;
139
+ }}
140
+
141
+ .report-info h3 {{
142
+ color: #495057;
143
+ margin-bottom: 15px;
144
+ font-size: 18px;
145
+ }}
146
+
147
+ .info-grid {{
148
+ display: grid;
149
+ grid-template-columns: 1fr 1fr;
150
+ gap: 20px;
151
+ margin-bottom: 20px;
152
+ }}
153
+
154
+ .info-item {{
155
+ display: flex;
156
+ justify-content: space-between;
157
+ align-items: center;
158
+ padding: 12px 0;
159
+ border-bottom: 1px solid #e9ecef;
160
+ }}
161
+
162
+ .info-label {{
163
+ font-weight: 500;
164
+ color: #495057;
165
+ }}
166
+
167
+ .info-value {{
168
+ font-weight: 600;
169
+ color: #212529;
170
+ }}
171
+
172
+ .stats-section {{
173
+ margin: 30px 0;
174
+ }}
175
+
176
+ .stats-section h3 {{
177
+ color: #495057;
178
+ margin-bottom: 20px;
179
+ font-size: 18px;
180
+ border-bottom: 2px solid #e9ecef;
181
+ padding-bottom: 10px;
182
+ }}
183
+
184
+ .stats-grid {{
185
+ display: grid;
186
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
187
+ gap: 20px;
188
+ margin-bottom: 20px;
189
+ }}
190
+
191
+ .stat-card {{
192
+ background-color: white;
193
+ border: 1px solid #e9ecef;
194
+ border-radius: 8px;
195
+ padding: 20px;
196
+ text-align: center;
197
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
198
+ }}
199
+
200
+ .stat-number {{
201
+ font-size: 32px;
202
+ font-weight: 700;
203
+ color: #667eea;
204
+ margin-bottom: 8px;
205
+ }}
206
+
207
+ .stat-label {{
208
+ font-size: 14px;
209
+ color: #6c757d;
210
+ text-transform: uppercase;
211
+ letter-spacing: 0.5px;
212
+ font-weight: 500;
213
+ }}
214
+
215
+ .success .stat-number {{ color: #28a745; }}
216
+ .warning .stat-number {{ color: #ffc107; }}
217
+ .danger .stat-number {{ color: #dc3545; }}
218
+
219
+ .attachments-section {{
220
+ background-color: #f8f9fa;
221
+ border-radius: 8px;
222
+ padding: 20px;
223
+ margin: 30px 0;
224
+ }}
225
+
226
+ .attachments-section h3 {{
227
+ color: #495057;
228
+ margin-bottom: 15px;
229
+ font-size: 16px;
230
+ }}
231
+
232
+ .attachment-item {{
233
+ display: flex;
234
+ align-items: center;
235
+ padding: 10px 0;
236
+ border-bottom: 1px solid #dee2e6;
237
+ }}
238
+
239
+ .attachment-item:last-child {{
240
+ border-bottom: none;
241
+ }}
242
+
243
+ .attachment-icon {{
244
+ width: 24px;
245
+ height: 24px;
246
+ background-color: #667eea;
247
+ border-radius: 4px;
248
+ margin-right: 12px;
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: center;
252
+ color: white;
253
+ font-size: 12px;
254
+ font-weight: bold;
255
+ }}
256
+
257
+ .attachment-name {{
258
+ font-weight: 500;
259
+ color: #495057;
260
+ }}
261
+
262
+ .footer {{
263
+ background-color: #495057;
264
+ color: #ffffff;
265
+ padding: 30px;
266
+ text-align: center;
267
+ }}
268
+
269
+ .footer-content {{
270
+ max-width: 500px;
271
+ margin: 0 auto;
272
+ }}
273
+
274
+ .footer h4 {{
275
+ margin-bottom: 15px;
276
+ font-size: 16px;
277
+ font-weight: 600;
278
+ }}
279
+
280
+ .footer p {{
281
+ font-size: 14px;
282
+ opacity: 0.8;
283
+ line-height: 1.5;
284
+ }}
285
+
286
+ .footer .timestamp {{
287
+ margin-top: 20px;
288
+ padding-top: 20px;
289
+ border-top: 1px solid rgba(255, 255, 255, 0.2);
290
+ font-size: 12px;
291
+ opacity: 0.7;
292
+ }}
293
+
294
+ @media only screen and (max-width: 600px) {{
295
+ .email-container {{
296
+ margin: 0;
297
+ box-shadow: none;
298
+ }}
299
+
300
+ .header {{
301
+ padding: 30px 20px;
302
+ }}
303
+
304
+ .content {{
305
+ padding: 20px;
306
+ }}
307
+
308
+ .info-grid {{
309
+ grid-template-columns: 1fr;
310
+ gap: 10px;
311
+ }}
312
+
313
+ .stats-grid {{
314
+ grid-template-columns: 1fr;
315
+ }}
316
+
317
+ .header h1 {{
318
+ font-size: 24px;
319
+ }}
320
+ }}
321
+ </style>
322
+ </head>
323
+ <body>
324
+ <div class="email-container">
325
+ <div class="header">
326
+ <h1>Hillside Medical Group</h1>
327
+ <p>Automation Processing Report</p>
328
+ </div>
329
+
330
+ <div class="status-banner">
331
+ Process {status_text.title()}
332
+ </div>
333
+
334
+ <div class="content">
335
+ <div class="report-info">
336
+ <h3>Report Information</h3>
337
+ <div class="info-grid">
338
+ <div class="info-item">
339
+ <span class="info-label">Report Name:</span>
340
+ <span class="info-value">{custom_name}</span>
341
+ </div>
342
+ <div class="info-item">
343
+ <span class="info-label">Generated:</span>
344
+ <span class="info-value">{current_date}</span>
345
+ </div>
346
+ <div class="info-item">
347
+ <span class="info-label">Process Status:</span>
348
+ <span class="info-value" style="color: {status_color};">{status_text.title()}</span>
349
+ </div>
350
+ <div class="info-item">
351
+ <span class="info-label">Email Method:</span>
352
+ <span class="info-value">Gmail API</span>
353
+ </div>
354
+ </div>
355
+ </div>
356
+
357
+ <div class="stats-section">
358
+ <h3>Processing Statistics</h3>
359
+ <div class="stats-grid">
360
+ <div class="stat-card">
361
+ <div class="stat-number">{stats['total']}</div>
362
+ <div class="stat-label">Total Records</div>
363
+ </div>
364
+ <div class="stat-card">
365
+ <div class="stat-number">{stats['processed']}</div>
366
+ <div class="stat-label">Processed</div>
367
+ </div>
368
+ <div class="stat-card success">
369
+ <div class="stat-number">{stats['successful']}</div>
370
+ <div class="stat-label">Successful</div>
371
+ </div>
372
+ <div class="stat-card danger">
373
+ <div class="stat-number">{stats['failed']}</div>
374
+ <div class="stat-label">Issues Found</div>
375
+ </div>
376
+ </div>
377
+ </div>
378
+
379
+ <div class="attachments-section">
380
+ <h3>Report Files</h3>
381
+ <div class="attachment-item">
382
+ <div class="attachment-icon">CSV</div>
383
+ <div class="attachment-name">{custom_name}_Full.csv - Complete processing report with all patient records</div>
384
+ </div>
385
+ <div class="attachment-item">
386
+ <div class="attachment-icon">CSV</div>
387
+ <div class="attachment-name">{custom_name}_Bad.csv - Records that encountered processing issues</div>
388
+ </div>
389
+ </div>
390
+
391
+ <div style="background-color: #e3f2fd; border-radius: 8px; padding: 20px; margin: 30px 0;">
392
+ <h4 style="color: #1565c0; margin-bottom: 10px;">Important Information</h4>
393
+ <p style="color: #1976d2; margin: 0;">
394
+ This automated report contains sensitive patient processing information.
395
+ Please ensure proper handling and storage according to your organization's data protection policies.
396
+ The attached files contain detailed processing results for review and record-keeping.
397
+ </p>
398
+ </div>
399
+ </div>
400
+
401
+ <div class="footer">
402
+ <div class="footer-content">
403
+ <h4>Hillside Medical Group</h4>
404
+ <p>
405
+ This automated report was generated by the Hillside Quantum Automation System.
406
+ The system processes patient records efficiently while maintaining the highest standards
407
+ of data security and accuracy.
408
+ </p>
409
+ <div class="timestamp">
410
+ Report generated on {current_date}
411
+ <br>
412
+ System: HuggingFace Spaces | Version: 2.0
413
+ </div>
414
+ </div>
415
+ </div>
416
+ </div>
417
+ </body>
418
+ </html>
419
+ """
420
+
421
+ return html_template
422
 
423
  def send_report(self, recipients, subject, body, attachments=None):
424
  if not self.service:
425
+ print("[Gmail API] Service not initialized")
426
  return False
427
 
428
  if not recipients:
429
+ print("[Gmail API] No recipients provided")
430
  return False
431
 
432
  try:
433
  from googleapiclient.errors import HttpError
434
 
435
+ print(f"[Gmail API] Sending professional report to: {recipients}")
436
 
 
437
  message = MIMEMultipart()
438
  message['From'] = self.sender_email
439
  message['To'] = ', '.join(recipients)
440
  message['Subject'] = subject
441
 
 
442
  message.attach(MIMEText(body, 'html'))
443
 
 
444
  if attachments:
445
  for filename, content in attachments.items():
446
  part = MIMEBase('application', 'octet-stream')
 
449
  part.add_header('Content-Disposition', f'attachment; filename="{filename}"')
450
  message.attach(part)
451
 
 
452
  raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode('utf-8')
453
 
 
454
  gmail_message = {'raw': raw_message}
455
  sent_message = self.service.users().messages().send(
456
  userId='me',
457
  body=gmail_message
458
  ).execute()
459
 
460
+ print(f"[Gmail API] Professional email sent successfully! Message ID: {sent_message['id']}")
461
  return True
462
 
463
  except HttpError as error:
464
+ print(f"[Gmail API] HTTP Error: {error}")
465
  return False
466
  except Exception as e:
467
+ print(f"[Gmail API] Error: {e}")
468
  return False
469
 
470
  class GoogleDriveService:
 
500
  print(f"[G-Drive] ERROR: File upload failed: {e}")
501
  return False
502
 
 
503
  email_service = GmailApiService()
504
  drive_service = GoogleDriveService()
505
 
 
542
  if not results:
543
  socketio.emit('process_complete', {'message': 'No patients were processed.'})
544
  return
545
+
546
  data = session_data.get(session_id, {})
547
  original_df = pd.read_csv(io.StringIO(data.get('csv_content')))
548
  if 'Status' not in original_df.columns:
549
  original_df.insert(1, 'Status', '')
550
+
551
  result_df = pd.DataFrame(results).set_index('Name')
552
  original_df.set_index('Name', inplace=True)
553
  original_df.update(result_df)
554
  full_df = original_df.reset_index()
555
  bad_df = full_df[full_df['Status'] == 'Bad'][['Name', 'Status']]
556
+
557
  timestamp = datetime.now().strftime("%d_%b_%Y")
558
  custom_name = data.get('filename') or timestamp
559
  full_report_name = f"{custom_name}_Full.csv"
560
  bad_report_name = f"{custom_name}_Bad.csv"
561
  full_report_content = full_df.to_csv(index=False)
562
+
563
  drive_service.upload_file(full_report_name, full_report_content)
564
+
565
  attachments = {full_report_name: full_report_content, bad_report_name: bad_df.to_csv(index=False)}
566
  status_text = "terminated by user" if is_terminated else "crashed due to an error" if is_crash_report else "completed successfully"
 
 
 
 
 
 
 
 
 
 
 
 
567
 
568
+ stats = {
569
+ 'total': len(full_df),
570
+ 'processed': len(results),
571
+ 'successful': len([r for r in results if r['Status'] == 'Done']),
572
+ 'failed': len([r for r in results if r['Status'] == 'Bad'])
573
+ }
574
+
575
+ subject = f"Hillside Automation Report - {status_text.title()}: {custom_name}"
576
+
577
+ professional_body = email_service.create_professional_email_template(
578
+ subject, status_text, stats, custom_name
579
+ )
580
+
581
+ email_success = email_service.send_report(data.get('emails'), subject, professional_body, attachments)
582
 
583
  if email_success:
584
+ socketio.emit('process_complete', {'message': f'Process {status_text}. Professional report sent successfully via Gmail API.'})
585
  else:
586
+ socketio.emit('process_complete', {'message': f'Process {status_text}. Email sending failed.'})
587
 
588
  @app.route('/')
589
  def status_page():
590
+ APP_STATUS_HTML = """<!DOCTYPE html><html lang="en"><head><title>Hillside Automation - Gmail API</title><style>body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);color:white;}.status-box{text-align:center;padding:40px 60px;background:rgba(255,255,255,0.1);border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,0.3);backdrop-filter:blur(10px);}h1{font-size:28px;margin-bottom:10px;} .indicator{font-size:18px;font-weight:600;padding:12px 20px;border-radius:25px;background:rgba(40,167,69,0.2);border:2px solid #28a745;color:#28a745;margin:15px 0;}.info{margin-top:20px;opacity:0.9;}</style></head><body><div class="status-box"><h1>Hillside Automation</h1><div class="indicator">Gmail API Mode - Professional Reports</div><div class="info">Chrome: Ready<br>Email: Gmail API (HTTP)<br>Drive: Integrated<br>Platform: HuggingFace<br><br><a href="https://quantbot.netlify.app" style="color:#60a5fa;">quantbot.netlify.app</a></div></div></body></html>"""
591
  return Response(APP_STATUS_HTML)
592
 
 
593
  @socketio.on('connect')
594
  def handle_connect():
595
  print(f'Frontend connected from: {FRONTEND_ORIGIN}')
 
640
 
641
  if __name__ == '__main__':
642
  print("====================================================================")
643
+ print(" Hillside Automation - Professional Gmail API Edition")
644
+ print(" Email: Gmail API with Professional Reports")
645
+ print(" SMTP: Completely bypassed")
646
+ print(f" Frontend: {FRONTEND_ORIGIN}")
647
  print("====================================================================")
648
  socketio.run(app, host='0.0.0.0', port=int(os.getenv('PORT', 7860)))