nonzeroexit commited on
Commit
1a54e21
·
verified ·
1 Parent(s): 0ff7b30

Update email_sender_api.py

Browse files
Files changed (1) hide show
  1. email_sender_api.py +72 -120
email_sender_api.py CHANGED
@@ -1,153 +1,105 @@
1
  from flask import Flask, request, jsonify
2
  from flask_cors import CORS
3
- import smtplib
4
- from email.mime.multipart import MIMEMultipart
5
- from email.mime.text import MIMEText
6
- # from email.mime.base import MIMEBase # Not used in simplified version
7
- # from email import encoders # Not used in simplified version
8
  import base64
9
  import os
10
- import socket
11
  import traceback
12
 
13
- # <<<--- FLASK APP INITIALIZATION MUST BE HERE (TOP LEVEL) --->>>
 
 
 
14
  app = Flask(__name__)
15
  CORS(app)
16
- # <<<--------------------------------------------------------->>>
17
-
18
- # Environment variables will be set as Secrets in Hugging Face Space settings
19
- SMTP_SERVER_HOST = os.environ.get("SMTP_SERVER_HOST")
20
- SMTP_SERVER_PORT = os.environ.get("SMTP_SERVER_PORT")
21
- SMTP_SENDER_EMAIL = os.environ.get("SMTP_SENDER_EMAIL")
22
- SMTP_SENDER_PASSWORD = os.environ.get("SMTP_SENDER_PASSWORD")
23
 
24
- SMTP_CONNECTION_TIMEOUT = 30
25
- SMTP_COMMAND_TIMEOUT = 30
 
 
26
 
27
- @app.route('/send-report-via-email', methods=['POST']) # This decorator now correctly sees 'app'
28
- def handle_send_email():
29
  request_id = base64.b64encode(os.urandom(6)).decode('utf-8')
30
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Received request at /send-report-via-email")
31
 
32
- if not all([SMTP_SERVER_HOST, SMTP_SERVER_PORT, SMTP_SENDER_EMAIL, SMTP_SENDER_PASSWORD]):
33
- error_msg = "EMAIL_SENDER_API (HF) [{request_id}]: ERROR - SMTP environment variables (Secrets) not fully set."
34
  print(error_msg)
35
- return jsonify({"status": "error", "message": "Email server configuration incomplete on the API."}), 500
36
 
37
- try:
38
- SMTP_PORT_INT = int(SMTP_SERVER_PORT)
39
- except ValueError:
40
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: ERROR - Invalid SMTP_SERVER_PORT: '{SMTP_SERVER_PORT}'."
41
  print(error_msg)
42
- return jsonify({"status": "error", "message": "Invalid email server port configured on the API."}), 500
43
 
44
  data = request.json
45
  if not data:
46
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - No JSON data received.")
47
  return jsonify({"status": "error", "message": "No data received by email API."}), 400
48
 
49
  recipient_email = data.get('recipient_email')
50
- # pdf_base64_data = data.get('pdf_base64_data') # Not using these in simplified test
51
- # pdf_filename = data.get('pdf_filename') # Not using these in simplified test
 
52
 
53
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Attempting to send to: {recipient_email}")
 
 
54
 
55
- if not recipient_email:
56
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Missing recipient_email in JSON payload.")
57
- return jsonify({"status": "error", "message": "Missing required data: recipient_email."}), 400
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
  try:
60
- # Create a simple plain text message
61
- simple_msg = MIMEMultipart()
62
- simple_msg['From'] = SMTP_SENDER_EMAIL
63
- simple_msg['To'] = recipient_email
64
- simple_msg['Subject'] = "EPIC-AMP - Simple Connectivity Test"
65
-
66
- simple_body = f"Hello {recipient_email},\n\nThis is a simple plain text email to test SMTP connectivity from the EPIC-AMP Hugging Face Space.\n\nIf you receive this, basic SMTP connection, authentication, and sending are working.\n\nRegards,\nThe EPIC-AMP Test System"
67
- simple_msg.attach(MIMEText(simple_body, 'plain'))
68
-
69
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Preparing to connect to SMTP server {SMTP_SERVER_HOST}:{SMTP_PORT_INT} with timeout {SMTP_CONNECTION_TIMEOUT}s")
70
-
71
- server = None
72
- try:
73
- if SMTP_PORT_INT == 465: # SSL
74
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Using SMTP_SSL for port 465.")
75
- server = smtplib.SMTP_SSL(SMTP_SERVER_HOST, SMTP_PORT_INT, timeout=SMTP_CONNECTION_TIMEOUT)
76
- else: # Standard port, try STARTTLS (e.g., for 587)
77
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Using standard SMTP for port {SMTP_PORT_INT}.")
78
- server = smtplib.SMTP(SMTP_SERVER_HOST, SMTP_PORT_INT, timeout=SMTP_CONNECTION_TIMEOUT)
79
-
80
- server.set_debuglevel(1)
81
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Connected. Sending EHLO/HELO...")
82
- server.ehlo()
83
-
84
- if SMTP_PORT_INT == 587:
85
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Port is 587, attempting STARTTLS...")
86
- server.starttls()
87
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - STARTTLS successful. Re-sending EHLO...")
88
- server.ehlo()
89
-
90
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Attempting login with user: {SMTP_SENDER_EMAIL}...")
91
- server.login(SMTP_SENDER_EMAIL, SMTP_SENDER_PASSWORD)
92
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Login successful. Sending mail to {recipient_email}...")
93
- server.sendmail(SMTP_SENDER_EMAIL, recipient_email, simple_msg.as_string())
94
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Mail sent successfully.")
95
-
96
- finally:
97
- if server:
98
- try:
99
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Quitting SMTP server connection.")
100
- server.quit()
101
- except Exception as e_quit:
102
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Error during server.quit(): {e_quit}")
103
 
