Spaces:
Sleeping
Sleeping
import os | |
import tempfile | |
from email.mime.multipart import MIMEMultipart | |
from email.mime.text import MIMEText | |
from email.mime.application import MIMEApplication | |
from dotenv import load_dotenv | |
from markdown_it import MarkdownIt | |
from datetime import datetime | |
import weasyprint | |
import base64 | |
from sendgrid import SendGridAPIClient | |
from sendgrid.helpers.mail import ( | |
Mail, Attachment, FileContent, FileName, | |
FileType, Disposition) | |
# Tải biến môi trường | |
load_dotenv() | |
# Thông tin email từ biến môi trường | |
SENDER_EMAIL = os.getenv("SENDER_EMAIL") | |
SENDER_APP_PASSWORD = os.getenv("SENDER_APP_PASSWORD") | |
def _markdown_to_html(markdown_string): | |
"""Convert Markdown to HTML""" | |
md = MarkdownIt() | |
html_content = md.render(markdown_string) | |
# Format current date | |
current_date = datetime.now().strftime("%d/%m/%Y") | |
# Create complete HTML with CSS for nice formatting | |
full_html = f""" | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Daily Market Report - {current_date}</title> | |
<style> | |
@page {{ | |
size: A4; | |
margin: 2cm; | |
}} | |
body {{ | |
font-family: Arial, Helvetica, sans-serif; | |
line-height: 1.5; | |
color: #333; | |
max-width: 800px; | |
margin: 0 auto; | |
}} | |
.report-header {{ | |
text-align: center; | |
margin-bottom: 30px; | |
}} | |
.report-date {{ | |
font-style: italic; | |
color: #666; | |
margin-bottom: 10px; | |
}} | |
.report-title {{ | |
font-size: 24pt; | |
margin-bottom: 5px; | |
color: #2c3e50; | |
}} | |
.report-subtitle {{ | |
font-size: 14pt; | |
color: #7f8c8d; | |
margin-top: 0; | |
}} | |
.report-body {{ | |
text-align: justify; | |
}} | |
h1, h2, h3, h4, h5, h6 {{ | |
color: #2c3e50; | |
margin-top: 20px; | |
}} | |
h1 {{ font-size: 20pt; }} | |
h2 {{ font-size: 18pt; }} | |
h3 {{ font-size: 16pt; }} | |
h4 {{ font-size: 14pt; }} | |
h5 {{ font-size: 12pt; }} | |
h6 {{ font-size: 10pt; }} | |
p {{ | |
margin-bottom: 10px; | |
}} | |
a {{ | |
color: #3498db; | |
text-decoration: none; | |
}} | |
a:hover {{ | |
text-decoration: underline; | |
}} | |
ul, ol {{ | |
margin: 10px 0 10px 20px; | |
}} | |
li {{ | |
margin-bottom: 5px; | |
}} | |
blockquote {{ | |
border-left: 4px solid #eee; | |
padding-left: 10px; | |
margin-left: 0; | |
color: #777; | |
}} | |
.section {{ | |
margin-bottom: 30px; | |
}} | |
.footer {{ | |
text-align: center; | |
margin-top: 40px; | |
padding-top: 20px; | |
font-size: 12px; | |
color: #777; | |
border-top: 1px solid #eee; | |
}} | |
/* Custom styling for bullet points */ | |
ul {{ | |
list-style-type: disc; | |
}} | |
ul ul {{ | |
list-style-type: circle; | |
}} | |
ul ul ul {{ | |
list-style-type: square; | |
}} | |
</style> | |
</head> | |
<body> | |
<div class="report-header"> | |
<div class="report-date">Date: {current_date}</div> | |
<h1 class="report-title">Daily Market Report</h1> | |
<h2 class="report-subtitle">AI Financial Dashboard</h2> | |
</div> | |
<div class="report-body"> | |
{html_content} | |
</div> | |
<div class="footer"> | |
This report was automatically generated by AI Financial Dashboard. Information is for reference only. | |
</div> | |
</body> | |
</html> | |
""" | |
return full_html | |
def _generate_pdf_from_markdown(markdown_string): | |
"""Generate PDF from Markdown using WeasyPrint""" | |
# Convert markdown to HTML | |
html_content = _markdown_to_html(markdown_string) | |
# Create temporary HTML file | |
with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as temp_html: | |
temp_html_path = temp_html.name | |
temp_html.write(html_content.encode('utf-8')) | |
# Create PDF from HTML | |
try: | |
# Create temporary PDF filename | |
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as temp_pdf: | |
temp_pdf_path = temp_pdf.name | |
# Generate PDF | |
weasyprint.HTML(filename=temp_html_path).write_pdf(temp_pdf_path) | |
# Read PDF content | |
with open(temp_pdf_path, 'rb') as f: | |
pdf_data = f.read() | |
# Delete temporary files | |
os.unlink(temp_html_path) | |
os.unlink(temp_pdf_path) | |
return pdf_data | |
except Exception as e: | |
# Handle errors and ensure temporary files are deleted | |
if os.path.exists(temp_html_path): | |
os.unlink(temp_html_path) | |
raise e | |
def send_report_via_email(report_markdown: str, recipient_email: str): | |
""" | |
Tạo báo cáo PDF từ markdown và gửi qua email bằng API của SendGrid. | |
""" | |
sendgrid_api_key = os.getenv("SENDGRID_API_KEY") | |
sender_email = os.getenv("SENDER_EMAIL") | |
if not sendgrid_api_key or not sender_email: | |
raise ValueError("Thiếu thông tin SENDGRID_API_KEY hoặc SENDER_EMAIL trong biến môi trường.") | |
# Tạo đối tượng Mail của SendGrid | |
message = Mail( | |
from_email=sender_email, | |
to_emails=recipient_email, | |
subject=f"Bản tin Thị trường Tài chính ngày {datetime.now().strftime('%d-%m-%Y')}", | |
html_content="<p>Xin chào,</p><p>Đây là bản tin thị trường được tạo tự động bởi AI Financial Dashboard.</p><p>Vui lòng xem chi tiết trong file PDF đính kèm.</p><p>Trân trọng,</p>" | |
) | |
try: | |
# Tạo file PDF trong bộ nhớ | |
pdf_data = _generate_pdf_from_markdown(report_markdown) | |
# Mã hóa base64 cho file đính kèm | |
encoded_file = base64.b64encode(pdf_data).decode() | |
# Tạo đối tượng Attachment | |
attached_file = Attachment( | |
FileContent(encoded_file), | |
FileName(f'Ban_tin_Thi_truong_{datetime.now().strftime("%Y%m%d")}.pdf'), | |
FileType('application/pdf'), | |
Disposition('attachment') | |
) | |
message.attachment = attached_file | |
# Gửi email qua API | |
sg = SendGridAPIClient(sendgrid_api_key) | |
response = sg.send(message) | |
# Kiểm tra response từ SendGrid | |
if 200 <= response.status_code < 300: | |
print(f"Email đã được gửi thành công đến {recipient_email} qua SendGrid.") | |
return True, "Email đã được gửi thành công!" | |
else: | |
print(f"Lỗi khi gửi email qua SendGrid: {response.body}") | |
return False, f"Không thể gửi email. Mã lỗi: {response.status_code}" | |
except Exception as e: | |
import traceback | |
traceback.print_exc() | |
print(f"Lỗi khi gửi email: {e}") | |
return False, f"Đã xảy ra lỗi: {e}" |