"""Email service for sending exam reports to teachers.""" from __future__ import annotations import os import smtplib import ssl from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from typing import Optional import markdown from .config import get_settings def markdown_to_html(md_content: str) -> str: """Convert markdown to styled HTML for email.""" # Convert markdown to HTML html_body = markdown.markdown( md_content, extensions=['tables', 'fenced_code', 'nl2br'] ) # Wrap in email template return f"""
{html_body}
""" async def send_email_via_gmail( to_email: str, subject: str, body_markdown: str, gmail_user: str, gmail_app_password: str ) -> dict: """ Send email using Gmail SMTP. Requires: - Gmail account - App Password (not your regular password) To get an App Password: 1. Enable 2-Step Verification on your Google account 2. Go to https://myaccount.google.com/apppasswords 3. Generate an app password for "Mail" """ try: html_content = markdown_to_html(body_markdown) # Create message message = MIMEMultipart("alternative") message["Subject"] = subject message["From"] = gmail_user message["To"] = to_email # Add plain text and HTML versions part1 = MIMEText(body_markdown, "plain") part2 = MIMEText(html_content, "html") message.attach(part1) message.attach(part2) # Create secure connection and send context = ssl.create_default_context() with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server: server.login(gmail_user, gmail_app_password) server.sendmail(gmail_user, to_email, message.as_string()) return {"status": "ok", "message": "Email sent via Gmail"} except smtplib.SMTPAuthenticationError: return { "status": "error", "message": "Gmail authentication failed. Check your email and app password." } except Exception as e: return { "status": "error", "message": f"Failed to send email: {str(e)}" } async def send_email_via_sendgrid( to_email: str, subject: str, body_markdown: str ) -> dict: """Send email using SendGrid API.""" settings = get_settings() try: from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail, Email, To, Content, HtmlContent html_content = markdown_to_html(body_markdown) message = Mail( from_email=Email(settings.sendgrid_from_email, "ClassLens"), to_emails=To(to_email), subject=subject, html_content=HtmlContent(html_content) ) message.add_content(Content("text/plain", body_markdown)) sg = SendGridAPIClient(settings.sendgrid_api_key) response = sg.send(message) if response.status_code in (200, 201, 202): return {"status": "ok", "message": "Email sent via SendGrid"} else: return { "status": "error", "message": f"SendGrid returned status {response.status_code}" } except Exception as e: return { "status": "error", "message": str(e) } async def send_email_report( email: str, subject: str, body_markdown: str ) -> dict: """ Send an email report to a teacher. Tries in order: 1. Gmail SMTP (if configured) 2. SendGrid (if configured) 3. Logs to console (fallback) Returns: {"status": "ok"} on success {"status": "error", "message": str} on failure """ # Try Gmail SMTP first gmail_user = os.getenv("GMAIL_USER", "") gmail_app_password = os.getenv("GMAIL_APP_PASSWORD", "") if gmail_user and gmail_app_password: result = await send_email_via_gmail(email, subject, body_markdown, gmail_user, gmail_app_password) if result["status"] == "ok": print(f"📧 Email sent to {email} via Gmail") return result else: print(f"⚠️ Gmail failed: {result['message']}, trying fallback...") # Try SendGrid settings = get_settings() if settings.sendgrid_api_key: result = await send_email_via_sendgrid(email, subject, body_markdown) if result["status"] == "ok": print(f"📧 Email sent to {email} via SendGrid") return result else: print(f"⚠️ SendGrid failed: {result['message']}") # Fallback: print to console print(f"\n{'='*60}") print(f"📧 EMAIL (not sent - no email service configured)") print(f"To: {email}") print(f"Subject: {subject}") print(f"{'='*60}") print(body_markdown[:500] + "..." if len(body_markdown) > 500 else body_markdown) print(f"{'='*60}\n") return { "status": "ok", "message": "Email logged to console (configure GMAIL_USER/GMAIL_APP_PASSWORD or SENDGRID_API_KEY to send real emails)" }