104
- success_msg = f"SIMPLE TEST: Email successfully sent to {recipient_email}."
105
- print(f"EMAIL_SENDER_API (HF) [{request_id}]: {success_msg}")
106
- return jsonify({"status": "success", "message": success_msg}), 200
 
 
 
107
 
108
- # Keep all the specific exception handling blocks
109
- except smtplib.SMTPConnectError as e:
110
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - SMTP Connect Error. {e}"
111
- print(error_msg); traceback.print_exc()
112
- return jsonify({"status": "error", "message": f"SIMPLIFIED TEST - Could not connect: {e.strerror if hasattr(e, 'strerror') else e}"}), 503
113
- except smtplib.SMTPHeloError as e:
114
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - SMTP Helo/Ehlo Error. {e}"
115
- print(error_msg); traceback.print_exc()
116
- return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Server HELO/EHLO error."}), 502
117
- except smtplib.SMTPAuthenticationError as e:
118
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - SMTP Authentication Error. {e}"
119
- print(error_msg); traceback.print_exc()
120
- return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Email server authentication failed."}), 500
121
- except socket.timeout as e:
122
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Socket Timeout. {e}"
123
- print(error_msg); traceback.print_exc()
124
- return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Connection timed out."}), 504
125
- except socket.gaierror as e:
126
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Address-related error. {e}"
127
- print(error_msg); traceback.print_exc()
128
- return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Could not resolve hostname."}), 503
129
- except OSError as e:
130
- if e.errno == 101:
131
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - OSError [Errno 101] Network is unreachable. {e}"
132
- print(error_msg); traceback.print_exc()
133
- return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Network is unreachable from API server."}), 503
134
  else:
135
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - An OS error occurred: {e}"
136
- print(error_msg); traceback.print_exc()
137
- return jsonify({"status": "error", "message": f"SIMPLIFIED TEST - OS Error: {type(e).__name__}"}), 500
138
- except Exception as e:
139
- error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - An unexpected error: {e}"
 
140
  print(error_msg)
141
  traceback.print_exc()
142
- return jsonify({"status": "error", "message": f"SIMPLIFIED TEST - Unexpected API error: {type(e).__name__}"}), 500
143
 
144
- # The if __name__ == '__main__': block for local testing:
145
- # On Hugging Face Spaces with Gunicorn, Gunicorn directly imports and runs the 'app' object,
146
- # so this block isn't strictly executed by Gunicorn.
147
- # It's useful if you want to run this script directly with `python email_sender_api.py` for local testing.
148
  # if __name__ == '__main__':
149
- # # For local testing, set your ENV VARS in this terminal session before running
150
- # api_port = int(os.environ.get("PORT", 5002)) # Default to 5002 for local test
151
- # print(f"Starting Email Sender API Service locally on port {api_port}...")
152
- # # ... (rest of the local startup print messages and app.run) ...
153
  # app.run(host='0.0.0.0', port=api_port, debug=True)
 
1
  from flask import Flask, request, jsonify
2
  from flask_cors import CORS
 
 
 
 
 
3
  import base64
4
  import os
 
5
  import traceback
6
 
7
+ # For SendGrid
8
+ from sendgrid import SendGridAPIClient
9
+ from sendgrid.helpers.mail import (Mail, Attachment, FileContent, FileName, FileType, Disposition)
10
+
11
  app = Flask(__name__)
12
  CORS(app)
 
 
 
 
 
 
 
13
 
14
+ # Environment variable for the Email API Key (set as a Secret in HF Space)
15
+ SENDGRID_API_KEY = os.environ.get("SENDGRID_API_KEY")
16
+ # Your sender email address - should be verified with SendGrid for best results
17
+ SENDER_EMAIL_ADDRESS = os.environ.get("SENDER_EMAIL_ADDRESS") # e.g., [email protected] or a domain email
18
 
19
+ @app.route('/send-report-via-email', methods=['POST'])
20
+ def handle_send_email_http_api():
21
  request_id = base64.b64encode(os.urandom(6)).decode('utf-8')
22
+ print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: Received request")
23
 
24
+ if not SENDGRID_API_KEY:
25
+ error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: ERROR - SENDGRID_API_KEY (Secret) not set."
26
  print(error_msg)
27
+ return jsonify({"status": "error", "message": "Email API key configuration incomplete."}), 500
28
 
29
+ if not SENDER_EMAIL_ADDRESS:
30
+ error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: ERROR - SENDER_EMAIL_ADDRESS (Secret) not set."
 
 
31
  print(error_msg)
32
+ return jsonify({"status": "error", "message": "Sender email address configuration incomplete."}), 500
33
 
34
  data = request.json
35
  if not data:
36
+ print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: No JSON data received.")
37
  return jsonify({"status": "error", "message": "No data received by email API."}), 400
38
 
39
  recipient_email = data.get('recipient_email')
40
+ pdf_base64_data = data.get('pdf_base64_data')
41
+ pdf_filename = data.get('pdf_filename')
42
+ print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: Attempting to send to: {recipient_email}, Filename: {pdf_filename}")
43
 
44
+ if not all([recipient_email, pdf_base64_data, pdf_filename]):
45
+ print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: Missing required data.")
46
+ return jsonify({"status": "error", "message": "Missing required data in payload."}), 400
47
 
48
+ message = Mail(
49
+ from_email=SENDER_EMAIL_ADDRESS,
50
+ to_emails=recipient_email,
51
+ subject=f"EPIC-AMP Analysis Report: {pdf_filename}",
52
+ html_content=f"""
53
+ <p>Dear User,</p>
54
+ <p>Please find your EPIC-AMP analysis report attached.</p>
55
+ <p>Filename: {pdf_filename}</p>
56
+ <p>This report includes details on your sequence analysis.</p>
57
+ <p>Thank you for using EPIC-AMP!</p>
58
+ <p>Best regards,<br>The EPIC-AMP Team<br>
59
+ Bioinformatics and Computational Biology Unit, Zewail City<br>
60
+ For inquiries: [email protected]</p>
61
+ """
62
+ )
63
 
64
  try:
65
+ # The PDF data is already base64 encoded from the frontend
66
+ attachment = Attachment()
67
+ attachment.file_content = FileContent(pdf_base64_data) # SendGrid library expects base64 string
68
+ attachment.file_name = FileName(pdf_filename)
69
+ attachment.file_type = FileType('application/pdf')
70
+ attachment.disposition = Disposition('attachment')
71
+ # attachment.content_id = ContentId('MyId') # Optional
72
+ message.attachment = attachment
73
+ except Exception as e:
74
+ error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: Error preparing attachment: {e}"
75
+ print(error_msg); traceback.print_exc()
76
+ return jsonify({"status": "error", "message": "Error preparing PDF attachment."}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ try:
79
+ sg = SendGridAPIClient(SENDGRID_API_KEY)
80
+ response = sg.send(message)
81
+ print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: SendGrid Response Status Code: {response.status_code}")
82
+ # print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: SendGrid Response Body: {response.body}")
83
+ # print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: SendGrid Response Headers: {response.headers}")
84
 
85
+ if 200 <= response.status_code < 300: # Check for successful status codes
86
+ success_msg = f"Report successfully sent to {recipient_email} via SendGrid."
87
+ print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: {success_msg}")
88
+ return jsonify({"status": "success", "message": success_msg}), 200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  else:
90
+ error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: SendGrid API Error (Status {response.status_code}): {response.body}"
91
+ print(error_msg)
92
+ return jsonify({"status": "error", "message": f"Email service provider API error (Status {response.status_code})."}), 502 # Bad Gateway / Upstream error
93
+
94
+ except Exception as e:
95
+ error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: An unexpected error occurred while sending email via SendGrid: {e}"
96
  print(error_msg)
97
  traceback.print_exc()
98
+ return jsonify({"status": "error", "message": f"An unexpected error occurred on the email API: {type(e).__name__}"}), 500
99
 
 
 
 
 
100
  # if __name__ == '__main__':
101
+ # # For local testing, set ENV VARS for SENDGRID_API_KEY and SENDER_EMAIL_ADDRESS
102
+ # api_port = int(os.environ.get("PORT", 5003)) # Different port for this version locally
103
+ # print(f"Starting HTTP Email Sender API Service (SendGrid) locally on port {api_port}...")
104
+ # # ... local startup messages ...
105
  # app.run(host='0.0.0.0', port=api_port, debug=True)